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.io.File; 020import java.util.ArrayList; 021import java.util.Arrays; 022import java.util.Collection; 023import java.util.Date; 024import java.util.HashMap; 025import java.util.List; 026import java.util.Map; 027import java.util.Set; 028import java.util.concurrent.ConcurrentHashMap; 029import java.util.concurrent.CopyOnWriteArrayList; 030import java.util.concurrent.CopyOnWriteArraySet; 031import java.util.concurrent.CountDownLatch; 032import java.util.concurrent.TimeUnit; 033 034import org.apache.camel.AsyncCallback; 035import org.apache.camel.CamelContext; 036import org.apache.camel.Component; 037import org.apache.camel.Consumer; 038import org.apache.camel.Endpoint; 039import org.apache.camel.Exchange; 040import org.apache.camel.ExchangePattern; 041import org.apache.camel.Expression; 042import org.apache.camel.Handler; 043import org.apache.camel.Message; 044import org.apache.camel.Predicate; 045import org.apache.camel.Processor; 046import org.apache.camel.Producer; 047import org.apache.camel.builder.ProcessorBuilder; 048import org.apache.camel.impl.DefaultAsyncProducer; 049import org.apache.camel.impl.DefaultEndpoint; 050import org.apache.camel.impl.InterceptSendToEndpoint; 051import org.apache.camel.spi.BrowsableEndpoint; 052import org.apache.camel.spi.UriEndpoint; 053import org.apache.camel.spi.UriParam; 054import org.apache.camel.util.CamelContextHelper; 055import org.apache.camel.util.CaseInsensitiveMap; 056import org.apache.camel.util.ExchangeHelper; 057import org.apache.camel.util.ExpressionComparator; 058import org.apache.camel.util.FileUtil; 059import org.apache.camel.util.ObjectHelper; 060import org.apache.camel.util.StopWatch; 061import org.slf4j.Logger; 062import org.slf4j.LoggerFactory; 063 064/** 065 * A Mock endpoint which provides a literate, fluent API for testing routes 066 * using a <a href="http://jmock.org/">JMock style</a> API. 067 * <p/> 068 * The mock endpoint have two set of methods 069 * <ul> 070 * <li>expectedXXX or expectsXXX - To set pre conditions, before the test is executed</li> 071 * <li>assertXXX - To assert assertions, after the test has been executed</li> 072 * </ul> 073 * Its <b>important</b> to know the difference between the two set. The former is used to 074 * set expectations before the test is being started (eg before the mock receives messages). 075 * The latter is used after the test has been executed, to verify the expectations; or 076 * other assertions which you can perform after the test has been completed. 077 * <p/> 078 * <b>Beware:</b> If you want to expect a mock does not receive any messages, by calling 079 * {@link #setExpectedMessageCount(int)} with <tt>0</tt>, then take extra care, 080 * as <tt>0</tt> matches when the tests starts, so you need to set a assert period time 081 * to let the test run for a while to make sure there are still no messages arrived; for 082 * that use {@link #setAssertPeriod(long)}. 083 * An alternative is to use <a href="http://camel.apache.org/notifybuilder.html">NotifyBuilder</a>, and use the notifier 084 * to know when Camel is done routing some messages, before you call the {@link #assertIsSatisfied()} method on the mocks. 085 * This allows you to not use a fixed assert period, to speedup testing times. 086 * 087 * @version 088 */ 089@UriEndpoint(scheme = "mock") 090public class MockEndpoint extends DefaultEndpoint implements BrowsableEndpoint { 091 private static final Logger LOG = LoggerFactory.getLogger(MockEndpoint.class); 092 // must be volatile so changes is visible between the thread which performs the assertions 093 // and the threads which process the exchanges when routing messages in Camel 094 protected volatile Processor reporter; 095 protected boolean copyOnExchange = true; 096 @UriParam 097 private volatile int expectedCount; 098 private volatile int counter; 099 private volatile Processor defaultProcessor; 100 private volatile Map<Integer, Processor> processors; 101 private volatile List<Exchange> receivedExchanges; 102 private volatile List<Throwable> failures; 103 private volatile List<Runnable> tests; 104 private volatile CountDownLatch latch; 105 @UriParam 106 private volatile long sleepForEmptyTest; 107 @UriParam 108 private volatile long resultWaitTime; 109 @UriParam 110 private volatile long resultMinimumWaitTime; 111 @UriParam 112 private volatile long assertPeriod; 113 @UriParam 114 private volatile int expectedMinimumCount; 115 private volatile List<?> expectedBodyValues; 116 private volatile List<Object> actualBodyValues; 117 private volatile Map<String, Object> expectedHeaderValues; 118 private volatile Map<String, Object> actualHeaderValues; 119 private volatile Map<String, Object> expectedPropertyValues; 120 private volatile Map<String, Object> actualPropertyValues; 121 @UriParam 122 private volatile int retainFirst; 123 @UriParam 124 private volatile int retainLast; 125 126 public MockEndpoint(String endpointUri, Component component) { 127 super(endpointUri, component); 128 init(); 129 } 130 131 @Deprecated 132 public MockEndpoint(String endpointUri) { 133 super(endpointUri); 134 init(); 135 } 136 137 public MockEndpoint() { 138 this(null); 139 } 140 141 /** 142 * A helper method to resolve the mock endpoint of the given URI on the given context 143 * 144 * @param context the camel context to try resolve the mock endpoint from 145 * @param uri the uri of the endpoint to resolve 146 * @return the endpoint 147 */ 148 public static MockEndpoint resolve(CamelContext context, String uri) { 149 return CamelContextHelper.getMandatoryEndpoint(context, uri, MockEndpoint.class); 150 } 151 152 public static void assertWait(long timeout, TimeUnit unit, MockEndpoint... endpoints) throws InterruptedException { 153 long start = System.currentTimeMillis(); 154 long left = unit.toMillis(timeout); 155 long end = start + left; 156 for (MockEndpoint endpoint : endpoints) { 157 if (!endpoint.await(left, TimeUnit.MILLISECONDS)) { 158 throw new AssertionError("Timeout waiting for endpoints to receive enough messages. " + endpoint.getEndpointUri() + " timed out."); 159 } 160 left = end - System.currentTimeMillis(); 161 if (left <= 0) { 162 left = 0; 163 } 164 } 165 } 166 167 public static void assertIsSatisfied(long timeout, TimeUnit unit, MockEndpoint... endpoints) throws InterruptedException { 168 assertWait(timeout, unit, endpoints); 169 for (MockEndpoint endpoint : endpoints) { 170 endpoint.assertIsSatisfied(); 171 } 172 } 173 174 public static void assertIsSatisfied(MockEndpoint... endpoints) throws InterruptedException { 175 for (MockEndpoint endpoint : endpoints) { 176 endpoint.assertIsSatisfied(); 177 } 178 } 179 180 181 /** 182 * Asserts that all the expectations on any {@link MockEndpoint} instances registered 183 * in the given context are valid 184 * 185 * @param context the camel context used to find all the available endpoints to be asserted 186 */ 187 public static void assertIsSatisfied(CamelContext context) throws InterruptedException { 188 ObjectHelper.notNull(context, "camelContext"); 189 Collection<Endpoint> endpoints = context.getEndpoints(); 190 for (Endpoint endpoint : endpoints) { 191 // if the endpoint was intercepted we should get the delegate 192 if (endpoint instanceof InterceptSendToEndpoint) { 193 endpoint = ((InterceptSendToEndpoint) endpoint).getDelegate(); 194 } 195 if (endpoint instanceof MockEndpoint) { 196 MockEndpoint mockEndpoint = (MockEndpoint) endpoint; 197 mockEndpoint.assertIsSatisfied(); 198 } 199 } 200 } 201 202 /** 203 * Asserts that all the expectations on any {@link MockEndpoint} instances registered 204 * in the given context are valid 205 * 206 * @param context the camel context used to find all the available endpoints to be asserted 207 * @param timeout timeout 208 * @param unit time unit 209 */ 210 public static void assertIsSatisfied(CamelContext context, long timeout, TimeUnit unit) throws InterruptedException { 211 ObjectHelper.notNull(context, "camelContext"); 212 ObjectHelper.notNull(unit, "unit"); 213 Collection<Endpoint> endpoints = context.getEndpoints(); 214 long millis = unit.toMillis(timeout); 215 for (Endpoint endpoint : endpoints) { 216 // if the endpoint was intercepted we should get the delegate 217 if (endpoint instanceof InterceptSendToEndpoint) { 218 endpoint = ((InterceptSendToEndpoint) endpoint).getDelegate(); 219 } 220 if (endpoint instanceof MockEndpoint) { 221 MockEndpoint mockEndpoint = (MockEndpoint) endpoint; 222 mockEndpoint.setResultWaitTime(millis); 223 mockEndpoint.assertIsSatisfied(); 224 } 225 } 226 } 227 228 /** 229 * Sets the assert period on all the expectations on any {@link MockEndpoint} instances registered 230 * in the given context. 231 * 232 * @param context the camel context used to find all the available endpoints 233 * @param period the period in millis 234 */ 235 public static void setAssertPeriod(CamelContext context, long period) { 236 ObjectHelper.notNull(context, "camelContext"); 237 Collection<Endpoint> endpoints = context.getEndpoints(); 238 for (Endpoint endpoint : endpoints) { 239 // if the endpoint was intercepted we should get the delegate 240 if (endpoint instanceof InterceptSendToEndpoint) { 241 endpoint = ((InterceptSendToEndpoint) endpoint).getDelegate(); 242 } 243 if (endpoint instanceof MockEndpoint) { 244 MockEndpoint mockEndpoint = (MockEndpoint) endpoint; 245 mockEndpoint.setAssertPeriod(period); 246 } 247 } 248 } 249 250 /** 251 * Reset all mock endpoints 252 * 253 * @param context the camel context used to find all the available endpoints to reset 254 */ 255 public static void resetMocks(CamelContext context) { 256 ObjectHelper.notNull(context, "camelContext"); 257 Collection<Endpoint> endpoints = context.getEndpoints(); 258 for (Endpoint endpoint : endpoints) { 259 // if the endpoint was intercepted we should get the delegate 260 if (endpoint instanceof InterceptSendToEndpoint) { 261 endpoint = ((InterceptSendToEndpoint) endpoint).getDelegate(); 262 } 263 if (endpoint instanceof MockEndpoint) { 264 MockEndpoint mockEndpoint = (MockEndpoint) endpoint; 265 mockEndpoint.reset(); 266 } 267 } 268 } 269 270 public static void expectsMessageCount(int count, MockEndpoint... endpoints) throws InterruptedException { 271 for (MockEndpoint endpoint : endpoints) { 272 endpoint.setExpectedMessageCount(count); 273 } 274 } 275 276 public List<Exchange> getExchanges() { 277 return getReceivedExchanges(); 278 } 279 280 public Consumer createConsumer(Processor processor) throws Exception { 281 throw new UnsupportedOperationException("You cannot consume from this endpoint"); 282 } 283 284 public Producer createProducer() throws Exception { 285 return new DefaultAsyncProducer(this) { 286 public boolean process(Exchange exchange, AsyncCallback callback) { 287 onExchange(exchange); 288 callback.done(true); 289 return true; 290 } 291 }; 292 } 293 294 public void reset() { 295 init(); 296 } 297 298 299 // Testing API 300 // ------------------------------------------------------------------------- 301 302 /** 303 * Handles the incoming exchange. 304 * <p/> 305 * This method turns this mock endpoint into a bean which you can use 306 * in the Camel routes, which allows you to inject MockEndpoint as beans 307 * in your routes and use the features of the mock to control the bean. 308 * 309 * @param exchange the exchange 310 * @throws Exception can be thrown 311 */ 312 @Handler 313 public void handle(Exchange exchange) throws Exception { 314 onExchange(exchange); 315 } 316 317 /** 318 * Set the processor that will be invoked when the index 319 * message is received. 320 */ 321 public void whenExchangeReceived(int index, Processor processor) { 322 this.processors.put(index, processor); 323 } 324 325 /** 326 * Set the processor that will be invoked when the some message 327 * is received. 328 * 329 * This processor could be overwritten by 330 * {@link #whenExchangeReceived(int, Processor)} method. 331 */ 332 public void whenAnyExchangeReceived(Processor processor) { 333 this.defaultProcessor = processor; 334 } 335 336 /** 337 * Set the expression which value will be set to the message body 338 * @param expression which is use to set the message body 339 */ 340 public void returnReplyBody(Expression expression) { 341 this.defaultProcessor = ProcessorBuilder.setBody(expression); 342 } 343 344 /** 345 * Set the expression which value will be set to the message header 346 * @param headerName that will be set value 347 * @param expression which is use to set the message header 348 */ 349 public void returnReplyHeader(String headerName, Expression expression) { 350 this.defaultProcessor = ProcessorBuilder.setHeader(headerName, expression); 351 } 352 353 354 /** 355 * Validates that all the available expectations on this endpoint are 356 * satisfied; or throw an exception 357 */ 358 public void assertIsSatisfied() throws InterruptedException { 359 assertIsSatisfied(sleepForEmptyTest); 360 } 361 362 /** 363 * Validates that all the available expectations on this endpoint are 364 * satisfied; or throw an exception 365 * 366 * @param timeoutForEmptyEndpoints the timeout in milliseconds that we 367 * should wait for the test to be true 368 */ 369 public void assertIsSatisfied(long timeoutForEmptyEndpoints) throws InterruptedException { 370 LOG.info("Asserting: " + this + " is satisfied"); 371 doAssertIsSatisfied(timeoutForEmptyEndpoints); 372 if (assertPeriod > 0) { 373 // if an assert period was set then re-assert again to ensure the assertion is still valid 374 Thread.sleep(assertPeriod); 375 LOG.info("Re-asserting: " + this + " is satisfied after " + assertPeriod + " millis"); 376 // do not use timeout when we re-assert 377 doAssertIsSatisfied(0); 378 } 379 } 380 381 protected void doAssertIsSatisfied(long timeoutForEmptyEndpoints) throws InterruptedException { 382 if (expectedCount == 0) { 383 if (timeoutForEmptyEndpoints > 0) { 384 LOG.debug("Sleeping for: " + timeoutForEmptyEndpoints + " millis to check there really are no messages received"); 385 Thread.sleep(timeoutForEmptyEndpoints); 386 } 387 assertEquals("Received message count", expectedCount, getReceivedCounter()); 388 } else if (expectedCount > 0) { 389 if (expectedCount != getReceivedCounter()) { 390 waitForCompleteLatch(); 391 } 392 assertEquals("Received message count", expectedCount, getReceivedCounter()); 393 } else if (expectedMinimumCount > 0 && getReceivedCounter() < expectedMinimumCount) { 394 waitForCompleteLatch(); 395 } 396 397 if (expectedMinimumCount >= 0) { 398 int receivedCounter = getReceivedCounter(); 399 assertTrue("Received message count " + receivedCounter + ", expected at least " + expectedMinimumCount, expectedMinimumCount <= receivedCounter); 400 } 401 402 for (Runnable test : tests) { 403 test.run(); 404 } 405 406 for (Throwable failure : failures) { 407 if (failure != null) { 408 LOG.error("Caught on " + getEndpointUri() + " Exception: " + failure, failure); 409 fail("Failed due to caught exception: " + failure); 410 } 411 } 412 } 413 414 /** 415 * Validates that the assertions fail on this endpoint 416 */ 417 public void assertIsNotSatisfied() throws InterruptedException { 418 boolean failed = false; 419 try { 420 assertIsSatisfied(); 421 // did not throw expected error... fail! 422 failed = true; 423 } catch (AssertionError e) { 424 LOG.info("Caught expected failure: " + e); 425 } 426 if (failed) { 427 // fail() throws the AssertionError to indicate the test failed. 428 fail("Expected assertion failure but test succeeded!"); 429 } 430 } 431 432 /** 433 * Validates that the assertions fail on this endpoint 434 435 * @param timeoutForEmptyEndpoints the timeout in milliseconds that we 436 * should wait for the test to be true 437 */ 438 public void assertIsNotSatisfied(long timeoutForEmptyEndpoints) throws InterruptedException { 439 boolean failed = false; 440 try { 441 assertIsSatisfied(timeoutForEmptyEndpoints); 442 // did not throw expected error... fail! 443 failed = true; 444 } catch (AssertionError e) { 445 LOG.info("Caught expected failure: " + e); 446 } 447 if (failed) { 448 // fail() throws the AssertionError to indicate the test failed. 449 fail("Expected assertion failure but test succeeded!"); 450 } 451 } 452 453 /** 454 * Specifies the expected number of message exchanges that should be 455 * received by this endpoint 456 * 457 * If you want to assert that <b>exactly</b> n messages arrives to this mock 458 * endpoint, then see also the {@link #setAssertPeriod(long)} method for further details. 459 * 460 * @param expectedCount the number of message exchanges that should be 461 * expected by this endpoint 462 * @see #setAssertPeriod(long) 463 */ 464 public void expectedMessageCount(int expectedCount) { 465 setExpectedMessageCount(expectedCount); 466 } 467 468 /** 469 * Sets a grace period after which the mock endpoint will re-assert 470 * to ensure the preliminary assertion is still valid. 471 * <p/> 472 * This is used for example to assert that <b>exactly</b> a number of messages 473 * arrives. For example if {@link #expectedMessageCount(int)} was set to 5, then 474 * the assertion is satisfied when 5 or more message arrives. To ensure that 475 * exactly 5 messages arrives, then you would need to wait a little period 476 * to ensure no further message arrives. This is what you can use this 477 * {@link #setAssertPeriod(long)} method for. 478 * <p/> 479 * By default this period is disabled. 480 * 481 * @param period grace period in millis 482 */ 483 public void setAssertPeriod(long period) { 484 this.assertPeriod = period; 485 } 486 487 /** 488 * Specifies the minimum number of expected message exchanges that should be 489 * received by this endpoint 490 * 491 * @param expectedCount the number of message exchanges that should be 492 * expected by this endpoint 493 */ 494 public void expectedMinimumMessageCount(int expectedCount) { 495 setMinimumExpectedMessageCount(expectedCount); 496 } 497 498 /** 499 * Sets an expectation that the given header name & value are received by this endpoint 500 * <p/> 501 * You can set multiple expectations for different header names. 502 * If you set a value of <tt>null</tt> that means we accept either the header is absent, or its value is <tt>null</tt> 503 */ 504 public void expectedHeaderReceived(final String name, final Object value) { 505 if (expectedHeaderValues == null) { 506 expectedHeaderValues = new CaseInsensitiveMap(); 507 // we just wants to expects to be called once 508 expects(new Runnable() { 509 public void run() { 510 for (int i = 0; i < getReceivedExchanges().size(); i++) { 511 Exchange exchange = getReceivedExchange(i); 512 for (Map.Entry<String, Object> entry : expectedHeaderValues.entrySet()) { 513 String key = entry.getKey(); 514 Object expectedValue = entry.getValue(); 515 516 // we accept that an expectedValue of null also means that the header may be absent 517 if (expectedValue != null) { 518 assertTrue("Exchange " + i + " has no headers", exchange.getIn().hasHeaders()); 519 boolean hasKey = exchange.getIn().getHeaders().containsKey(key); 520 assertTrue("No header with name " + key + " found for message: " + i, hasKey); 521 } 522 523 Object actualValue = exchange.getIn().getHeader(key); 524 actualValue = extractActualValue(exchange, actualValue, expectedValue); 525 526 assertEquals("Header with name " + key + " for message: " + i, expectedValue, actualValue); 527 } 528 } 529 } 530 }); 531 } 532 expectedHeaderValues.put(name, value); 533 } 534 535 /** 536 * Adds an expectation that the given header values are received by this 537 * endpoint in any order 538 */ 539 public void expectedHeaderValuesReceivedInAnyOrder(final String name, final List<?> values) { 540 expectedMessageCount(values.size()); 541 542 expects(new Runnable() { 543 public void run() { 544 // these are the expected values to find 545 final Set<Object> actualHeaderValues = new CopyOnWriteArraySet<Object>(values); 546 547 for (int i = 0; i < getReceivedExchanges().size(); i++) { 548 Exchange exchange = getReceivedExchange(i); 549 550 Object actualValue = exchange.getIn().getHeader(name); 551 for (Object expectedValue : actualHeaderValues) { 552 actualValue = extractActualValue(exchange, actualValue, expectedValue); 553 // remove any found values 554 actualHeaderValues.remove(actualValue); 555 } 556 } 557 558 // should be empty, as we should find all the values 559 assertTrue("Expected " + values.size() + " headers with key[" + name + "], received " + (values.size() - actualHeaderValues.size()) 560 + " headers. Expected header values: " + actualHeaderValues, actualHeaderValues.isEmpty()); 561 } 562 }); 563 } 564 565 /** 566 * Adds an expectation that the given header values are received by this 567 * endpoint in any order 568 */ 569 public void expectedHeaderValuesReceivedInAnyOrder(String name, Object... values) { 570 List<Object> valueList = new ArrayList<Object>(); 571 valueList.addAll(Arrays.asList(values)); 572 expectedHeaderValuesReceivedInAnyOrder(name, valueList); 573 } 574 575 /** 576 * Sets an expectation that the given property name & value are received by this endpoint 577 * <p/> 578 * You can set multiple expectations for different property names. 579 * If you set a value of <tt>null</tt> that means we accept either the property is absent, or its value is <tt>null</tt> 580 */ 581 public void expectedPropertyReceived(final String name, final Object value) { 582 if (expectedPropertyValues == null) { 583 expectedPropertyValues = new ConcurrentHashMap<String, Object>(); 584 } 585 if (value != null) { 586 // ConcurrentHashMap cannot store null values 587 expectedPropertyValues.put(name, value); 588 } 589 590 expects(new Runnable() { 591 public void run() { 592 for (int i = 0; i < getReceivedExchanges().size(); i++) { 593 Exchange exchange = getReceivedExchange(i); 594 for (Map.Entry<String, Object> entry : expectedPropertyValues.entrySet()) { 595 String key = entry.getKey(); 596 Object expectedValue = entry.getValue(); 597 598 // we accept that an expectedValue of null also means that the header may be absent 599 if (expectedValue != null) { 600 assertTrue("Exchange " + i + " has no properties", !exchange.getProperties().isEmpty()); 601 boolean hasKey = exchange.getProperties().containsKey(key); 602 assertTrue("No property with name " + key + " found for message: " + i, hasKey); 603 } 604 605 Object actualValue = exchange.getProperty(key); 606 actualValue = extractActualValue(exchange, actualValue, expectedValue); 607 608 assertEquals("Property with name " + key + " for message: " + i, expectedValue, actualValue); 609 } 610 } 611 } 612 }); 613 } 614 615 /** 616 * Adds an expectation that the given body values are received by this 617 * endpoint in the specified order 618 */ 619 public void expectedBodiesReceived(final List<?> bodies) { 620 expectedMessageCount(bodies.size()); 621 this.expectedBodyValues = bodies; 622 this.actualBodyValues = new ArrayList<Object>(); 623 624 expects(new Runnable() { 625 public void run() { 626 for (int i = 0; i < expectedBodyValues.size(); i++) { 627 Exchange exchange = getReceivedExchange(i); 628 assertTrue("No exchange received for counter: " + i, exchange != null); 629 630 Object expectedBody = expectedBodyValues.get(i); 631 Object actualBody = null; 632 if (i < actualBodyValues.size()) { 633 actualBody = actualBodyValues.get(i); 634 } 635 actualBody = extractActualValue(exchange, actualBody, expectedBody); 636 637 assertEquals("Body of message: " + i, expectedBody, actualBody); 638 } 639 } 640 }); 641 } 642 643 private Object extractActualValue(Exchange exchange, Object actualValue, Object expectedValue) { 644 if (actualValue == null) { 645 return null; 646 } 647 648 if (actualValue instanceof Expression) { 649 actualValue = ((Expression)actualValue).evaluate(exchange, expectedValue != null ? expectedValue.getClass() : Object.class); 650 } else if (actualValue instanceof Predicate) { 651 actualValue = ((Predicate)actualValue).matches(exchange); 652 } else if (expectedValue != null) { 653 String from = actualValue.getClass().getName(); 654 String to = expectedValue.getClass().getName(); 655 actualValue = getCamelContext().getTypeConverter().convertTo(expectedValue.getClass(), exchange, actualValue); 656 assertTrue("There is no type conversion possible from " + from + " to " + to, actualValue != null); 657 } 658 return actualValue; 659 } 660 661 /** 662 * Sets an expectation that the given predicates matches the received messages by this endpoint 663 */ 664 public void expectedMessagesMatches(Predicate... predicates) { 665 for (int i = 0; i < predicates.length; i++) { 666 final int messageIndex = i; 667 final Predicate predicate = predicates[i]; 668 final AssertionClause clause = new AssertionClause(this) { 669 public void run() { 670 addPredicate(predicate); 671 applyAssertionOn(MockEndpoint.this, messageIndex, assertExchangeReceived(messageIndex)); 672 } 673 }; 674 expects(clause); 675 } 676 } 677 678 /** 679 * Sets an expectation that the given body values are received by this endpoint 680 */ 681 public void expectedBodiesReceived(Object... bodies) { 682 List<Object> bodyList = new ArrayList<Object>(); 683 bodyList.addAll(Arrays.asList(bodies)); 684 expectedBodiesReceived(bodyList); 685 } 686 687 /** 688 * Adds an expectation that the given body value are received by this endpoint 689 */ 690 public AssertionClause expectedBodyReceived() { 691 expectedMessageCount(1); 692 final AssertionClause clause = new AssertionClause(this) { 693 public void run() { 694 Exchange exchange = getReceivedExchange(0); 695 assertTrue("No exchange received for counter: " + 0, exchange != null); 696 697 Object actualBody = exchange.getIn().getBody(); 698 Expression exp = createExpression(getCamelContext()); 699 Object expectedBody = exp.evaluate(exchange, Object.class); 700 701 assertEquals("Body of message: " + 0, expectedBody, actualBody); 702 } 703 }; 704 expects(clause); 705 return clause; 706 } 707 708 /** 709 * Adds an expectation that the given body values are received by this 710 * endpoint in any order 711 */ 712 public void expectedBodiesReceivedInAnyOrder(final List<?> bodies) { 713 expectedMessageCount(bodies.size()); 714 this.expectedBodyValues = bodies; 715 this.actualBodyValues = new ArrayList<Object>(); 716 717 expects(new Runnable() { 718 public void run() { 719 List<Object> actualBodyValuesSet = new ArrayList<Object>(actualBodyValues); 720 for (int i = 0; i < expectedBodyValues.size(); i++) { 721 Exchange exchange = getReceivedExchange(i); 722 assertTrue("No exchange received for counter: " + i, exchange != null); 723 724 Object expectedBody = expectedBodyValues.get(i); 725 assertTrue("Message with body " + expectedBody + " was expected but not found in " + actualBodyValuesSet, actualBodyValuesSet.remove(expectedBody)); 726 } 727 } 728 }); 729 } 730 731 /** 732 * Adds an expectation that the given body values are received by this 733 * endpoint in any order 734 */ 735 public void expectedBodiesReceivedInAnyOrder(Object... bodies) { 736 List<Object> bodyList = new ArrayList<Object>(); 737 bodyList.addAll(Arrays.asList(bodies)); 738 expectedBodiesReceivedInAnyOrder(bodyList); 739 } 740 741 /** 742 * Adds an expectation that a file exists with the given name 743 * 744 * @param name name of file, will cater for / and \ on different OS platforms 745 */ 746 public void expectedFileExists(final String name) { 747 expectedFileExists(name, null); 748 } 749 750 /** 751 * Adds an expectation that a file exists with the given name 752 * <p/> 753 * Will wait at most 5 seconds while checking for the existence of the file. 754 * 755 * @param name name of file, will cater for / and \ on different OS platforms 756 * @param content content of file to compare, can be <tt>null</tt> to not compare content 757 */ 758 public void expectedFileExists(final String name, final String content) { 759 final File file = new File(FileUtil.normalizePath(name)); 760 761 expects(new Runnable() { 762 public void run() { 763 // wait at most 5 seconds for the file to exists 764 final long timeout = System.currentTimeMillis() + 5000; 765 766 boolean stop = false; 767 while (!stop && !file.exists()) { 768 try { 769 Thread.sleep(50); 770 } catch (InterruptedException e) { 771 // ignore 772 } 773 stop = System.currentTimeMillis() > timeout; 774 } 775 776 assertTrue("The file should exists: " + name, file.exists()); 777 778 if (content != null) { 779 String body = getCamelContext().getTypeConverter().convertTo(String.class, file); 780 assertEquals("Content of file: " + name, content, body); 781 } 782 } 783 }); 784 } 785 786 /** 787 * Adds an expectation that messages received should have the given exchange pattern 788 */ 789 public void expectedExchangePattern(final ExchangePattern exchangePattern) { 790 expectedMessagesMatches(new Predicate() { 791 public boolean matches(Exchange exchange) { 792 return exchange.getPattern().equals(exchangePattern); 793 } 794 }); 795 } 796 797 /** 798 * Adds an expectation that messages received should have ascending values 799 * of the given expression such as a user generated counter value 800 */ 801 public void expectsAscending(final Expression expression) { 802 expects(new Runnable() { 803 public void run() { 804 assertMessagesAscending(expression); 805 } 806 }); 807 } 808 809 /** 810 * Adds an expectation that messages received should have ascending values 811 * of the given expression such as a user generated counter value 812 */ 813 public AssertionClause expectsAscending() { 814 final AssertionClause clause = new AssertionClause(this) { 815 public void run() { 816 assertMessagesAscending(createExpression(getCamelContext())); 817 } 818 }; 819 expects(clause); 820 return clause; 821 } 822 823 /** 824 * Adds an expectation that messages received should have descending values 825 * of the given expression such as a user generated counter value 826 */ 827 public void expectsDescending(final Expression expression) { 828 expects(new Runnable() { 829 public void run() { 830 assertMessagesDescending(expression); 831 } 832 }); 833 } 834 835 /** 836 * Adds an expectation that messages received should have descending values 837 * of the given expression such as a user generated counter value 838 */ 839 public AssertionClause expectsDescending() { 840 final AssertionClause clause = new AssertionClause(this) { 841 public void run() { 842 assertMessagesDescending(createExpression(getCamelContext())); 843 } 844 }; 845 expects(clause); 846 return clause; 847 } 848 849 /** 850 * Adds an expectation that no duplicate messages should be received using 851 * the expression to determine the message ID 852 * 853 * @param expression the expression used to create a unique message ID for 854 * message comparison (which could just be the message 855 * payload if the payload can be tested for uniqueness using 856 * {@link Object#equals(Object)} and 857 * {@link Object#hashCode()} 858 */ 859 public void expectsNoDuplicates(final Expression expression) { 860 expects(new Runnable() { 861 public void run() { 862 assertNoDuplicates(expression); 863 } 864 }); 865 } 866 867 /** 868 * Adds an expectation that no duplicate messages should be received using 869 * the expression to determine the message ID 870 */ 871 public AssertionClause expectsNoDuplicates() { 872 final AssertionClause clause = new AssertionClause(this) { 873 public void run() { 874 assertNoDuplicates(createExpression(getCamelContext())); 875 } 876 }; 877 expects(clause); 878 return clause; 879 } 880 881 /** 882 * Asserts that the messages have ascending values of the given expression 883 */ 884 public void assertMessagesAscending(Expression expression) { 885 assertMessagesSorted(expression, true); 886 } 887 888 /** 889 * Asserts that the messages have descending values of the given expression 890 */ 891 public void assertMessagesDescending(Expression expression) { 892 assertMessagesSorted(expression, false); 893 } 894 895 protected void assertMessagesSorted(Expression expression, boolean ascending) { 896 String type = ascending ? "ascending" : "descending"; 897 ExpressionComparator comparator = new ExpressionComparator(expression); 898 List<Exchange> list = getReceivedExchanges(); 899 for (int i = 1; i < list.size(); i++) { 900 int j = i - 1; 901 Exchange e1 = list.get(j); 902 Exchange e2 = list.get(i); 903 int result = comparator.compare(e1, e2); 904 if (result == 0) { 905 fail("Messages not " + type + ". Messages" + j + " and " + i + " are equal with value: " 906 + expression.evaluate(e1, Object.class) + " for expression: " + expression + ". Exchanges: " + e1 + " and " + e2); 907 } else { 908 if (!ascending) { 909 result = result * -1; 910 } 911 if (result > 0) { 912 fail("Messages not " + type + ". Message " + j + " has value: " + expression.evaluate(e1, Object.class) 913 + " and message " + i + " has value: " + expression.evaluate(e2, Object.class) + " for expression: " 914 + expression + ". Exchanges: " + e1 + " and " + e2); 915 } 916 } 917 } 918 } 919 920 public void assertNoDuplicates(Expression expression) { 921 Map<Object, Exchange> map = new HashMap<Object, Exchange>(); 922 List<Exchange> list = getReceivedExchanges(); 923 for (int i = 0; i < list.size(); i++) { 924 Exchange e2 = list.get(i); 925 Object key = expression.evaluate(e2, Object.class); 926 Exchange e1 = map.get(key); 927 if (e1 != null) { 928 fail("Duplicate message found on message " + i + " has value: " + key + " for expression: " + expression + ". Exchanges: " + e1 + " and " + e2); 929 } else { 930 map.put(key, e2); 931 } 932 } 933 } 934 935 /** 936 * Adds the expectation which will be invoked when enough messages are received 937 */ 938 public void expects(Runnable runnable) { 939 tests.add(runnable); 940 } 941 942 /** 943 * Adds an assertion to the given message index 944 * 945 * @param messageIndex the number of the message 946 * @return the assertion clause 947 */ 948 public AssertionClause message(final int messageIndex) { 949 final AssertionClause clause = new AssertionClause(this) { 950 public void run() { 951 applyAssertionOn(MockEndpoint.this, messageIndex, assertExchangeReceived(messageIndex)); 952 } 953 }; 954 expects(clause); 955 return clause; 956 } 957 958 /** 959 * Adds an assertion to all the received messages 960 * 961 * @return the assertion clause 962 */ 963 public AssertionClause allMessages() { 964 final AssertionClause clause = new AssertionClause(this) { 965 public void run() { 966 List<Exchange> list = getReceivedExchanges(); 967 int index = 0; 968 for (Exchange exchange : list) { 969 applyAssertionOn(MockEndpoint.this, index++, exchange); 970 } 971 } 972 }; 973 expects(clause); 974 return clause; 975 } 976 977 /** 978 * Asserts that the given index of message is received (starting at zero) 979 */ 980 public Exchange assertExchangeReceived(int index) { 981 int count = getReceivedCounter(); 982 assertTrue("Not enough messages received. Was: " + count, count > index); 983 return getReceivedExchange(index); 984 } 985 986 // Properties 987 // ------------------------------------------------------------------------- 988 public List<Throwable> getFailures() { 989 return failures; 990 } 991 992 public int getReceivedCounter() { 993 return counter; 994 } 995 996 public List<Exchange> getReceivedExchanges() { 997 return receivedExchanges; 998 } 999 1000 public int getExpectedCount() { 1001 return expectedCount; 1002 } 1003 1004 public long getSleepForEmptyTest() { 1005 return sleepForEmptyTest; 1006 } 1007 1008 /** 1009 * Allows a sleep to be specified to wait to check that this endpoint really 1010 * is empty when {@link #expectedMessageCount(int)} is called with zero 1011 * 1012 * @param sleepForEmptyTest the milliseconds to sleep for to determine that 1013 * this endpoint really is empty 1014 */ 1015 public void setSleepForEmptyTest(long sleepForEmptyTest) { 1016 this.sleepForEmptyTest = sleepForEmptyTest; 1017 } 1018 1019 public long getResultWaitTime() { 1020 return resultWaitTime; 1021 } 1022 1023 /** 1024 * Sets the maximum amount of time (in millis) the {@link #assertIsSatisfied()} will 1025 * wait on a latch until it is satisfied 1026 */ 1027 public void setResultWaitTime(long resultWaitTime) { 1028 this.resultWaitTime = resultWaitTime; 1029 } 1030 1031 /** 1032 * Sets the minimum expected amount of time (in millis) the {@link #assertIsSatisfied()} will 1033 * wait on a latch until it is satisfied 1034 */ 1035 public void setMinimumResultWaitTime(long resultMinimumWaitTime) { 1036 this.resultMinimumWaitTime = resultMinimumWaitTime; 1037 } 1038 1039 /** 1040 * Specifies the expected number of message exchanges that should be 1041 * received by this endpoint. 1042 * <p/> 1043 * <b>Beware:</b> If you want to expect that <tt>0</tt> messages, then take extra care, 1044 * as <tt>0</tt> matches when the tests starts, so you need to set a assert period time 1045 * to let the test run for a while to make sure there are still no messages arrived; for 1046 * that use {@link #setAssertPeriod(long)}. 1047 * An alternative is to use <a href="http://camel.apache.org/notifybuilder.html">NotifyBuilder</a>, and use the notifier 1048 * to know when Camel is done routing some messages, before you call the {@link #assertIsSatisfied()} method on the mocks. 1049 * This allows you to not use a fixed assert period, to speedup testing times. 1050 * <p/> 1051 * If you want to assert that <b>exactly</b> n'th message arrives to this mock 1052 * endpoint, then see also the {@link #setAssertPeriod(long)} method for further details. 1053 * 1054 * @param expectedCount the number of message exchanges that should be 1055 * expected by this endpoint 1056 * @see #setAssertPeriod(long) 1057 */ 1058 public void setExpectedMessageCount(int expectedCount) { 1059 this.expectedCount = expectedCount; 1060 if (expectedCount <= 0) { 1061 latch = null; 1062 } else { 1063 latch = new CountDownLatch(expectedCount); 1064 } 1065 } 1066 1067 /** 1068 * Specifies the minimum number of expected message exchanges that should be 1069 * received by this endpoint 1070 * 1071 * @param expectedCount the number of message exchanges that should be 1072 * expected by this endpoint 1073 */ 1074 public void setMinimumExpectedMessageCount(int expectedCount) { 1075 this.expectedMinimumCount = expectedCount; 1076 if (expectedCount <= 0) { 1077 latch = null; 1078 } else { 1079 latch = new CountDownLatch(expectedMinimumCount); 1080 } 1081 } 1082 1083 public Processor getReporter() { 1084 return reporter; 1085 } 1086 1087 /** 1088 * Allows a processor to added to the endpoint to report on progress of the test 1089 */ 1090 public void setReporter(Processor reporter) { 1091 this.reporter = reporter; 1092 } 1093 1094 /** 1095 * Specifies to only retain the first n'th number of received {@link Exchange}s. 1096 * <p/> 1097 * This is used when testing with big data, to reduce memory consumption by not storing 1098 * copies of every {@link Exchange} this mock endpoint receives. 1099 * <p/> 1100 * <b>Important:</b> When using this limitation, then the {@link #getReceivedCounter()} 1101 * will still return the actual number of received {@link Exchange}s. For example 1102 * if we have received 5000 {@link Exchange}s, and have configured to only retain the first 1103 * 10 {@link Exchange}s, then the {@link #getReceivedCounter()} will still return <tt>5000</tt> 1104 * but there is only the first 10 {@link Exchange}s in the {@link #getExchanges()} and 1105 * {@link #getReceivedExchanges()} methods. 1106 * <p/> 1107 * When using this method, then some of the other expectation methods is not supported, 1108 * for example the {@link #expectedBodiesReceived(Object...)} sets a expectation on the first 1109 * number of bodies received. 1110 * <p/> 1111 * You can configure both {@link #setRetainFirst(int)} and {@link #setRetainLast(int)} methods, 1112 * to limit both the first and last received. 1113 * 1114 * @param retainFirst to limit and only keep the first n'th received {@link Exchange}s, use 1115 * <tt>0</tt> to not retain any messages, or <tt>-1</tt> to retain all. 1116 * @see #setRetainLast(int) 1117 */ 1118 public void setRetainFirst(int retainFirst) { 1119 this.retainFirst = retainFirst; 1120 } 1121 1122 /** 1123 * Specifies to only retain the last n'th number of received {@link Exchange}s. 1124 * <p/> 1125 * This is used when testing with big data, to reduce memory consumption by not storing 1126 * copies of every {@link Exchange} this mock endpoint receives. 1127 * <p/> 1128 * <b>Important:</b> When using this limitation, then the {@link #getReceivedCounter()} 1129 * will still return the actual number of received {@link Exchange}s. For example 1130 * if we have received 5000 {@link Exchange}s, and have configured to only retain the last 1131 * 20 {@link Exchange}s, then the {@link #getReceivedCounter()} will still return <tt>5000</tt> 1132 * but there is only the last 20 {@link Exchange}s in the {@link #getExchanges()} and 1133 * {@link #getReceivedExchanges()} methods. 1134 * <p/> 1135 * When using this method, then some of the other expectation methods is not supported, 1136 * for example the {@link #expectedBodiesReceived(Object...)} sets a expectation on the first 1137 * number of bodies received. 1138 * <p/> 1139 * You can configure both {@link #setRetainFirst(int)} and {@link #setRetainLast(int)} methods, 1140 * to limit both the first and last received. 1141 * 1142 * @param retainLast to limit and only keep the last n'th received {@link Exchange}s, use 1143 * <tt>0</tt> to not retain any messages, or <tt>-1</tt> to retain all. 1144 * @see #setRetainFirst(int) 1145 */ 1146 public void setRetainLast(int retainLast) { 1147 this.retainLast = retainLast; 1148 } 1149 1150 // Implementation methods 1151 // ------------------------------------------------------------------------- 1152 private void init() { 1153 expectedCount = -1; 1154 counter = 0; 1155 defaultProcessor = null; 1156 processors = new HashMap<Integer, Processor>(); 1157 receivedExchanges = new CopyOnWriteArrayList<Exchange>(); 1158 failures = new CopyOnWriteArrayList<Throwable>(); 1159 tests = new CopyOnWriteArrayList<Runnable>(); 1160 latch = null; 1161 sleepForEmptyTest = 0; 1162 resultWaitTime = 0; 1163 resultMinimumWaitTime = 0L; 1164 assertPeriod = 0L; 1165 expectedMinimumCount = -1; 1166 expectedBodyValues = null; 1167 actualBodyValues = new ArrayList<Object>(); 1168 expectedHeaderValues = null; 1169 actualHeaderValues = null; 1170 expectedPropertyValues = null; 1171 actualPropertyValues = null; 1172 retainFirst = -1; 1173 retainLast = -1; 1174 } 1175 1176 protected synchronized void onExchange(Exchange exchange) { 1177 try { 1178 if (reporter != null) { 1179 reporter.process(exchange); 1180 } 1181 Exchange copy = exchange; 1182 if (copyOnExchange) { 1183 // copy the exchange so the mock stores the copy and not the actual exchange 1184 copy = ExchangeHelper.createCopy(exchange, true); 1185 } 1186 performAssertions(exchange, copy); 1187 } catch (Throwable e) { 1188 // must catch java.lang.Throwable as AssertionError extends java.lang.Error 1189 failures.add(e); 1190 } finally { 1191 // make sure latch is counted down to avoid test hanging forever 1192 if (latch != null) { 1193 latch.countDown(); 1194 } 1195 } 1196 } 1197 1198 /** 1199 * Performs the assertions on the incoming exchange. 1200 * 1201 * @param exchange the actual exchange 1202 * @param copy a copy of the exchange (only store this) 1203 * @throws Exception can be thrown if something went wrong 1204 */ 1205 protected void performAssertions(Exchange exchange, Exchange copy) throws Exception { 1206 Message in = copy.getIn(); 1207 Object actualBody = in.getBody(); 1208 1209 if (expectedHeaderValues != null) { 1210 if (actualHeaderValues == null) { 1211 actualHeaderValues = new CaseInsensitiveMap(); 1212 } 1213 if (in.hasHeaders()) { 1214 actualHeaderValues.putAll(in.getHeaders()); 1215 } 1216 } 1217 1218 if (expectedPropertyValues != null) { 1219 if (actualPropertyValues == null) { 1220 actualPropertyValues = new ConcurrentHashMap<String, Object>(); 1221 } 1222 actualPropertyValues.putAll(copy.getProperties()); 1223 } 1224 1225 if (expectedBodyValues != null) { 1226 int index = actualBodyValues.size(); 1227 if (expectedBodyValues.size() > index) { 1228 Object expectedBody = expectedBodyValues.get(index); 1229 if (expectedBody != null) { 1230 // prefer to convert body early, for example when using files 1231 // we need to read the content at this time 1232 Object body = in.getBody(expectedBody.getClass()); 1233 if (body != null) { 1234 actualBody = body; 1235 } 1236 } 1237 actualBodyValues.add(actualBody); 1238 } 1239 } 1240 1241 // let counter be 0 index-based in the logs 1242 if (LOG.isDebugEnabled()) { 1243 String msg = getEndpointUri() + " >>>> " + counter + " : " + copy + " with body: " + actualBody; 1244 if (copy.getIn().hasHeaders()) { 1245 msg += " and headers:" + copy.getIn().getHeaders(); 1246 } 1247 LOG.debug(msg); 1248 } 1249 1250 // record timestamp when exchange was received 1251 copy.setProperty(Exchange.RECEIVED_TIMESTAMP, new Date()); 1252 1253 // add a copy of the received exchange 1254 addReceivedExchange(copy); 1255 // and then increment counter after adding received exchange 1256 ++counter; 1257 1258 Processor processor = processors.get(getReceivedCounter()) != null 1259 ? processors.get(getReceivedCounter()) : defaultProcessor; 1260 1261 if (processor != null) { 1262 try { 1263 // must process the incoming exchange and NOT the copy as the idea 1264 // is the end user can manipulate the exchange 1265 processor.process(exchange); 1266 } catch (Exception e) { 1267 // set exceptions on exchange so we can throw exceptions to simulate errors 1268 exchange.setException(e); 1269 } 1270 } 1271 } 1272 1273 /** 1274 * Adds the received exchange. 1275 * 1276 * @param copy a copy of the received exchange 1277 */ 1278 protected void addReceivedExchange(Exchange copy) { 1279 if (retainFirst == 0 && retainLast == 0) { 1280 // do not retain any messages at all 1281 } else if (retainFirst < 0 && retainLast < 0) { 1282 // no limitation so keep them all 1283 receivedExchanges.add(copy); 1284 } else { 1285 // okay there is some sort of limitations, so figure out what to retain 1286 if (retainFirst > 0 && counter < retainFirst) { 1287 // store a copy as its within the retain first limitation 1288 receivedExchanges.add(copy); 1289 } else if (retainLast > 0) { 1290 // remove the oldest from the last retained boundary, 1291 int index = receivedExchanges.size() - retainLast; 1292 if (index >= 0) { 1293 // but must be outside the first range as well 1294 // otherwise we should not remove the oldest 1295 if (retainFirst <= 0 || retainFirst <= index) { 1296 receivedExchanges.remove(index); 1297 } 1298 } 1299 // store a copy of the last n'th received 1300 receivedExchanges.add(copy); 1301 } 1302 } 1303 } 1304 1305 protected void waitForCompleteLatch() throws InterruptedException { 1306 if (latch == null) { 1307 fail("Should have a latch!"); 1308 } 1309 1310 StopWatch watch = new StopWatch(); 1311 waitForCompleteLatch(resultWaitTime); 1312 long delta = watch.stop(); 1313 LOG.debug("Took {} millis to complete latch", delta); 1314 1315 if (resultMinimumWaitTime > 0 && delta < resultMinimumWaitTime) { 1316 fail("Expected minimum " + resultMinimumWaitTime 1317 + " millis waiting on the result, but was faster with " + delta + " millis."); 1318 } 1319 } 1320 1321 protected void waitForCompleteLatch(long timeout) throws InterruptedException { 1322 // Wait for a default 10 seconds if resultWaitTime is not set 1323 long waitTime = timeout == 0 ? 10000L : timeout; 1324 1325 // now let's wait for the results 1326 LOG.debug("Waiting on the latch for: " + timeout + " millis"); 1327 latch.await(waitTime, TimeUnit.MILLISECONDS); 1328 } 1329 1330 protected void assertEquals(String message, Object expectedValue, Object actualValue) { 1331 if (!ObjectHelper.equal(expectedValue, actualValue)) { 1332 fail(message + ". Expected: <" + expectedValue + "> but was: <" + actualValue + ">"); 1333 } 1334 } 1335 1336 protected void assertTrue(String message, boolean predicate) { 1337 if (!predicate) { 1338 fail(message); 1339 } 1340 } 1341 1342 protected void fail(Object message) { 1343 if (LOG.isDebugEnabled()) { 1344 List<Exchange> list = getReceivedExchanges(); 1345 int index = 0; 1346 for (Exchange exchange : list) { 1347 LOG.debug("{} failed and received[{}]: {}", new Object[]{getEndpointUri(), ++index, exchange}); 1348 } 1349 } 1350 throw new AssertionError(getEndpointUri() + " " + message); 1351 } 1352 1353 public int getExpectedMinimumCount() { 1354 return expectedMinimumCount; 1355 } 1356 1357 public void await() throws InterruptedException { 1358 if (latch != null) { 1359 latch.await(); 1360 } 1361 } 1362 1363 public boolean await(long timeout, TimeUnit unit) throws InterruptedException { 1364 if (latch != null) { 1365 return latch.await(timeout, unit); 1366 } 1367 return true; 1368 } 1369 1370 public boolean isSingleton() { 1371 return true; 1372 } 1373 1374 public boolean isLenientProperties() { 1375 return true; 1376 } 1377 1378 private Exchange getReceivedExchange(int index) { 1379 if (index <= receivedExchanges.size() - 1) { 1380 return receivedExchanges.get(index); 1381 } else { 1382 return null; 1383 } 1384 } 1385 1386}