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     */
017    package org.apache.camel.component.mock;
018    
019    import java.util.Date;
020    import java.util.Locale;
021    import java.util.concurrent.TimeUnit;
022    
023    import org.apache.camel.Exchange;
024    import org.apache.camel.Expression;
025    import org.apache.camel.builder.BinaryPredicateSupport;
026    import org.apache.camel.util.Time;
027    import org.slf4j.Logger;
028    import 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     */
036    public 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    }