001/**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.camel.component.mock;
018
019import java.util.Date;
020import java.util.Locale;
021import java.util.concurrent.TimeUnit;
022
023import org.apache.camel.Exchange;
024import org.apache.camel.Expression;
025import org.apache.camel.builder.BinaryPredicateSupport;
026import org.apache.camel.util.Time;
027import org.slf4j.Logger;
028import org.slf4j.LoggerFactory;
029
030/**
031 * Represents time based clauses for setting expectations on the mocks.
032 * Such as time constrains for the received messages.
033 *
034 * @version 
035 */
036public class TimeClause extends BinaryPredicateSupport {
037
038    private static final Logger LOG = LoggerFactory.getLogger(TimeClause.class);
039
040    private Time timeFrom;
041    private Time timeTo;
042    private boolean beforeNext;
043    private String was;
044
045    public TimeClause(Expression left, Expression right) {
046        // previous, next
047        super(left, right);
048    }
049
050    // TimeUnit DSL
051    // -------------------------------------------------------------------------
052
053    public class TimeClassUnit {
054
055        private final TimeClause clause;
056        private int from;
057        private int to;
058
059        public TimeClassUnit(TimeClause clause, int to) {
060            this(clause, -1, to);
061        }
062
063        public TimeClassUnit(TimeClause clause, int from, int to) {
064            this.clause = clause;
065            this.from = from;
066            this.to = to;
067        }
068
069        public TimeClause millis() {
070            period(TimeUnit.MILLISECONDS);
071            return clause;
072        }
073
074        public TimeClause seconds() {
075            period(TimeUnit.SECONDS);
076            return clause;
077        }
078
079        public TimeClause minutes() {
080            period(TimeUnit.MINUTES);
081            return clause;
082        }
083
084        private void period(TimeUnit unit) {
085            if (from > 0) {
086                timeFrom = new Time(from, unit);
087            }
088            timeTo = new Time(to, unit);
089        }
090    }
091
092    // DSL
093    // -------------------------------------------------------------------------
094
095    public TimeClassUnit noLaterThan(int period) {
096        TimeClassUnit unit = new TimeClassUnit(this, period);
097        return unit;
098    }
099
100    public TimeClassUnit between(int from, int to) {
101        TimeClassUnit unit = new TimeClassUnit(this, from, to);
102        return unit;
103    }
104
105    public void beforeNext() {
106        this.beforeNext = true;
107    }
108
109    public void afterPrevious() {
110        this.beforeNext = false;
111    }
112
113    // Implementation
114    // -------------------------------------------------------------------------
115
116    protected boolean matches(Exchange exchange, Object leftValue, Object rightValue) {
117        was = null;
118        boolean answer = true;
119
120        if (timeTo == null) {
121            throw new IllegalArgumentException("The time period has not been set. Ensure to include the time unit as well.");
122        }
123
124        Date currentDate = exchange.getProperty(Exchange.RECEIVED_TIMESTAMP, Date.class);
125
126        // the other date is either the previous or the next
127        Date otherDate;
128        if (beforeNext) {
129            // grab the previous value (left)
130            if (leftValue != null) {
131                otherDate = (Date) leftValue;
132            } else {
133                // we hit a boundary so grab the other
134                otherDate = (Date) rightValue;
135            }
136        } else {
137            // grab the next value (right)
138            if (rightValue != null) {
139                otherDate = (Date) rightValue;
140            } else {
141                // we hit a boundary so grab the other
142                otherDate = (Date) leftValue;
143            }
144        }
145
146        // if we could not grab the value, we hit a boundary (ie. either 0 message or last message)
147        if (otherDate == null) {
148            return true;
149        }
150
151        // compute if we were within the allowed time range
152        Time current = new Time(currentDate.getTime(), TimeUnit.MILLISECONDS);
153        Time other = new Time(otherDate.getTime(), TimeUnit.MILLISECONDS);
154        // must absolute delta as when we hit the boundaries the delta would negative
155        long delta = Math.abs(other.toMillis() - current.toMillis());
156        was = "delta: " + delta + " millis";
157
158        if (timeFrom != null) {
159            long from = timeFrom.toMillis();
160            answer = delta >= from;
161        }
162        if (answer) {
163            long to = timeTo.toMillis();
164            answer = delta <= to;
165        }
166
167        if (LOG.isDebugEnabled()) {
168            LOG.debug("Evaluated time clause [{}] with current: {}, other: {} -> {}", new Object[]{toString(), currentDate, otherDate, answer});
169        }
170
171        return answer;
172    }
173
174    @Override
175    protected String getOperationText() {
176        return beforeNext ? "before next" : "after previous";
177    }
178
179    @Override
180    public String toString() {
181        if (timeFrom == null) {
182            return "no later than " + timeTo + " " + getOperationText() + " (" + was + ")";
183        } else {
184            return "between " + timeFrom.getNumber() + "-" + timeTo.getNumber() + " " + timeTo.getTimeUnit().toString().toLowerCase(Locale.ENGLISH)
185                    + " " + getOperationText() + " (" + was + ")";
186        }
187    }
188}