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.io.File;
020 import java.util.ArrayList;
021 import java.util.Arrays;
022 import java.util.Collection;
023 import java.util.HashMap;
024 import java.util.HashSet;
025 import java.util.List;
026 import java.util.Map;
027 import java.util.Set;
028 import java.util.concurrent.CopyOnWriteArrayList;
029 import java.util.concurrent.CountDownLatch;
030 import java.util.concurrent.TimeUnit;
031
032 import org.apache.camel.CamelContext;
033 import org.apache.camel.Component;
034 import org.apache.camel.Consumer;
035 import org.apache.camel.Endpoint;
036 import org.apache.camel.Exchange;
037 import org.apache.camel.Expression;
038 import org.apache.camel.Message;
039 import org.apache.camel.Predicate;
040 import org.apache.camel.Processor;
041 import org.apache.camel.Producer;
042 import org.apache.camel.builder.ExpressionClause;
043 import org.apache.camel.impl.DefaultEndpoint;
044 import org.apache.camel.impl.DefaultProducer;
045 import org.apache.camel.spi.BrowsableEndpoint;
046 import org.apache.camel.util.CamelContextHelper;
047 import org.apache.camel.util.ExpressionComparator;
048 import org.apache.camel.util.FileUtil;
049 import org.apache.camel.util.ObjectHelper;
050 import org.apache.commons.logging.Log;
051 import org.apache.commons.logging.LogFactory;
052
053 /**
054 * A Mock endpoint which provides a literate, fluent API for testing routes
055 * using a <a href="http://jmock.org/">JMock style</a> API.
056 *
057 * @version $Revision: 906378 $
058 */
059 public class MockEndpoint extends DefaultEndpoint implements BrowsableEndpoint {
060 private static final transient Log LOG = LogFactory.getLog(MockEndpoint.class);
061 private int expectedCount;
062 private int counter;
063 private Processor defaultProcessor;
064 private Map<Integer, Processor> processors;
065 private List<Exchange> receivedExchanges;
066 private List<Throwable> failures;
067 private List<Runnable> tests;
068 private CountDownLatch latch;
069 private long sleepForEmptyTest;
070 private long resultWaitTime;
071 private long resultMinimumWaitTime;
072 private int expectedMinimumCount;
073 private List<Object> expectedBodyValues;
074 private List<Object> actualBodyValues;
075 private String headerName;
076 private Object headerValue;
077 private Object actualHeader;
078 private String propertyName;
079 private Object propertyValue;
080 private Object actualProperty;
081 private Processor reporter;
082
083 public MockEndpoint(String endpointUri, Component component) {
084 super(endpointUri, component);
085 init();
086 }
087
088 public MockEndpoint(String endpointUri) {
089 super(endpointUri);
090 init();
091 }
092
093 public MockEndpoint() {
094 this(null);
095 }
096
097 /**
098 * A helper method to resolve the mock endpoint of the given URI on the given context
099 *
100 * @param context the camel context to try resolve the mock endpoint from
101 * @param uri the uri of the endpoint to resolve
102 * @return the endpoint
103 */
104 public static MockEndpoint resolve(CamelContext context, String uri) {
105 return CamelContextHelper.getMandatoryEndpoint(context, uri, MockEndpoint.class);
106 }
107
108 public static void assertWait(long timeout, TimeUnit unit, MockEndpoint... endpoints) throws InterruptedException {
109 long start = System.currentTimeMillis();
110 long left = unit.toMillis(timeout);
111 long end = start + left;
112 for (MockEndpoint endpoint : endpoints) {
113 if (!endpoint.await(left, TimeUnit.MILLISECONDS)) {
114 throw new AssertionError("Timeout waiting for endpoints to receive enough messages. " + endpoint.getEndpointUri() + " timed out.");
115 }
116 left = end - System.currentTimeMillis();
117 if (left <= 0) {
118 left = 0;
119 }
120 }
121 }
122
123 public static void assertIsSatisfied(long timeout, TimeUnit unit, MockEndpoint... endpoints) throws InterruptedException {
124 assertWait(timeout, unit, endpoints);
125 for (MockEndpoint endpoint : endpoints) {
126 endpoint.assertIsSatisfied();
127 }
128 }
129
130 public static void assertIsSatisfied(MockEndpoint... endpoints) throws InterruptedException {
131 for (MockEndpoint endpoint : endpoints) {
132 endpoint.assertIsSatisfied();
133 }
134 }
135
136
137 /**
138 * Asserts that all the expectations on any {@link MockEndpoint} instances registered
139 * in the given context are valid
140 *
141 * @param context the camel context used to find all the available endpoints to be asserted
142 */
143 public static void assertIsSatisfied(CamelContext context) throws InterruptedException {
144 ObjectHelper.notNull(context, "camelContext");
145 Collection<Endpoint> endpoints = context.getEndpoints();
146 for (Endpoint endpoint : endpoints) {
147 if (endpoint instanceof MockEndpoint) {
148 MockEndpoint mockEndpoint = (MockEndpoint) endpoint;
149 mockEndpoint.assertIsSatisfied();
150 }
151 }
152 }
153
154 public static void expectsMessageCount(int count, MockEndpoint... endpoints) throws InterruptedException {
155 for (MockEndpoint endpoint : endpoints) {
156 endpoint.setExpectedMessageCount(count);
157 }
158 }
159
160 public List<Exchange> getExchanges() {
161 return getReceivedExchanges();
162 }
163
164 public Consumer createConsumer(Processor processor) throws Exception {
165 throw new UnsupportedOperationException("You cannot consume from this endpoint");
166 }
167
168 public Producer createProducer() throws Exception {
169 return new DefaultProducer(this) {
170 public void process(Exchange exchange) {
171 onExchange(exchange);
172 }
173 };
174 }
175
176 public void reset() {
177 init();
178 }
179
180
181 // Testing API
182 // -------------------------------------------------------------------------
183
184 /**
185 * Set the processor that will be invoked when the index
186 * message is received.
187 */
188 public void whenExchangeReceived(int index, Processor processor) {
189 this.processors.put(index, processor);
190 }
191
192 /**
193 * Set the processor that will be invoked when the some message
194 * is received.
195 *
196 * This processor could be overwritten by
197 * {@link #whenExchangeReceived(int, Processor)} method.
198 */
199 public void whenAnyExchangeReceived(Processor processor) {
200 this.defaultProcessor = processor;
201 }
202
203 /**
204 * Validates that all the available expectations on this endpoint are
205 * satisfied; or throw an exception
206 */
207 public void assertIsSatisfied() throws InterruptedException {
208 assertIsSatisfied(sleepForEmptyTest);
209 }
210
211 /**
212 * Validates that all the available expectations on this endpoint are
213 * satisfied; or throw an exception
214 *
215 * @param timeoutForEmptyEndpoints the timeout in milliseconds that we
216 * should wait for the test to be true
217 */
218 public void assertIsSatisfied(long timeoutForEmptyEndpoints) throws InterruptedException {
219 LOG.info("Asserting: " + this + " is satisfied");
220 if (expectedCount == 0) {
221 if (timeoutForEmptyEndpoints > 0) {
222 LOG.debug("Sleeping for: " + timeoutForEmptyEndpoints + " millis to check there really are no messages received");
223 Thread.sleep(timeoutForEmptyEndpoints);
224 }
225 assertEquals("Received message count", expectedCount, getReceivedCounter());
226 } else if (expectedCount > 0) {
227 if (expectedCount != getReceivedCounter()) {
228 waitForCompleteLatch();
229 }
230 assertEquals("Received message count", expectedCount, getReceivedCounter());
231 } else if (expectedMinimumCount > 0 && getReceivedCounter() < expectedMinimumCount) {
232 waitForCompleteLatch();
233 }
234
235 if (expectedMinimumCount >= 0) {
236 int receivedCounter = getReceivedCounter();
237 assertTrue("Received message count " + receivedCounter + ", expected at least " + expectedMinimumCount, expectedMinimumCount <= receivedCounter);
238 }
239
240 for (Runnable test : tests) {
241 test.run();
242 }
243
244 for (Throwable failure : failures) {
245 if (failure != null) {
246 LOG.error("Caught on " + getEndpointUri() + " Exception: " + failure, failure);
247 fail("Failed due to caught exception: " + failure);
248 }
249 }
250 }
251
252 /**
253 * Validates that the assertions fail on this endpoint
254 */
255 public void assertIsNotSatisfied() throws InterruptedException {
256 boolean failed = false;
257 try {
258 assertIsSatisfied();
259 // did not throw expected error... fail!
260 failed = true;
261 } catch (AssertionError e) {
262 LOG.info("Caught expected failure: " + e);
263 }
264 if (failed) {
265 // fail() throws the AssertionError to indicate the test failed.
266 fail("Expected assertion failure but test succeeded!");
267 }
268 }
269
270 /**
271 * Validates that the assertions fail on this endpoint
272
273 * @param timeoutForEmptyEndpoints the timeout in milliseconds that we
274 * should wait for the test to be true
275 */
276 public void assertIsNotSatisfied(long timeoutForEmptyEndpoints) throws InterruptedException {
277 boolean failed = false;
278 try {
279 assertIsSatisfied(timeoutForEmptyEndpoints);
280 // did not throw expected error... fail!
281 failed = true;
282 } catch (AssertionError e) {
283 LOG.info("Caught expected failure: " + e);
284 }
285 if (failed) {
286 // fail() throws the AssertionError to indicate the test failed.
287 fail("Expected assertion failure but test succeeded!");
288 }
289 }
290
291 /**
292 * Specifies the expected number of message exchanges that should be
293 * received by this endpoint
294 *
295 * @param expectedCount the number of message exchanges that should be
296 * expected by this endpoint
297 */
298 public void expectedMessageCount(int expectedCount) {
299 setExpectedMessageCount(expectedCount);
300 }
301
302 /**
303 * Specifies the minimum number of expected message exchanges that should be
304 * received by this endpoint
305 *
306 * @param expectedCount the number of message exchanges that should be
307 * expected by this endpoint
308 */
309 public void expectedMinimumMessageCount(int expectedCount) {
310 setMinimumExpectedMessageCount(expectedCount);
311 }
312
313 /**
314 * Sets an expectation that the given header name & value are received by this endpoint
315 */
316 public void expectedHeaderReceived(final String name, final Object value) {
317 this.headerName = name;
318 this.headerValue = value;
319
320 expects(new Runnable() {
321 public void run() {
322 assertTrue("No header with name " + headerName + " found.", actualHeader != null);
323
324 Object actualValue;
325 if (actualHeader instanceof Expression) {
326 actualValue = ((Expression)actualHeader).evaluate(mostRecentExchange(), headerValue.getClass());
327 } else if (actualHeader instanceof Predicate) {
328 actualValue = ((Predicate)actualHeader).matches(mostRecentExchange());
329 } else {
330 actualValue = getCamelContext().getTypeConverter().convertTo(headerValue.getClass(), actualHeader);
331 assertTrue("There is no type conversion possible from " + actualHeader.getClass().getName()
332 + " to " + headerValue.getClass().getName(), actualValue != null);
333 }
334 assertEquals("Header with name " + headerName, headerValue, actualValue);
335 }
336 });
337 }
338
339 private Exchange mostRecentExchange() {
340 return receivedExchanges.get(receivedExchanges.size() - 1);
341 }
342
343 /**
344 * Sets an expectation that the given property name & value are received by this endpoint
345 */
346 public void expectedPropertyReceived(final String name, final Object value) {
347 this.propertyName = name;
348 this.propertyValue = value;
349
350 expects(new Runnable() {
351 public void run() {
352 assertTrue("No property with name " + propertyName + " found.", actualProperty != null);
353
354 Object actualValue = getCamelContext().getTypeConverter().convertTo(actualProperty.getClass(), propertyValue);
355 assertEquals("Property with name " + propertyName, actualValue, actualProperty);
356 }
357 });
358 }
359
360 /**
361 * Adds an expectation that the given body values are received by this
362 * endpoint in the specified order
363 */
364 @SuppressWarnings("unchecked")
365 public void expectedBodiesReceived(final List bodies) {
366 expectedMessageCount(bodies.size());
367 this.expectedBodyValues = bodies;
368 this.actualBodyValues = new ArrayList<Object>();
369
370 expects(new Runnable() {
371 public void run() {
372 for (int i = 0; i < expectedBodyValues.size(); i++) {
373 Exchange exchange = getReceivedExchanges().get(i);
374 assertTrue("No exchange received for counter: " + i, exchange != null);
375
376 Object expectedBody = expectedBodyValues.get(i);
377 Object actualBody = null;
378 if (i < actualBodyValues.size()) {
379 actualBody = actualBodyValues.get(i);
380 }
381
382 // TODO: coerce types before assertEquals
383 assertEquals("Body of message: " + i, expectedBody, actualBody);
384 }
385 }
386 });
387 }
388
389 /**
390 * Sets an expectation that the given body values are received by this endpoint
391 */
392 public void expectedBodiesReceived(Object... bodies) {
393 List<Object> bodyList = new ArrayList<Object>();
394 bodyList.addAll(Arrays.asList(bodies));
395 expectedBodiesReceived(bodyList);
396 }
397
398 /**
399 * Adds an expectation that the given body value are received by this endpoint
400 */
401 public ExpressionClause<?> expectedBodyReceived() {
402 final ExpressionClause<?> clause = new ExpressionClause<MockEndpoint>(this);
403
404 expectedMessageCount(1);
405
406 expects(new Runnable() {
407 public void run() {
408 Exchange exchange = getReceivedExchanges().get(0);
409 assertTrue("No exchange received for counter: " + 0, exchange != null);
410
411 Object actualBody = exchange.getIn().getBody();
412 Object expectedBody = clause.evaluate(exchange, Object.class);
413
414 assertEquals("Body of message: " + 0, expectedBody, actualBody);
415 }
416 });
417
418 return clause;
419 }
420
421 /**
422 * Adds an expectation that the given body values are received by this
423 * endpoint in any order
424 */
425 @SuppressWarnings("unchecked")
426 public void expectedBodiesReceivedInAnyOrder(final List bodies) {
427 expectedMessageCount(bodies.size());
428 this.expectedBodyValues = bodies;
429 this.actualBodyValues = new ArrayList<Object>();
430
431 expects(new Runnable() {
432 public void run() {
433 Set<Object> actualBodyValuesSet = new HashSet<Object>(actualBodyValues);
434 for (int i = 0; i < expectedBodyValues.size(); i++) {
435 Exchange exchange = getReceivedExchanges().get(i);
436 assertTrue("No exchange received for counter: " + i, exchange != null);
437
438 Object expectedBody = expectedBodyValues.get(i);
439 assertTrue("Message with body " + expectedBody
440 + " was expected but not found in " + actualBodyValuesSet,
441 actualBodyValuesSet.remove(expectedBody));
442 }
443 }
444 });
445 }
446
447 /**
448 * Adds an expectation that the given body values are received by this
449 * endpoint in any order
450 */
451 public void expectedBodiesReceivedInAnyOrder(Object... bodies) {
452 List<Object> bodyList = new ArrayList<Object>();
453 bodyList.addAll(Arrays.asList(bodies));
454 expectedBodiesReceivedInAnyOrder(bodyList);
455 }
456
457 /**
458 * Adds an expectation that a file exists with the given name
459 *
460 * @param name name of file, will cater for / and \ on different OS platforms
461 */
462 public void expectedFileExists(final String name) {
463 expectedFileExists(name, null);
464 }
465
466 /**
467 * Adds an expectation that a file exists with the given name
468 * <p/>
469 * Will wait at most 5 seconds while checking for the existence of the file.
470 *
471 * @param name name of file, will cater for / and \ on different OS platforms
472 * @param content content of file to compare, can be <tt>null</tt> to not compare content
473 */
474 public void expectedFileExists(final String name, final String content) {
475 final File file = new File(FileUtil.normalizePath(name)).getAbsoluteFile();
476
477 expects(new Runnable() {
478 public void run() {
479 // wait at most 5 seconds for the file to exists
480 final long timeout = System.currentTimeMillis() + 5000;
481
482 boolean stop = false;
483 while (!stop && !file.exists()) {
484 try {
485 Thread.sleep(50);
486 } catch (InterruptedException e) {
487 // ignore
488 }
489 stop = System.currentTimeMillis() > timeout;
490 }
491
492 assertTrue("The file should exists: " + name, file.exists());
493
494 if (content != null) {
495 String body = getCamelContext().getTypeConverter().convertTo(String.class, file);
496 assertEquals("Content of file: " + name, content, body);
497 }
498 }
499 });
500 }
501
502 /**
503 * Adds an expectation that messages received should have ascending values
504 * of the given expression such as a user generated counter value
505 */
506 public void expectsAscending(final Expression expression) {
507 expects(new Runnable() {
508 public void run() {
509 assertMessagesAscending(expression);
510 }
511 });
512 }
513
514 /**
515 * Adds an expectation that messages received should have ascending values
516 * of the given expression such as a user generated counter value
517 */
518 public ExpressionClause<?> expectsAscending() {
519 final ExpressionClause<?> clause = new ExpressionClause<MockEndpoint>(this);
520 expects(new Runnable() {
521 public void run() {
522 assertMessagesAscending(clause.getExpressionValue());
523 }
524 });
525 return clause;
526 }
527
528 /**
529 * Adds an expectation that messages received should have descending values
530 * of the given expression such as a user generated counter value
531 */
532 public void expectsDescending(final Expression expression) {
533 expects(new Runnable() {
534 public void run() {
535 assertMessagesDescending(expression);
536 }
537 });
538 }
539
540 /**
541 * Adds an expectation that messages received should have descending values
542 * of the given expression such as a user generated counter value
543 */
544 public ExpressionClause<?> expectsDescending() {
545 final ExpressionClause<?> clause = new ExpressionClause<MockEndpoint>(this);
546 expects(new Runnable() {
547 public void run() {
548 assertMessagesDescending(clause.getExpressionValue());
549 }
550 });
551 return clause;
552 }
553
554 /**
555 * Adds an expectation that no duplicate messages should be received using
556 * the expression to determine the message ID
557 *
558 * @param expression the expression used to create a unique message ID for
559 * message comparison (which could just be the message
560 * payload if the payload can be tested for uniqueness using
561 * {@link Object#equals(Object)} and
562 * {@link Object#hashCode()}
563 */
564 public void expectsNoDuplicates(final Expression expression) {
565 expects(new Runnable() {
566 public void run() {
567 assertNoDuplicates(expression);
568 }
569 });
570 }
571
572 /**
573 * Adds an expectation that no duplicate messages should be received using
574 * the expression to determine the message ID
575 */
576 public ExpressionClause<?> expectsNoDuplicates() {
577 final ExpressionClause<?> clause = new ExpressionClause<MockEndpoint>(this);
578 expects(new Runnable() {
579 public void run() {
580 assertNoDuplicates(clause.getExpressionValue());
581 }
582 });
583 return clause;
584 }
585
586 /**
587 * Asserts that the messages have ascending values of the given expression
588 */
589 public void assertMessagesAscending(Expression expression) {
590 assertMessagesSorted(expression, true);
591 }
592
593 /**
594 * Asserts that the messages have descending values of the given expression
595 */
596 public void assertMessagesDescending(Expression expression) {
597 assertMessagesSorted(expression, false);
598 }
599
600 protected void assertMessagesSorted(Expression expression, boolean ascending) {
601 String type = ascending ? "ascending" : "descending";
602 ExpressionComparator comparator = new ExpressionComparator(expression);
603 List<Exchange> list = getReceivedExchanges();
604 for (int i = 1; i < list.size(); i++) {
605 int j = i - 1;
606 Exchange e1 = list.get(j);
607 Exchange e2 = list.get(i);
608 int result = comparator.compare(e1, e2);
609 if (result == 0) {
610 fail("Messages not " + type + ". Messages" + j + " and " + i + " are equal with value: "
611 + expression.evaluate(e1, Object.class) + " for expression: " + expression + ". Exchanges: " + e1 + " and " + e2);
612 } else {
613 if (!ascending) {
614 result = result * -1;
615 }
616 if (result > 0) {
617 fail("Messages not " + type + ". Message " + j + " has value: " + expression.evaluate(e1, Object.class)
618 + " and message " + i + " has value: " + expression.evaluate(e2, Object.class) + " for expression: "
619 + expression + ". Exchanges: " + e1 + " and " + e2);
620 }
621 }
622 }
623 }
624
625 public void assertNoDuplicates(Expression expression) {
626 Map<Object, Exchange> map = new HashMap<Object, Exchange>();
627 List<Exchange> list = getReceivedExchanges();
628 for (int i = 0; i < list.size(); i++) {
629 Exchange e2 = list.get(i);
630 Object key = expression.evaluate(e2, Object.class);
631 Exchange e1 = map.get(key);
632 if (e1 != null) {
633 fail("Duplicate message found on message " + i + " has value: " + key + " for expression: " + expression + ". Exchanges: " + e1 + " and " + e2);
634 } else {
635 map.put(key, e2);
636 }
637 }
638 }
639
640 /**
641 * Adds the expectation which will be invoked when enough messages are received
642 */
643 public void expects(Runnable runnable) {
644 tests.add(runnable);
645 }
646
647 /**
648 * Adds an assertion to the given message index
649 *
650 * @param messageIndex the number of the message
651 * @return the assertion clause
652 */
653 public AssertionClause message(final int messageIndex) {
654 final AssertionClause clause = new AssertionClause() {
655 public void run() {
656 applyAssertionOn(MockEndpoint.this, messageIndex, assertExchangeReceived(messageIndex));
657 }
658 };
659 expects(clause);
660 return clause;
661 }
662
663 /**
664 * Adds an assertion to all the received messages
665 *
666 * @return the assertion clause
667 */
668 public AssertionClause allMessages() {
669 final AssertionClause clause = new AssertionClause() {
670 public void run() {
671 List<Exchange> list = getReceivedExchanges();
672 int index = 0;
673 for (Exchange exchange : list) {
674 applyAssertionOn(MockEndpoint.this, index++, exchange);
675 }
676 }
677 };
678 expects(clause);
679 return clause;
680 }
681
682 /**
683 * Asserts that the given index of message is received (starting at zero)
684 */
685 public Exchange assertExchangeReceived(int index) {
686 int count = getReceivedCounter();
687 assertTrue("Not enough messages received. Was: " + count, count > index);
688 return getReceivedExchanges().get(index);
689 }
690
691 // Properties
692 // -------------------------------------------------------------------------
693 public List<Throwable> getFailures() {
694 return failures;
695 }
696
697 public int getReceivedCounter() {
698 return receivedExchanges.size();
699 }
700
701 public List<Exchange> getReceivedExchanges() {
702 return receivedExchanges;
703 }
704
705 public int getExpectedCount() {
706 return expectedCount;
707 }
708
709 public long getSleepForEmptyTest() {
710 return sleepForEmptyTest;
711 }
712
713 /**
714 * Allows a sleep to be specified to wait to check that this endpoint really
715 * is empty when {@link #expectedMessageCount(int)} is called with zero
716 *
717 * @param sleepForEmptyTest the milliseconds to sleep for to determine that
718 * this endpoint really is empty
719 */
720 public void setSleepForEmptyTest(long sleepForEmptyTest) {
721 this.sleepForEmptyTest = sleepForEmptyTest;
722 }
723
724 public long getResultWaitTime() {
725 return resultWaitTime;
726 }
727
728 /**
729 * Sets the maximum amount of time (in millis) the {@link #assertIsSatisfied()} will
730 * wait on a latch until it is satisfied
731 */
732 public void setResultWaitTime(long resultWaitTime) {
733 this.resultWaitTime = resultWaitTime;
734 }
735
736 /**
737 * Sets the minimum expected amount of time (in millis) the {@link #assertIsSatisfied()} will
738 * wait on a latch until it is satisfied
739 */
740 public void setMinimumResultWaitTime(long resultMinimumWaitTime) {
741 this.resultMinimumWaitTime = resultMinimumWaitTime;
742 }
743
744 /**
745 * Specifies the expected number of message exchanges that should be
746 * received by this endpoint
747 *
748 * @param expectedCount the number of message exchanges that should be
749 * expected by this endpoint
750 */
751 public void setExpectedMessageCount(int expectedCount) {
752 this.expectedCount = expectedCount;
753 if (expectedCount <= 0) {
754 latch = null;
755 } else {
756 latch = new CountDownLatch(expectedCount);
757 }
758 }
759
760 /**
761 * Specifies the minimum number of expected message exchanges that should be
762 * received by this endpoint
763 *
764 * @param expectedCount the number of message exchanges that should be
765 * expected by this endpoint
766 */
767 public void setMinimumExpectedMessageCount(int expectedCount) {
768 this.expectedMinimumCount = expectedCount;
769 if (expectedCount <= 0) {
770 latch = null;
771 } else {
772 latch = new CountDownLatch(expectedMinimumCount);
773 }
774 }
775
776 public Processor getReporter() {
777 return reporter;
778 }
779
780 /**
781 * Allows a processor to added to the endpoint to report on progress of the test
782 */
783 public void setReporter(Processor reporter) {
784 this.reporter = reporter;
785 }
786
787 // Implementation methods
788 // -------------------------------------------------------------------------
789 private void init() {
790 expectedCount = -1;
791 counter = 0;
792 processors = new HashMap<Integer, Processor>();
793 receivedExchanges = new CopyOnWriteArrayList<Exchange>();
794 failures = new CopyOnWriteArrayList<Throwable>();
795 tests = new CopyOnWriteArrayList<Runnable>();
796 latch = null;
797 sleepForEmptyTest = 0;
798 resultWaitTime = 0;
799 resultMinimumWaitTime = 0L;
800 expectedMinimumCount = -1;
801 expectedBodyValues = null;
802 actualBodyValues = new ArrayList<Object>();
803 }
804
805 protected synchronized void onExchange(Exchange exchange) {
806 try {
807 if (reporter != null) {
808 reporter.process(exchange);
809 }
810 performAssertions(exchange);
811 } catch (Throwable e) {
812 // must catch java.lang.Throwable as AssertionException extends java.lang.Error
813 failures.add(e);
814 } finally {
815 // make sure latch is counted down to avoid test hanging forever
816 if (latch != null) {
817 latch.countDown();
818 }
819 }
820 }
821
822 @SuppressWarnings("unchecked")
823 protected void performAssertions(Exchange exchange) throws Exception {
824 Message in = exchange.getIn();
825 Object actualBody = in.getBody();
826
827 if (headerName != null) {
828 actualHeader = in.getHeader(headerName);
829 }
830
831 if (propertyName != null) {
832 actualProperty = exchange.getProperty(propertyName);
833 }
834
835 if (expectedBodyValues != null) {
836 int index = actualBodyValues.size();
837 if (expectedBodyValues.size() > index) {
838 Object expectedBody = expectedBodyValues.get(index);
839 if (expectedBody != null) {
840 actualBody = in.getBody(expectedBody.getClass());
841 }
842 actualBodyValues.add(actualBody);
843 }
844 }
845
846 ++counter;
847 if (LOG.isDebugEnabled()) {
848 LOG.debug(getEndpointUri() + " >>>> " + counter + " : " + exchange + " with body: " + actualBody);
849 }
850
851 receivedExchanges.add(exchange);
852
853 Processor processor = processors.get(getReceivedCounter()) != null
854 ? processors.get(getReceivedCounter()) : defaultProcessor;
855
856 if (processor != null) {
857 try {
858 processor.process(exchange);
859 } catch (Exception e) {
860 // set exceptions on exchange so we can throw exceptions to simulate errors
861 exchange.setException(e);
862 }
863 }
864 }
865
866 protected void waitForCompleteLatch() throws InterruptedException {
867 if (latch == null) {
868 fail("Should have a latch!");
869 }
870
871 long start = System.currentTimeMillis();
872 waitForCompleteLatch(resultWaitTime);
873 long delta = System.currentTimeMillis() - start;
874 LOG.debug("Took " + delta + " millis to complete latch");
875
876 if (resultMinimumWaitTime > 0 && delta < resultMinimumWaitTime) {
877 fail("Expected minimum " + resultMinimumWaitTime
878 + " millis waiting on the result, but was faster with " + delta + " millis.");
879 }
880 }
881
882 protected void waitForCompleteLatch(long timeout) throws InterruptedException {
883 // Wait for a default 10 seconds if resultWaitTime is not set
884 long waitTime = timeout == 0 ? 10000L : timeout;
885
886 // now lets wait for the results
887 LOG.debug("Waiting on the latch for: " + timeout + " millis");
888 latch.await(waitTime, TimeUnit.MILLISECONDS);
889 }
890
891 protected void assertEquals(String message, Object expectedValue, Object actualValue) {
892 if (!ObjectHelper.equal(expectedValue, actualValue)) {
893 fail(message + ". Expected: <" + expectedValue + "> but was: <" + actualValue + ">");
894 }
895 }
896
897 protected void assertTrue(String message, boolean predicate) {
898 if (!predicate) {
899 fail(message);
900 }
901 }
902
903 protected void fail(Object message) {
904 if (LOG.isDebugEnabled()) {
905 List<Exchange> list = getReceivedExchanges();
906 int index = 0;
907 for (Exchange exchange : list) {
908 LOG.debug(getEndpointUri() + " failed and received[" + (++index) + "]: " + exchange);
909 }
910 }
911 throw new AssertionError(getEndpointUri() + " " + message);
912 }
913
914 public int getExpectedMinimumCount() {
915 return expectedMinimumCount;
916 }
917
918 public void await() throws InterruptedException {
919 if (latch != null) {
920 latch.await();
921 }
922 }
923
924 public boolean await(long timeout, TimeUnit unit) throws InterruptedException {
925 if (latch != null) {
926 return latch.await(timeout, unit);
927 }
928 return true;
929 }
930
931 public boolean isSingleton() {
932 return true;
933 }
934 }