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.builder; 018 019import java.util.ArrayList; 020import java.util.Arrays; 021import java.util.List; 022import java.util.concurrent.ConcurrentHashMap; 023import java.util.concurrent.ConcurrentMap; 024import java.util.concurrent.CountDownLatch; 025import java.util.concurrent.TimeUnit; 026import java.util.concurrent.atomic.AtomicBoolean; 027import java.util.concurrent.atomic.AtomicInteger; 028 029import org.apache.camel.CamelContext; 030import org.apache.camel.Endpoint; 031import org.apache.camel.Exchange; 032import org.apache.camel.Expression; 033import org.apache.camel.Predicate; 034import org.apache.camel.RuntimeCamelException; 035import org.apache.camel.spi.CamelEvent; 036import org.apache.camel.spi.CamelEvent.ExchangeCompletedEvent; 037import org.apache.camel.spi.CamelEvent.ExchangeCreatedEvent; 038import org.apache.camel.spi.CamelEvent.ExchangeFailedEvent; 039import org.apache.camel.spi.CamelEvent.ExchangeSentEvent; 040import org.apache.camel.spi.NotifyBuilderMatcher; 041import org.apache.camel.spi.RouteContext; 042import org.apache.camel.spi.UnitOfWork; 043import org.apache.camel.support.EndpointHelper; 044import org.apache.camel.support.EventNotifierSupport; 045import org.apache.camel.support.PatternHelper; 046import org.apache.camel.support.service.ServiceHelper; 047import org.apache.camel.util.ObjectHelper; 048import org.apache.camel.util.StringHelper; 049import org.slf4j.Logger; 050import org.slf4j.LoggerFactory; 051 052/** 053 * A builder to build an expression based on 054 * {@link org.apache.camel.spi.EventNotifier} notifications about 055 * {@link Exchange} being routed. 056 * <p/> 057 * This builder can be used for testing purposes where you want to know when a 058 * test is supposed to be done. The idea is that you can build an expression 059 * that explains when the test is done. For example when Camel have finished 060 * routing 5 messages. You can then in your test await for this condition to 061 * occur. 062 */ 063public class NotifyBuilder { 064 065 private static final Logger LOG = LoggerFactory.getLogger(NotifyBuilder.class); 066 067 private final CamelContext context; 068 069 // notifier to hook into Camel to listen for events 070 private final EventNotifierSupport eventNotifier; 071 072 // the predicates build with this builder 073 private final List<EventPredicateHolder> predicates = new ArrayList<>(); 074 075 // latch to be used to signal predicates matches 076 private CountDownLatch latch = new CountDownLatch(1); 077 078 // the current state while building an event predicate where we use a stack 079 // and the operation 080 private final List<EventPredicate> stack = new ArrayList<>(); 081 private EventOperation operation; 082 private boolean created; 083 // keep state of how many wereSentTo we have added 084 private int wereSentToIndex; 085 // default wait time 086 private long waitTime = 10000L; 087 088 // computed value whether all the predicates matched 089 private volatile boolean matches; 090 091 /** 092 * Creates a new builder. 093 * 094 * @param context the Camel context 095 */ 096 public NotifyBuilder(CamelContext context) { 097 this.context = context; 098 eventNotifier = new ExchangeNotifier(); 099 try { 100 ServiceHelper.startService(eventNotifier); 101 } catch (Exception e) { 102 throw RuntimeCamelException.wrapRuntimeCamelException(e); 103 } 104 context.getManagementStrategy().addEventNotifier(eventNotifier); 105 } 106 107 /** 108 * Optionally a <tt>from</tt> endpoint which means that this expression 109 * should only be based on {@link Exchange} which is originated from the 110 * particular endpoint(s). 111 * 112 * @param endpointUri uri of endpoint or pattern (see the EndpointHelper 113 * javadoc) 114 * @return the builder 115 * @see EndpointHelper#matchEndpoint(org.apache.camel.CamelContext, String, 116 * String) 117 */ 118 public NotifyBuilder from(final String endpointUri) { 119 stack.add(new EventPredicateSupport() { 120 121 @Override 122 public boolean isAbstract() { 123 // is abstract as its a filter 124 return true; 125 } 126 127 @Override 128 public boolean onExchange(Exchange exchange) { 129 // filter non matching exchanges 130 if (exchange.getFromEndpoint() != null) { 131 return EndpointHelper.matchEndpoint(context, exchange.getFromEndpoint().getEndpointUri(), endpointUri); 132 } else { 133 return false; 134 } 135 } 136 137 public boolean matches() { 138 // should be true as we use the onExchange to filter 139 return true; 140 } 141 142 @Override 143 public String toString() { 144 return "from(" + endpointUri + ")"; 145 } 146 }); 147 return this; 148 } 149 150 /** 151 * Optionally a <tt>from</tt> route which means that this expression should 152 * only be based on {@link Exchange} which is originated from the particular 153 * route(s). 154 * 155 * @param routeId id of route or pattern (see the EndpointHelper javadoc) 156 * @return the builder 157 * @see EndpointHelper#matchEndpoint(org.apache.camel.CamelContext, String, 158 * String) 159 */ 160 public NotifyBuilder fromRoute(final String routeId) { 161 stack.add(new EventPredicateSupport() { 162 163 @Override 164 public boolean isAbstract() { 165 // is abstract as its a filter 166 return true; 167 } 168 169 @Override 170 public boolean onExchange(Exchange exchange) { 171 String id = EndpointHelper.getRouteIdFromEndpoint(exchange.getFromEndpoint()); 172 173 if (id == null) { 174 id = exchange.getFromRouteId(); 175 } 176 177 // filter non matching exchanges 178 return PatternHelper.matchPattern(id, routeId); 179 } 180 181 public boolean matches() { 182 // should be true as we use the onExchange to filter 183 return true; 184 } 185 186 @Override 187 public String toString() { 188 return "fromRoute(" + routeId + ")"; 189 } 190 }); 191 return this; 192 } 193 194 /** 195 * Optionally a <tt>from</tt> current route which means that this expression 196 * should only be based on {@link Exchange} which is the current route(s). 197 * 198 * @param routeId id of route or pattern (see the EndpointHelper javadoc) 199 * @return the builder 200 * @see EndpointHelper#matchEndpoint(org.apache.camel.CamelContext, String, 201 * String) 202 */ 203 public NotifyBuilder fromCurrentRoute(final String routeId) { 204 stack.add(new EventPredicateSupport() { 205 206 @Override 207 public boolean isAbstract() { 208 // is abstract as its a filter 209 return true; 210 } 211 212 @Override 213 public boolean onExchangeSent(Exchange exchange, Endpoint endpoint, long timeTaken) { 214 UnitOfWork uow = exchange.getUnitOfWork(); 215 RouteContext rc = uow != null ? uow.getRouteContext() : null; 216 if (rc != null) { 217 String id = rc.getRouteId(); 218 return PatternHelper.matchPattern(id, routeId); 219 } else { 220 return false; 221 } 222 } 223 224 public boolean matches() { 225 // should be true as we use the onExchange to filter 226 return true; 227 } 228 229 @Override 230 public String toString() { 231 return "fromCurrentRoute(" + routeId + ")"; 232 } 233 }); 234 return this; 235 } 236 237 private NotifyBuilder fromRoutesOnly() { 238 // internal and should always be in top of stack 239 stack.add(0, new EventPredicateSupport() { 240 241 @Override 242 public boolean isAbstract() { 243 // is abstract as its a filter 244 return true; 245 } 246 247 @Override 248 public boolean onExchange(Exchange exchange) { 249 // always accept direct endpoints as they are a special case as 250 // it will create the UoW beforehand 251 // and just continue to route that on the consumer side, which 252 // causes the EventNotifier not to 253 // emit events when the consumer received the exchange, as its 254 // already done. For example by 255 // ProducerTemplate which creates the UoW before producing 256 // messages. 257 if (exchange.getFromEndpoint() != null && exchange.getFromEndpoint().getEndpointUri().startsWith("direct:")) { 258 return true; 259 } 260 return PatternHelper.matchPattern(exchange.getFromRouteId(), "*"); 261 } 262 263 public boolean matches() { 264 // should be true as we use the onExchange to filter 265 return true; 266 } 267 268 @Override 269 public String toString() { 270 // we dont want any to string output as this is an internal 271 // predicate to match only from routes 272 return ""; 273 } 274 }); 275 return this; 276 } 277 278 /** 279 * Optionally a filter to only allow matching {@link Exchange} to be used 280 * for matching. 281 * 282 * @param predicate the predicate to use for the filter 283 * @return the builder 284 */ 285 public NotifyBuilder filter(final Predicate predicate) { 286 stack.add(new EventPredicateSupport() { 287 288 @Override 289 public boolean isAbstract() { 290 // is abstract as its a filter 291 return true; 292 } 293 294 @Override 295 public boolean onExchange(Exchange exchange) { 296 // filter non matching exchanges 297 return predicate.matches(exchange); 298 } 299 300 public boolean matches() { 301 // should be true as we use the onExchange to filter 302 return true; 303 } 304 305 @Override 306 public String toString() { 307 return "filter(" + predicate + ")"; 308 } 309 }); 310 return this; 311 } 312 313 /** 314 * Optionally a filter to only allow matching {@link Exchange} to be used 315 * for matching. 316 * 317 * @return the builder 318 */ 319 public ExpressionClauseSupport<NotifyBuilder> filter() { 320 final ExpressionClauseSupport<NotifyBuilder> clause = new ExpressionClauseSupport<>(this); 321 stack.add(new EventPredicateSupport() { 322 323 @Override 324 public boolean isAbstract() { 325 // is abstract as its a filter 326 return true; 327 } 328 329 @Override 330 public boolean onExchange(Exchange exchange) { 331 // filter non matching exchanges 332 Expression exp = clause.createExpression(exchange.getContext()); 333 return exp.evaluate(exchange, Boolean.class); 334 } 335 336 public boolean matches() { 337 // should be true as we use the onExchange to filter 338 return true; 339 } 340 341 @Override 342 public String toString() { 343 return "filter(" + clause + ")"; 344 } 345 }); 346 return clause; 347 } 348 349 /** 350 * Optionally a <tt>sent to</tt> endpoint which means that this expression 351 * should only be based on {@link Exchange} which has been sent to the given 352 * endpoint uri. 353 * <p/> 354 * Notice the {@link Exchange} may have been sent to other endpoints as 355 * well. This condition will match if the {@link Exchange} has been sent at 356 * least once to the given endpoint. 357 * 358 * @param endpointUri uri of endpoint or pattern (see the EndpointHelper 359 * javadoc) 360 * @return the builder 361 * @see EndpointHelper#matchEndpoint(org.apache.camel.CamelContext, String, 362 * String) 363 */ 364 public NotifyBuilder wereSentTo(final String endpointUri) { 365 // insert in start of stack but after the previous wereSentTo 366 stack.add(wereSentToIndex++, new EventPredicateSupport() { 367 private ConcurrentMap<String, String> sentTo = new ConcurrentHashMap<>(); 368 369 @Override 370 public boolean isAbstract() { 371 // is abstract as its a filter 372 return true; 373 } 374 375 @Override 376 public boolean onExchangeSent(Exchange exchange, Endpoint endpoint, long timeTaken) { 377 if (EndpointHelper.matchEndpoint(context, endpoint.getEndpointUri(), endpointUri)) { 378 sentTo.put(exchange.getExchangeId(), exchange.getExchangeId()); 379 } 380 return onExchange(exchange); 381 } 382 383 @Override 384 public boolean onExchange(Exchange exchange) { 385 // filter only when sentTo 386 String sent = sentTo.get(exchange.getExchangeId()); 387 return sent != null; 388 } 389 390 public boolean matches() { 391 // should be true as we use the onExchange to filter 392 return true; 393 } 394 395 @Override 396 public void reset() { 397 sentTo.clear(); 398 } 399 400 @Override 401 public String toString() { 402 return "wereSentTo(" + endpointUri + ")"; 403 } 404 }); 405 return this; 406 } 407 408 /** 409 * Sets a condition when <tt>number</tt> of {@link Exchange} has been 410 * received. 411 * <p/> 412 * The number matching is <i>at least</i> based which means that if more 413 * messages received it will match also. 414 * 415 * @param number at least number of messages 416 * @return the builder 417 */ 418 public NotifyBuilder whenReceived(final int number) { 419 stack.add(new EventPredicateSupport() { 420 private AtomicInteger current = new AtomicInteger(); 421 422 @Override 423 public boolean onExchangeCreated(Exchange exchange) { 424 current.incrementAndGet(); 425 return true; 426 } 427 428 public boolean matches() { 429 return current.get() >= number; 430 } 431 432 @Override 433 public void reset() { 434 current.set(0); 435 } 436 437 @Override 438 public String toString() { 439 return "whenReceived(" + number + ")"; 440 } 441 }); 442 return this; 443 } 444 445 /** 446 * Sets a condition when <tt>number</tt> of {@link Exchange} is done being 447 * processed. 448 * <p/> 449 * The number matching is <i>at least</i> based which means that if more 450 * messages received it will match also. 451 * <p/> 452 * The difference between <i>done</i> and <i>completed</i> is that done can 453 * also include failed messages, where as completed is only successful 454 * processed messages. 455 * 456 * @param number at least number of messages 457 * @return the builder 458 */ 459 public NotifyBuilder whenDone(final int number) { 460 stack.add(new EventPredicateSupport() { 461 private final AtomicInteger current = new AtomicInteger(); 462 463 @Override 464 public boolean onExchangeCompleted(Exchange exchange) { 465 current.incrementAndGet(); 466 return true; 467 } 468 469 @Override 470 public boolean onExchangeFailed(Exchange exchange) { 471 current.incrementAndGet(); 472 return true; 473 } 474 475 public boolean matches() { 476 return current.get() >= number; 477 } 478 479 @Override 480 public void reset() { 481 current.set(0); 482 } 483 484 @Override 485 public String toString() { 486 return "whenDone(" + number + ")"; 487 } 488 }); 489 return this; 490 } 491 492 /** 493 * Sets a condition when tne <tt>n'th</tt> (by index) {@link Exchange} is 494 * done being processed. 495 * <p/> 496 * The difference between <i>done</i> and <i>completed</i> is that done can 497 * also include failed messages, where as completed is only successful 498 * processed messages. 499 * 500 * @param index the message by index to be done 501 * @return the builder 502 */ 503 public NotifyBuilder whenDoneByIndex(final int index) { 504 stack.add(new EventPredicateSupport() { 505 private AtomicInteger current = new AtomicInteger(); 506 private String id; 507 private AtomicBoolean done = new AtomicBoolean(); 508 509 @Override 510 public boolean onExchangeCreated(Exchange exchange) { 511 if (current.get() == index) { 512 id = exchange.getExchangeId(); 513 } 514 current.incrementAndGet(); 515 return true; 516 } 517 518 @Override 519 public boolean onExchangeCompleted(Exchange exchange) { 520 if (exchange.getExchangeId().equals(id)) { 521 done.set(true); 522 } 523 return true; 524 } 525 526 @Override 527 public boolean onExchangeFailed(Exchange exchange) { 528 if (exchange.getExchangeId().equals(id)) { 529 done.set(true); 530 } 531 return true; 532 } 533 534 public boolean matches() { 535 return done.get(); 536 } 537 538 @Override 539 public void reset() { 540 current.set(0); 541 id = null; 542 done.set(false); 543 } 544 545 @Override 546 public String toString() { 547 return "whenDoneByIndex(" + index + ")"; 548 } 549 }); 550 return this; 551 } 552 553 /** 554 * Sets a condition when <tt>number</tt> of {@link Exchange} has been 555 * completed. 556 * <p/> 557 * The number matching is <i>at least</i> based which means that if more 558 * messages received it will match also. 559 * <p/> 560 * The difference between <i>done</i> and <i>completed</i> is that done can 561 * also include failed messages, where as completed is only successful 562 * processed messages. 563 * 564 * @param number at least number of messages 565 * @return the builder 566 */ 567 public NotifyBuilder whenCompleted(final int number) { 568 stack.add(new EventPredicateSupport() { 569 private AtomicInteger current = new AtomicInteger(); 570 571 @Override 572 public boolean onExchangeCompleted(Exchange exchange) { 573 current.incrementAndGet(); 574 return true; 575 } 576 577 public boolean matches() { 578 return current.get() >= number; 579 } 580 581 @Override 582 public void reset() { 583 current.set(0); 584 } 585 586 @Override 587 public String toString() { 588 return "whenCompleted(" + number + ")"; 589 } 590 }); 591 return this; 592 } 593 594 /** 595 * Sets a condition when <tt>number</tt> of {@link Exchange} has failed. 596 * <p/> 597 * The number matching is <i>at least</i> based which means that if more 598 * messages received it will match also. 599 * 600 * @param number at least number of messages 601 * @return the builder 602 */ 603 public NotifyBuilder whenFailed(final int number) { 604 stack.add(new EventPredicateSupport() { 605 private AtomicInteger current = new AtomicInteger(); 606 607 @Override 608 public boolean onExchangeFailed(Exchange exchange) { 609 current.incrementAndGet(); 610 return true; 611 } 612 613 public boolean matches() { 614 return current.get() >= number; 615 } 616 617 @Override 618 public void reset() { 619 current.set(0); 620 } 621 622 @Override 623 public String toString() { 624 return "whenFailed(" + number + ")"; 625 } 626 }); 627 return this; 628 } 629 630 /** 631 * Sets a condition when <tt>number</tt> of {@link Exchange} is done being 632 * processed. 633 * <p/> 634 * messages, where as completed is only successful processed messages. 635 * 636 * @param number exactly number of messages 637 * @return the builder 638 */ 639 public NotifyBuilder whenExactlyDone(final int number) { 640 stack.add(new EventPredicateSupport() { 641 private AtomicInteger current = new AtomicInteger(); 642 643 @Override 644 public boolean onExchangeCompleted(Exchange exchange) { 645 current.incrementAndGet(); 646 return true; 647 } 648 649 @Override 650 public boolean onExchangeFailed(Exchange exchange) { 651 current.incrementAndGet(); 652 return true; 653 } 654 655 public boolean matches() { 656 return current.get() == number; 657 } 658 659 @Override 660 public void reset() { 661 current.set(0); 662 } 663 664 @Override 665 public String toString() { 666 return "whenExactlyDone(" + number + ")"; 667 } 668 }); 669 return this; 670 } 671 672 /** 673 * Sets a condition when <tt>number</tt> of {@link Exchange} has been 674 * completed. 675 * <p/> 676 * The difference between <i>done</i> and <i>completed</i> is that done can 677 * also include failed messages, where as completed is only successful 678 * processed messages. 679 * 680 * @param number exactly number of messages 681 * @return the builder 682 */ 683 public NotifyBuilder whenExactlyCompleted(final int number) { 684 stack.add(new EventPredicateSupport() { 685 private AtomicInteger current = new AtomicInteger(); 686 687 @Override 688 public boolean onExchangeCompleted(Exchange exchange) { 689 current.incrementAndGet(); 690 return true; 691 } 692 693 public boolean matches() { 694 return current.get() == number; 695 } 696 697 @Override 698 public void reset() { 699 current.set(0); 700 } 701 702 @Override 703 public String toString() { 704 return "whenExactlyCompleted(" + number + ")"; 705 } 706 }); 707 return this; 708 } 709 710 /** 711 * Sets a condition when <tt>number</tt> of {@link Exchange} has failed. 712 * 713 * @param number exactly number of messages 714 * @return the builder 715 */ 716 public NotifyBuilder whenExactlyFailed(final int number) { 717 stack.add(new EventPredicateSupport() { 718 private AtomicInteger current = new AtomicInteger(); 719 720 @Override 721 public boolean onExchangeFailed(Exchange exchange) { 722 current.incrementAndGet(); 723 return true; 724 } 725 726 public boolean matches() { 727 return current.get() == number; 728 } 729 730 @Override 731 public void reset() { 732 current.set(0); 733 } 734 735 @Override 736 public String toString() { 737 return "whenExactlyFailed(" + number + ")"; 738 } 739 }); 740 return this; 741 } 742 743 /** 744 * Sets a condition that <b>any received</b> {@link Exchange} should match 745 * the {@link Predicate} 746 * 747 * @param predicate the predicate 748 * @return the builder 749 */ 750 public NotifyBuilder whenAnyReceivedMatches(final Predicate predicate) { 751 return doWhenAnyMatches(predicate, true); 752 } 753 754 /** 755 * Sets a condition that <b>any done</b> {@link Exchange} should match the 756 * {@link Predicate} 757 * 758 * @param predicate the predicate 759 * @return the builder 760 */ 761 public NotifyBuilder whenAnyDoneMatches(final Predicate predicate) { 762 return doWhenAnyMatches(predicate, false); 763 } 764 765 private NotifyBuilder doWhenAnyMatches(final Predicate predicate, final boolean received) { 766 stack.add(new EventPredicateSupport() { 767 private final AtomicBoolean matches = new AtomicBoolean(); 768 769 @Override 770 public boolean onExchangeCompleted(Exchange exchange) { 771 if (!received && !matches.get()) { 772 matches.set(predicate.matches(exchange)); 773 } 774 return true; 775 } 776 777 @Override 778 public boolean onExchangeFailed(Exchange exchange) { 779 if (!received && !matches.get()) { 780 matches.set(predicate.matches(exchange)); 781 } 782 return true; 783 } 784 785 @Override 786 public boolean onExchangeCreated(Exchange exchange) { 787 if (received && !matches.get()) { 788 matches.set(predicate.matches(exchange)); 789 } 790 return true; 791 } 792 793 public boolean matches() { 794 return matches.get(); 795 } 796 797 @Override 798 public void reset() { 799 matches.set(false); 800 } 801 802 @Override 803 public String toString() { 804 if (received) { 805 return "whenAnyReceivedMatches(" + predicate + ")"; 806 } else { 807 return "whenAnyDoneMatches(" + predicate + ")"; 808 } 809 } 810 }); 811 return this; 812 } 813 814 /** 815 * Sets a condition that <b>all received</b> {@link Exchange} should match 816 * the {@link Predicate} 817 * 818 * @param predicate the predicate 819 * @return the builder 820 */ 821 public NotifyBuilder whenAllReceivedMatches(final Predicate predicate) { 822 return doWhenAllMatches(predicate, true); 823 } 824 825 /** 826 * Sets a condition that <b>all done</b> {@link Exchange} should match the 827 * {@link Predicate} 828 * 829 * @param predicate the predicate 830 * @return the builder 831 */ 832 public NotifyBuilder whenAllDoneMatches(final Predicate predicate) { 833 return doWhenAllMatches(predicate, false); 834 } 835 836 private NotifyBuilder doWhenAllMatches(final Predicate predicate, final boolean received) { 837 stack.add(new EventPredicateSupport() { 838 private final AtomicBoolean matches = new AtomicBoolean(true); 839 840 @Override 841 public boolean onExchangeCompleted(Exchange exchange) { 842 if (!received && matches.get()) { 843 matches.set(predicate.matches(exchange)); 844 } 845 return true; 846 } 847 848 @Override 849 public boolean onExchangeFailed(Exchange exchange) { 850 if (!received && matches.get()) { 851 matches.set(predicate.matches(exchange)); 852 } 853 return true; 854 } 855 856 @Override 857 public boolean onExchangeCreated(Exchange exchange) { 858 if (received && matches.get()) { 859 matches.set(predicate.matches(exchange)); 860 } 861 return true; 862 } 863 864 public boolean matches() { 865 return matches.get(); 866 } 867 868 @Override 869 public void reset() { 870 matches.set(true); 871 } 872 873 @Override 874 public String toString() { 875 if (received) { 876 return "whenAllReceivedMatches(" + predicate + ")"; 877 } else { 878 return "whenAllDoneMatches(" + predicate + ")"; 879 } 880 } 881 }); 882 return this; 883 } 884 885 /** 886 * Sets a condition that the bodies is expected to be <b>received</b> in the 887 * order as well. 888 * <p/> 889 * This condition will discard any additional messages. If you need a more 890 * strict condition then use {@link #whenExactBodiesReceived(Object...)} 891 * 892 * @param bodies the expected bodies 893 * @return the builder 894 * @see #whenExactBodiesReceived(Object...) 895 */ 896 public NotifyBuilder whenBodiesReceived(Object... bodies) { 897 List<Object> bodyList = new ArrayList<>(); 898 bodyList.addAll(Arrays.asList(bodies)); 899 return doWhenBodies(bodyList, true, false); 900 } 901 902 /** 903 * Sets a condition that the bodies is expected to be <b>done</b> in the 904 * order as well. 905 * <p/> 906 * This condition will discard any additional messages. If you need a more 907 * strict condition then use {@link #whenExactBodiesDone(Object...)} 908 * 909 * @param bodies the expected bodies 910 * @return the builder 911 * @see #whenExactBodiesDone(Object...) 912 */ 913 public NotifyBuilder whenBodiesDone(Object... bodies) { 914 List<Object> bodyList = new ArrayList<>(); 915 bodyList.addAll(Arrays.asList(bodies)); 916 return doWhenBodies(bodyList, false, false); 917 } 918 919 /** 920 * Sets a condition that the bodies is expected to be <b>received</b> in the 921 * order as well. 922 * <p/> 923 * This condition is strict which means that it only expect that exact 924 * number of bodies 925 * 926 * @param bodies the expected bodies 927 * @return the builder 928 * @see #whenBodiesReceived(Object...) 929 */ 930 public NotifyBuilder whenExactBodiesReceived(Object... bodies) { 931 List<Object> bodyList = new ArrayList<>(); 932 bodyList.addAll(Arrays.asList(bodies)); 933 return doWhenBodies(bodyList, true, true); 934 } 935 936 /** 937 * Sets a condition that the bodies is expected to be <b>done</b> in the 938 * order as well. 939 * <p/> 940 * This condition is strict which means that it only expect that exact 941 * number of bodies 942 * 943 * @param bodies the expected bodies 944 * @return the builder 945 * @see #whenExactBodiesDone(Object...) 946 */ 947 public NotifyBuilder whenExactBodiesDone(Object... bodies) { 948 List<Object> bodyList = new ArrayList<>(); 949 bodyList.addAll(Arrays.asList(bodies)); 950 return doWhenBodies(bodyList, false, true); 951 } 952 953 private NotifyBuilder doWhenBodies(final List<?> bodies, final boolean received, final boolean exact) { 954 stack.add(new EventPredicateSupport() { 955 private volatile boolean matches; 956 private final AtomicInteger current = new AtomicInteger(); 957 958 @Override 959 public boolean onExchangeCreated(Exchange exchange) { 960 if (received) { 961 matchBody(exchange); 962 } 963 return true; 964 } 965 966 @Override 967 public boolean onExchangeFailed(Exchange exchange) { 968 if (!received) { 969 matchBody(exchange); 970 } 971 return true; 972 } 973 974 @Override 975 public boolean onExchangeCompleted(Exchange exchange) { 976 if (!received) { 977 matchBody(exchange); 978 } 979 return true; 980 } 981 982 private void matchBody(Exchange exchange) { 983 if (current.incrementAndGet() > bodies.size()) { 984 // out of bounds 985 return; 986 } 987 988 Object actual = exchange.getIn().getBody(); 989 Object expected = bodies.get(current.get() - 1); 990 matches = ObjectHelper.equal(expected, actual); 991 } 992 993 public boolean matches() { 994 if (exact) { 995 return matches && current.get() == bodies.size(); 996 } else { 997 return matches && current.get() >= bodies.size(); 998 } 999 } 1000 1001 @Override 1002 public void reset() { 1003 matches = false; 1004 current.set(0); 1005 } 1006 1007 @Override 1008 public String toString() { 1009 if (received) { 1010 return "" + (exact ? "whenExactBodiesReceived(" : "whenBodiesReceived(") + bodies + ")"; 1011 } else { 1012 return "" + (exact ? "whenExactBodiesDone(" : "whenBodiesDone(") + bodies + ")"; 1013 } 1014 } 1015 }); 1016 return this; 1017 } 1018 1019 /** 1020 * Sets a condition when the provided matcher (such as mock endpoint) is 1021 * satisfied based on {@link Exchange} being sent to it when they are 1022 * <b>done</b>. 1023 * <p/> 1024 * The idea is that you can use mock endpoints (or other matchers) for 1025 * setting fine grained expectations and then use that together with this 1026 * builder. The mock provided does <b>NOT</b> have to already exist in the 1027 * route. You can just create a new pseudo mock and this builder will send 1028 * the done {@link Exchange} to it. So its like adding the mock to the end 1029 * of your route(s). 1030 * 1031 * @param matcher the matcher such as mock endpoint 1032 * @return the builder 1033 */ 1034 public NotifyBuilder whenDoneSatisfied(final NotifyBuilderMatcher matcher) { 1035 return doWhenSatisfied(matcher, false); 1036 } 1037 1038 /** 1039 * Sets a condition when the provided matcher (such as mock endpoint) is 1040 * satisfied based on {@link Exchange} being sent to it when they are 1041 * <b>received</b>. 1042 * <p/> 1043 * The idea is that you can use mock endpoints (or other matchers) for 1044 * setting fine grained expectations and then use that together with this 1045 * builder. The mock provided does <b>NOT</b> have to already exist in the 1046 * route. You can just create a new pseudo mock and this builder will send 1047 * the done {@link Exchange} to it. So its like adding the mock to the end 1048 * of your route(s). 1049 * 1050 * @param matcher the matcher such as mock endpoint 1051 * @return the builder 1052 */ 1053 public NotifyBuilder whenReceivedSatisfied(final NotifyBuilderMatcher matcher) { 1054 return doWhenSatisfied(matcher, true); 1055 } 1056 1057 private NotifyBuilder doWhenSatisfied(final NotifyBuilderMatcher matcher, final boolean received) { 1058 stack.add(new EventPredicateSupport() { 1059 1060 @Override 1061 public boolean onExchangeCreated(Exchange exchange) { 1062 if (received) { 1063 matcher.notifyBuilderOnExchange(exchange); 1064 } 1065 return true; 1066 } 1067 1068 @Override 1069 public boolean onExchangeFailed(Exchange exchange) { 1070 if (!received) { 1071 matcher.notifyBuilderOnExchange(exchange); 1072 } 1073 return true; 1074 } 1075 1076 @Override 1077 public boolean onExchangeCompleted(Exchange exchange) { 1078 if (!received) { 1079 matcher.notifyBuilderOnExchange(exchange); 1080 } 1081 return true; 1082 } 1083 1084 public boolean matches() { 1085 return matcher.notifyBuilderMatches(); 1086 } 1087 1088 @Override 1089 public void reset() { 1090 matcher.notifyBuilderReset(); 1091 } 1092 1093 @Override 1094 public String toString() { 1095 if (received) { 1096 return "whenReceivedSatisfied(" + matcher + ")"; 1097 } else { 1098 return "whenDoneSatisfied(" + matcher + ")"; 1099 } 1100 } 1101 }); 1102 return this; 1103 } 1104 1105 /** 1106 * Sets a condition when the provided matcher (such as mock endpoint) is 1107 * <b>not</b> satisfied based on {@link Exchange} being sent to it when they 1108 * are <b>received</b>. 1109 * <p/> 1110 * The idea is that you can use mock endpoints (or other matchers) for 1111 * setting fine grained expectations and then use that together with this 1112 * builder. The mock provided does <b>NOT</b> have to already exist in the 1113 * route. You can just create a new pseudo mock and this builder will send 1114 * the done {@link Exchange} to it. So its like adding the mock to the end 1115 * of your route(s). 1116 * 1117 * @param matcher the matcher such as mock endpoint 1118 * @return the builder 1119 */ 1120 public NotifyBuilder whenReceivedNotSatisfied(final NotifyBuilderMatcher matcher) { 1121 return doWhenNotSatisfied(matcher, true); 1122 } 1123 1124 /** 1125 * Sets a condition when the provided matcher (such as mock endpoint) is 1126 * <b>not</b> satisfied based on {@link Exchange} being sent to it when they 1127 * are <b>done</b>. 1128 * <p/> 1129 * The idea is that you can use mock endpoints (or other matchers) for 1130 * setting fine grained expectations and then use that together with this 1131 * builder. The mock provided does <b>NOT</b> have to already exist in the 1132 * route. You can just create a new pseudo mock and this builder will send 1133 * the done {@link Exchange} to it. So its like adding the mock to the end 1134 * of your route(s). 1135 * 1136 * @param matcher the matcher such as mock endpoint 1137 * @return the builder 1138 */ 1139 public NotifyBuilder whenDoneNotSatisfied(final NotifyBuilderMatcher matcher) { 1140 return doWhenNotSatisfied(matcher, false); 1141 } 1142 1143 private NotifyBuilder doWhenNotSatisfied(final NotifyBuilderMatcher mock, final boolean received) { 1144 stack.add(new EventPredicateSupport() { 1145 1146 @Override 1147 public boolean onExchangeCreated(Exchange exchange) { 1148 if (received) { 1149 mock.notifyBuilderOnExchange(exchange); 1150 } 1151 return true; 1152 } 1153 1154 @Override 1155 public boolean onExchangeFailed(Exchange exchange) { 1156 if (!received) { 1157 mock.notifyBuilderOnExchange(exchange); 1158 } 1159 return true; 1160 } 1161 1162 @Override 1163 public boolean onExchangeCompleted(Exchange exchange) { 1164 if (!received) { 1165 mock.notifyBuilderOnExchange(exchange); 1166 } 1167 return true; 1168 } 1169 1170 public boolean matches() { 1171 return !mock.notifyBuilderMatches(); 1172 } 1173 1174 @Override 1175 public void reset() { 1176 mock.notifyBuilderReset(); 1177 } 1178 1179 @Override 1180 public String toString() { 1181 if (received) { 1182 return "whenReceivedNotSatisfied(" + mock + ")"; 1183 } else { 1184 return "whenDoneNotSatisfied(" + mock + ")"; 1185 } 1186 } 1187 }); 1188 return this; 1189 } 1190 1191 /** 1192 * Prepares to append an additional expression using the <i>and</i> 1193 * operator. 1194 * 1195 * @return the builder 1196 */ 1197 public NotifyBuilder and() { 1198 doCreate(EventOperation.and); 1199 return this; 1200 } 1201 1202 /** 1203 * Prepares to append an additional expression using the <i>or</i> operator. 1204 * 1205 * @return the builder 1206 */ 1207 public NotifyBuilder or() { 1208 doCreate(EventOperation.or); 1209 return this; 1210 } 1211 1212 /** 1213 * Prepares to append an additional expression using the <i>not</i> 1214 * operator. 1215 * 1216 * @return the builder 1217 */ 1218 public NotifyBuilder not() { 1219 doCreate(EventOperation.not); 1220 return this; 1221 } 1222 1223 /** 1224 * Specifies the wait time in millis to use in the 1225 * {@link #matchesWaitTime()} method. 1226 */ 1227 public NotifyBuilder waitTime(long waitTime) { 1228 this.waitTime = waitTime; 1229 return this; 1230 } 1231 1232 /** 1233 * Creates the expression this builder should use for matching. 1234 * <p/> 1235 * You must call this method when you are finished building the expressions. 1236 * 1237 * @return the created builder ready for matching 1238 */ 1239 public NotifyBuilder create() { 1240 doCreate(EventOperation.and); 1241 if (eventNotifier.isStopped()) { 1242 throw new IllegalStateException("A destroyed NotifyBuilder cannot be re-created."); 1243 } 1244 created = true; 1245 return this; 1246 } 1247 1248 /** 1249 * De-registers this builder from its {@link CamelContext}. 1250 * <p/> 1251 * Once destroyed, this instance will not function again. 1252 */ 1253 public void destroy() { 1254 context.getManagementStrategy().removeEventNotifier(eventNotifier); 1255 try { 1256 ServiceHelper.stopService(eventNotifier); 1257 } catch (Exception e) { 1258 throw RuntimeCamelException.wrapRuntimeCamelException(e); 1259 } 1260 created = false; 1261 } 1262 1263 /** 1264 * Does all the expression match? 1265 * <p/> 1266 * This operation will return immediately which means it can be used for 1267 * testing at this very moment. 1268 * 1269 * @return <tt>true</tt> if matching, <tt>false</tt> otherwise 1270 */ 1271 public boolean matches() { 1272 if (!created) { 1273 throw new IllegalStateException("NotifyBuilder has not been created. Invoke the create() method before matching."); 1274 } 1275 return matches; 1276 } 1277 1278 /** 1279 * Does all the expression match? 1280 * <p/> 1281 * This operation will wait until the match is <tt>true</tt> or otherwise a 1282 * timeout occur which means <tt>false</tt> will be returned. 1283 * 1284 * @param timeout the timeout value 1285 * @param timeUnit the time unit 1286 * @return <tt>true</tt> if matching, <tt>false</tt> otherwise due to 1287 * timeout 1288 */ 1289 public boolean matches(long timeout, TimeUnit timeUnit) { 1290 if (!created) { 1291 throw new IllegalStateException("NotifyBuilder has not been created. Invoke the create() method before matching."); 1292 } 1293 try { 1294 latch.await(timeout, timeUnit); 1295 } catch (InterruptedException e) { 1296 throw RuntimeCamelException.wrapRuntimeCamelException(e); 1297 } 1298 return matches(); 1299 } 1300 1301 /** 1302 * Does all the expressions match? 1303 * <p/> 1304 * This operation will wait until the match is <tt>true</tt> or otherwise a 1305 * timeout occur which means <tt>false</tt> will be returned. 1306 * <p/> 1307 * The timeout value is by default 10 seconds. 1308 * 1309 * @return <tt>true</tt> if matching, <tt>false</tt> otherwise due to 1310 * timeout 1311 * @deprecated use {@link #matchesWaitTime()} instead 1312 */ 1313 @Deprecated 1314 public boolean matchesMockWaitTime() { 1315 return matchesWaitTime(); 1316 } 1317 1318 /** 1319 * Does all the expressions match? 1320 * <p/> 1321 * This operation will wait until the match is <tt>true</tt> or otherwise a 1322 * timeout occur which means <tt>false</tt> will be returned. 1323 * <p/> 1324 * The timeout value is by default 10 seconds. 1325 * 1326 * @return <tt>true</tt> if matching, <tt>false</tt> otherwise due to 1327 * timeout 1328 */ 1329 public boolean matchesWaitTime() { 1330 if (!created) { 1331 throw new IllegalStateException("NotifyBuilder has not been created. Invoke the create() method before matching."); 1332 } 1333 1334 return matches(waitTime, TimeUnit.MILLISECONDS); 1335 } 1336 1337 /** 1338 * Resets the notifier. 1339 */ 1340 public void reset() { 1341 for (EventPredicateHolder predicate : predicates) { 1342 predicate.reset(); 1343 } 1344 latch = new CountDownLatch(1); 1345 matches = false; 1346 } 1347 1348 @Override 1349 public String toString() { 1350 StringBuilder sb = new StringBuilder(); 1351 for (EventPredicateHolder eventPredicateHolder : predicates) { 1352 if (sb.length() > 0) { 1353 sb.append("."); 1354 } 1355 sb.append(eventPredicateHolder.toString()); 1356 } 1357 // a crude way of skipping the first invisible operation 1358 return StringHelper.after(sb.toString(), "()."); 1359 } 1360 1361 private void doCreate(EventOperation newOperation) { 1362 // init operation depending on the newOperation 1363 if (operation == null) { 1364 // if the first new operation is an or then this operation must be 1365 // an or as well 1366 // otherwise it should be and based 1367 operation = newOperation == EventOperation.or ? EventOperation.or : EventOperation.and; 1368 } 1369 1370 // we have some predicates 1371 if (!stack.isEmpty()) { 1372 // we only want to match from routes, so skip for example events 1373 // which is triggered by producer templates etc. 1374 fromRoutesOnly(); 1375 1376 // the stack must have at least one non abstract 1377 boolean found = false; 1378 for (EventPredicate predicate : stack) { 1379 if (!predicate.isAbstract()) { 1380 found = true; 1381 break; 1382 } 1383 } 1384 if (!found) { 1385 throw new IllegalArgumentException("NotifyBuilder must contain at least one non-abstract predicate (such as whenDone)"); 1386 } 1387 1388 CompoundEventPredicate compound = new CompoundEventPredicate(stack); 1389 stack.clear(); 1390 predicates.add(new EventPredicateHolder(operation, compound)); 1391 } 1392 1393 operation = newOperation; 1394 // reset wereSentTo index position as this its a new group 1395 wereSentToIndex = 0; 1396 } 1397 1398 /** 1399 * Notifier which hooks into Camel to listen for {@link Exchange} relevant 1400 * events for this builder 1401 */ 1402 private final class ExchangeNotifier extends EventNotifierSupport { 1403 1404 @Override 1405 public void notify(CamelEvent event) throws Exception { 1406 if (event instanceof ExchangeCreatedEvent) { 1407 onExchangeCreated((ExchangeCreatedEvent)event); 1408 } else if (event instanceof ExchangeCompletedEvent) { 1409 onExchangeCompleted((ExchangeCompletedEvent)event); 1410 } else if (event instanceof ExchangeFailedEvent) { 1411 onExchangeFailed((ExchangeFailedEvent)event); 1412 } else if (event instanceof ExchangeSentEvent) { 1413 onExchangeSent((ExchangeSentEvent)event); 1414 } 1415 1416 // now compute whether we matched 1417 computeMatches(); 1418 } 1419 1420 @Override 1421 public boolean isEnabled(CamelEvent event) { 1422 return true; 1423 } 1424 1425 private void onExchangeCreated(ExchangeCreatedEvent event) { 1426 for (EventPredicateHolder predicate : predicates) { 1427 predicate.getPredicate().onExchangeCreated(event.getExchange()); 1428 } 1429 } 1430 1431 private void onExchangeCompleted(ExchangeCompletedEvent event) { 1432 for (EventPredicateHolder predicate : predicates) { 1433 predicate.getPredicate().onExchangeCompleted(event.getExchange()); 1434 } 1435 } 1436 1437 private void onExchangeFailed(ExchangeFailedEvent event) { 1438 for (EventPredicateHolder predicate : predicates) { 1439 predicate.getPredicate().onExchangeFailed(event.getExchange()); 1440 } 1441 } 1442 1443 private void onExchangeSent(ExchangeSentEvent event) { 1444 for (EventPredicateHolder predicate : predicates) { 1445 predicate.getPredicate().onExchangeSent(event.getExchange(), event.getEndpoint(), event.getTimeTaken()); 1446 } 1447 } 1448 1449 private synchronized void computeMatches() { 1450 // use a temporary answer until we have computed the value to assign 1451 Boolean answer = null; 1452 1453 for (EventPredicateHolder holder : predicates) { 1454 EventOperation operation = holder.getOperation(); 1455 if (EventOperation.and == operation) { 1456 if (holder.getPredicate().matches()) { 1457 answer = true; 1458 } else { 1459 answer = false; 1460 // and break out since its an AND so it must match 1461 break; 1462 } 1463 } else if (EventOperation.or == operation) { 1464 if (holder.getPredicate().matches()) { 1465 answer = true; 1466 } 1467 } else if (EventOperation.not == operation) { 1468 if (holder.getPredicate().matches()) { 1469 answer = false; 1470 // and break out since its a NOT so it must not match 1471 break; 1472 } else { 1473 answer = true; 1474 } 1475 } 1476 } 1477 1478 // if we did compute a value then assign that 1479 if (answer != null) { 1480 matches = answer; 1481 if (matches) { 1482 // signal completion 1483 latch.countDown(); 1484 } 1485 } 1486 } 1487 1488 @Override 1489 protected void doStart() throws Exception { 1490 // we only care about Exchange events 1491 setIgnoreCamelContextEvents(true); 1492 setIgnoreRouteEvents(true); 1493 setIgnoreServiceEvents(true); 1494 } 1495 } 1496 1497 private enum EventOperation { 1498 and, or, not 1499 } 1500 1501 private interface EventPredicate { 1502 1503 /** 1504 * Evaluates whether the predicate matched or not. 1505 * 1506 * @return <tt>true</tt> if matched, <tt>false</tt> otherwise 1507 */ 1508 boolean matches(); 1509 1510 /** 1511 * Resets the predicate 1512 */ 1513 void reset(); 1514 1515 /** 1516 * Whether the predicate is abstract 1517 */ 1518 boolean isAbstract(); 1519 1520 /** 1521 * Callback for {@link Exchange} lifecycle 1522 * 1523 * @param exchange the exchange 1524 * @return <tt>true</tt> to allow continue evaluating, <tt>false</tt> to 1525 * stop immediately 1526 */ 1527 boolean onExchangeCreated(Exchange exchange); 1528 1529 /** 1530 * Callback for {@link Exchange} lifecycle 1531 * 1532 * @param exchange the exchange 1533 * @return <tt>true</tt> to allow continue evaluating, <tt>false</tt> to 1534 * stop immediately 1535 */ 1536 boolean onExchangeCompleted(Exchange exchange); 1537 1538 /** 1539 * Callback for {@link Exchange} lifecycle 1540 * 1541 * @param exchange the exchange 1542 * @return <tt>true</tt> to allow continue evaluating, <tt>false</tt> to 1543 * stop immediately 1544 */ 1545 boolean onExchangeFailed(Exchange exchange); 1546 1547 /** 1548 * Callback for {@link Exchange} lifecycle 1549 * 1550 * @param exchange the exchange 1551 * @param endpoint the endpoint sent to 1552 * @param timeTaken time taken in millis to send the to endpoint 1553 * @return <tt>true</tt> to allow continue evaluating, <tt>false</tt> to 1554 * stop immediately 1555 */ 1556 boolean onExchangeSent(Exchange exchange, Endpoint endpoint, long timeTaken); 1557 } 1558 1559 private abstract class EventPredicateSupport implements EventPredicate { 1560 1561 @Override 1562 public boolean isAbstract() { 1563 return false; 1564 } 1565 1566 @Override 1567 public void reset() { 1568 // noop 1569 } 1570 1571 @Override 1572 public boolean onExchangeCreated(Exchange exchange) { 1573 return onExchange(exchange); 1574 } 1575 1576 @Override 1577 public boolean onExchangeCompleted(Exchange exchange) { 1578 return onExchange(exchange); 1579 } 1580 1581 @Override 1582 public boolean onExchangeFailed(Exchange exchange) { 1583 return onExchange(exchange); 1584 } 1585 1586 @Override 1587 public boolean onExchangeSent(Exchange exchange, Endpoint endpoint, long timeTaken) { 1588 // no need to invoke onExchange as this is a special case when the 1589 // Exchange 1590 // was sent to a specific endpoint 1591 return true; 1592 } 1593 1594 public boolean onExchange(Exchange exchange) { 1595 return true; 1596 } 1597 } 1598 1599 /** 1600 * To hold an operation and predicate 1601 */ 1602 private final class EventPredicateHolder { 1603 private final EventOperation operation; 1604 private final EventPredicate predicate; 1605 1606 private EventPredicateHolder(EventOperation operation, EventPredicate predicate) { 1607 this.operation = operation; 1608 this.predicate = predicate; 1609 } 1610 1611 public EventOperation getOperation() { 1612 return operation; 1613 } 1614 1615 public EventPredicate getPredicate() { 1616 return predicate; 1617 } 1618 1619 public void reset() { 1620 predicate.reset(); 1621 } 1622 1623 @Override 1624 public String toString() { 1625 return operation.name() + "()." + predicate; 1626 } 1627 } 1628 1629 /** 1630 * To hold multiple predicates which are part of same expression 1631 */ 1632 private final class CompoundEventPredicate implements EventPredicate { 1633 1634 private List<EventPredicate> predicates = new ArrayList<>(); 1635 1636 private CompoundEventPredicate(List<EventPredicate> predicates) { 1637 this.predicates.addAll(predicates); 1638 } 1639 1640 @Override 1641 public boolean isAbstract() { 1642 return false; 1643 } 1644 1645 @Override 1646 public boolean matches() { 1647 for (EventPredicate predicate : predicates) { 1648 boolean answer = predicate.matches(); 1649 LOG.trace("matches() {} -> {}", predicate, answer); 1650 if (!answer) { 1651 // break at first false 1652 return false; 1653 } 1654 } 1655 return true; 1656 } 1657 1658 @Override 1659 public void reset() { 1660 for (EventPredicate predicate : predicates) { 1661 LOG.trace("reset() {}", predicate); 1662 predicate.reset(); 1663 } 1664 } 1665 1666 @Override 1667 public boolean onExchangeCreated(Exchange exchange) { 1668 for (EventPredicate predicate : predicates) { 1669 boolean answer = predicate.onExchangeCreated(exchange); 1670 LOG.trace("onExchangeCreated() {} -> {}", predicate, answer); 1671 if (!answer) { 1672 // break at first false 1673 return false; 1674 } 1675 } 1676 return true; 1677 } 1678 1679 @Override 1680 public boolean onExchangeCompleted(Exchange exchange) { 1681 for (EventPredicate predicate : predicates) { 1682 boolean answer = predicate.onExchangeCompleted(exchange); 1683 LOG.trace("onExchangeCompleted() {} -> {}", predicate, answer); 1684 if (!answer) { 1685 // break at first false 1686 return false; 1687 } 1688 } 1689 return true; 1690 } 1691 1692 @Override 1693 public boolean onExchangeFailed(Exchange exchange) { 1694 for (EventPredicate predicate : predicates) { 1695 boolean answer = predicate.onExchangeFailed(exchange); 1696 LOG.trace("onExchangeFailed() {} -> {}", predicate, answer); 1697 if (!answer) { 1698 // break at first false 1699 return false; 1700 } 1701 } 1702 return true; 1703 } 1704 1705 @Override 1706 public boolean onExchangeSent(Exchange exchange, Endpoint endpoint, long timeTaken) { 1707 for (EventPredicate predicate : predicates) { 1708 boolean answer = predicate.onExchangeSent(exchange, endpoint, timeTaken); 1709 LOG.trace("onExchangeSent() {} {} -> {}", endpoint, predicate, answer); 1710 if (!answer) { 1711 // break at first false 1712 return false; 1713 } 1714 } 1715 return true; 1716 } 1717 1718 @Override 1719 public String toString() { 1720 StringBuilder sb = new StringBuilder(); 1721 for (EventPredicate eventPredicate : predicates) { 1722 if (sb.length() > 0) { 1723 sb.append("."); 1724 } 1725 sb.append(eventPredicate.toString()); 1726 } 1727 return sb.toString(); 1728 } 1729 } 1730 1731}