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 }