001 /**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.camel.builder;
018
019 import java.util.ArrayList;
020 import java.util.Arrays;
021 import java.util.EventObject;
022 import java.util.Iterator;
023 import java.util.List;
024 import java.util.Stack;
025 import java.util.concurrent.CountDownLatch;
026 import java.util.concurrent.TimeUnit;
027
028 import org.apache.camel.CamelContext;
029 import org.apache.camel.Exchange;
030 import org.apache.camel.Expression;
031 import org.apache.camel.Predicate;
032 import org.apache.camel.Producer;
033 import org.apache.camel.component.mock.MockEndpoint;
034 import org.apache.camel.management.EventNotifierSupport;
035 import org.apache.camel.management.event.ExchangeCompletedEvent;
036 import org.apache.camel.management.event.ExchangeCreatedEvent;
037 import org.apache.camel.management.event.ExchangeFailureEvent;
038 import org.apache.camel.spi.EventNotifier;
039 import org.apache.camel.util.EndpointHelper;
040 import org.apache.camel.util.ObjectHelper;
041 import org.apache.camel.util.ServiceHelper;
042
043 /**
044 * A builder to build an expression based on {@link org.apache.camel.spi.EventNotifier} notifications
045 * about {@link Exchange} being routed.
046 * <p/>
047 * This builder can be used for testing purposes where you want to know when a test is supposed to be done.
048 * The idea is that you can build an expression that explains when the test is done. For example when Camel
049 * have finished routing 5 messages. You can then in your test await for this condition to occur.
050 *
051 * @version $Revision: 900466 $
052 */
053 public class NotifyBuilder {
054
055 // notifier to hook into Camel to listen for events
056 private final EventNotifier eventNotifier = new ExchangeNotifier();
057
058 // the predicates build with this builder
059 private final List<EventPredicateHolder> predicates = new ArrayList<EventPredicateHolder>();
060
061 // latch to be used to signal predicates matches
062 private final CountDownLatch latch = new CountDownLatch(1);
063
064 // the current state while building an event predicate where we use a stack and the operation
065 private final Stack<EventPredicate> stack = new Stack<EventPredicate>();
066 private EventOperation operation;
067
068 // computed value whether all the predicates matched
069 private boolean matches;
070
071 /**
072 * Creates a new builder.
073 *
074 * @param context the Camel context
075 */
076 public NotifyBuilder(CamelContext context) {
077 try {
078 ServiceHelper.startService(eventNotifier);
079 } catch (Exception e) {
080 throw ObjectHelper.wrapRuntimeCamelException(e);
081 }
082 context.getManagementStrategy().addEventNotifier(eventNotifier);
083 }
084
085 /**
086 * Optionally a <tt>from</tt> endpoint which means that this expression should only be based
087 * on {@link Exchange} which is originated from the particular endpoint(s).
088 *
089 * @param endpointUri uri of endpoint or pattern (see the EndpointHelper javadoc)
090 * @return the builder
091 * @see org.apache.camel.util.EndpointHelper#matchEndpoint(String, String)
092 */
093 public NotifyBuilder from(final String endpointUri) {
094 stack.push(new EventPredicateSupport() {
095
096 @Override
097 public boolean onExchange(Exchange exchange) {
098 // filter non matching exchanges
099 return EndpointHelper.matchEndpoint(exchange.getFromEndpoint().getEndpointUri(), endpointUri);
100 }
101
102 public boolean matches() {
103 return true;
104 }
105
106 @Override
107 public String toString() {
108 return "from(" + endpointUri + ")";
109 }
110 });
111 return this;
112 }
113
114 /**
115 * Optionally a filter to only allow matching {@link Exchange} to be used for matching.
116 *
117 * @param predicate the predicate to use for the filter
118 * @return the builder
119 */
120 public NotifyBuilder filter(final Predicate predicate) {
121 stack.push(new EventPredicateSupport() {
122
123 @Override
124 public boolean onExchange(Exchange exchange) {
125 // filter non matching exchanges
126 return predicate.matches(exchange);
127 }
128
129 public boolean matches() {
130 return true;
131 }
132
133 @Override
134 public String toString() {
135 return "filter(" + predicate + ")";
136 }
137 });
138 return this;
139 }
140
141 /**
142 * Optionally a filter to only allow matching {@link Exchange} to be used for matching.
143 *
144 * @return the builder
145 */
146 public ExpressionClauseSupport<NotifyBuilder> filter() {
147 final ExpressionClauseSupport<NotifyBuilder> clause = new ExpressionClauseSupport<NotifyBuilder>(this);
148 stack.push(new EventPredicateSupport() {
149
150 @Override
151 public boolean onExchange(Exchange exchange) {
152 // filter non matching exchanges
153 Expression exp = clause.createExpression(exchange.getContext());
154 return exp.evaluate(exchange, Boolean.class);
155 }
156
157 public boolean matches() {
158 return true;
159 }
160
161 @Override
162 public String toString() {
163 return "filter(" + clause + ")";
164 }
165 });
166 return clause;
167 }
168
169 /**
170 * Sets a condition when <tt>number</tt> of {@link Exchange} has been received.
171 * <p/>
172 * The number matching is <i>at least</i> based which means that if more messages received
173 * it will match also.
174 *
175 * @param number at least number of messages
176 * @return the builder
177 */
178 public NotifyBuilder whenReceived(final int number) {
179 stack.push(new EventPredicateSupport() {
180 private int current;
181
182 @Override
183 public boolean onExchangeCreated(Exchange exchange) {
184 current++;
185 return true;
186 }
187
188 public boolean matches() {
189 return current >= number;
190 }
191
192 @Override
193 public String toString() {
194 return "whenReceived(" + number + ")";
195 }
196 });
197 return this;
198 }
199
200 /**
201 * Sets a condition when <tt>number</tt> of {@link Exchange} is done being processed.
202 * <p/>
203 * The number matching is <i>at least</i> based which means that if more messages received
204 * it will match also.
205 * <p/>
206 * The difference between <i>done</i> and <i>completed</i> is that done can also include failed
207 * messages, where as completed is only successful processed messages.
208 *
209 * @param number at least number of messages
210 * @return the builder
211 */
212 public NotifyBuilder whenDone(final int number) {
213 stack.add(new EventPredicateSupport() {
214 private int current;
215
216 @Override
217 public boolean onExchangeCompleted(Exchange exchange) {
218 current++;
219 return true;
220 }
221
222 @Override
223 public boolean onExchangeFailure(Exchange exchange) {
224 current++;
225 return true;
226 }
227
228 public boolean matches() {
229 return current >= number;
230 }
231
232 @Override
233 public String toString() {
234 return "whenDone(" + number + ")";
235 }
236 });
237 return this;
238 }
239
240 /**
241 * Sets a condition when <tt>number</tt> of {@link Exchange} has been completed.
242 * <p/>
243 * The number matching is <i>at least</i> based which means that if more messages received
244 * it will match also.
245 * <p/>
246 * The difference between <i>done</i> and <i>completed</i> is that done can also include failed
247 * messages, where as completed is only successful processed messages.
248 *
249 * @param number at least number of messages
250 * @return the builder
251 */
252 public NotifyBuilder whenCompleted(final int number) {
253 stack.add(new EventPredicateSupport() {
254 private int current;
255
256 @Override
257 public boolean onExchangeCompleted(Exchange exchange) {
258 current++;
259 return true;
260 }
261
262 public boolean matches() {
263 return current >= number;
264 }
265
266 @Override
267 public String toString() {
268 return "whenCompleted(" + number + ")";
269 }
270 });
271 return this;
272 }
273
274 /**
275 * Sets a condition when <tt>number</tt> of {@link Exchange} has failed.
276 * <p/>
277 * The number matching is <i>at least</i> based which means that if more messages received
278 * it will match also.
279 *
280 * @param number at least number of messages
281 * @return the builder
282 */
283 public NotifyBuilder whenFailed(final int number) {
284 stack.add(new EventPredicateSupport() {
285 private int current;
286
287 @Override
288 public boolean onExchangeFailure(Exchange exchange) {
289 current++;
290 return true;
291 }
292
293 public boolean matches() {
294 return current >= number;
295 }
296
297 @Override
298 public String toString() {
299 return "whenFailed(" + number + ")";
300 }
301 });
302 return this;
303 }
304
305 /**
306 * Sets a condition when <tt>number</tt> of {@link Exchange} is done being processed.
307 * <p/>
308 * messages, where as completed is only successful processed messages.
309 *
310 * @param number exactly number of messages
311 * @return the builder
312 */
313 public NotifyBuilder whenExactlyDone(final int number) {
314 stack.add(new EventPredicateSupport() {
315 private int current;
316
317 @Override
318 public boolean onExchangeCompleted(Exchange exchange) {
319 current++;
320 return true;
321 }
322
323 @Override
324 public boolean onExchangeFailure(Exchange exchange) {
325 current++;
326 return true;
327 }
328
329 public boolean matches() {
330 return current == number;
331 }
332
333 @Override
334 public String toString() {
335 return "whenExactlyDone(" + number + ")";
336 }
337 });
338 return this;
339 }
340
341 /**
342 * Sets a condition when <tt>number</tt> of {@link Exchange} has been completed.
343 * <p/>
344 * The difference between <i>done</i> and <i>completed</i> is that done can also include failed
345 * messages, where as completed is only successful processed messages.
346 *
347 * @param number exactly number of messages
348 * @return the builder
349 */
350 public NotifyBuilder whenExactlyCompleted(final int number) {
351 stack.add(new EventPredicateSupport() {
352 private int current;
353
354 @Override
355 public boolean onExchangeCompleted(Exchange exchange) {
356 current++;
357 return true;
358 }
359
360 public boolean matches() {
361 return current == number;
362 }
363
364 @Override
365 public String toString() {
366 return "whenExactlyCompleted(" + number + ")";
367 }
368 });
369 return this;
370 }
371
372 /**
373 * Sets a condition when <tt>number</tt> of {@link Exchange} has failed.
374 *
375 * @param number exactly number of messages
376 * @return the builder
377 */
378 public NotifyBuilder whenExactlyFailed(final int number) {
379 stack.add(new EventPredicateSupport() {
380 private int current;
381
382 @Override
383 public boolean onExchangeFailure(Exchange exchange) {
384 current++;
385 return true;
386 }
387
388 public boolean matches() {
389 return current == number;
390 }
391
392 @Override
393 public String toString() {
394 return "whenExactlyFailed(" + number + ")";
395 }
396 });
397 return this;
398 }
399
400 /**
401 * Sets a condition that <b>any received</b> {@link Exchange} should match the {@link Predicate}
402 *
403 * @param predicate the predicate
404 * @return the builder
405 */
406 public NotifyBuilder whenAnyReceivedMatches(final Predicate predicate) {
407 return doWhenAnyMatches(predicate, true);
408 }
409
410 /**
411 * Sets a condition that <b>any done</b> {@link Exchange} should match the {@link Predicate}
412 *
413 * @param predicate the predicate
414 * @return the builder
415 */
416 public NotifyBuilder whenAnyDoneMatches(final Predicate predicate) {
417 return doWhenAnyMatches(predicate, false);
418 }
419
420 private NotifyBuilder doWhenAnyMatches(final Predicate predicate, final boolean received) {
421 stack.push(new EventPredicateSupport() {
422 private boolean matches;
423
424 @Override
425 public boolean onExchangeCompleted(Exchange exchange) {
426 if (!received && !matches) {
427 matches = predicate.matches(exchange);
428 }
429 return true;
430 }
431
432 @Override
433 public boolean onExchangeFailure(Exchange exchange) {
434 if (!received && !matches) {
435 matches = predicate.matches(exchange);
436 }
437 return true;
438 }
439
440 @Override
441 public boolean onExchangeCreated(Exchange exchange) {
442 if (received && !matches) {
443 matches = predicate.matches(exchange);
444 }
445 return true;
446 }
447
448 public boolean matches() {
449 return matches;
450 }
451
452 @Override
453 public String toString() {
454 if (received) {
455 return "whenAnyReceivedMatches(" + predicate + ")";
456 } else {
457 return "whenAnyDoneMatches(" + predicate + ")";
458 }
459 }
460 });
461 return this;
462 }
463
464 /**
465 * Sets a condition that <b>all received</b> {@link Exchange} should match the {@link Predicate}
466 *
467 * @param predicate the predicate
468 * @return the builder
469 */
470 public NotifyBuilder whenAllReceivedMatches(final Predicate predicate) {
471 return doWhenAllMatches(predicate, true);
472 }
473
474 /**
475 * Sets a condition that <b>all done</b> {@link Exchange} should match the {@link Predicate}
476 *
477 * @param predicate the predicate
478 * @return the builder
479 */
480 public NotifyBuilder whenAllDoneMatches(final Predicate predicate) {
481 return doWhenAllMatches(predicate, false);
482 }
483
484 private NotifyBuilder doWhenAllMatches(final Predicate predicate, final boolean received) {
485 stack.push(new EventPredicateSupport() {
486 private boolean matches = true;
487
488 @Override
489 public boolean onExchangeCompleted(Exchange exchange) {
490 if (!received && matches) {
491 matches = predicate.matches(exchange);
492 }
493 return true;
494 }
495
496 @Override
497 public boolean onExchangeFailure(Exchange exchange) {
498 if (!received && matches) {
499 matches = predicate.matches(exchange);
500 }
501 return true;
502 }
503
504 @Override
505 public boolean onExchangeCreated(Exchange exchange) {
506 if (received && matches) {
507 matches = predicate.matches(exchange);
508 }
509 return true;
510 }
511
512 public boolean matches() {
513 return matches;
514 }
515
516 @Override
517 public String toString() {
518 if (received) {
519 return "whenAllReceivedMatches(" + predicate + ")";
520 } else {
521 return "whenAllDoneMatches(" + predicate + ")";
522 }
523 }
524 });
525 return this;
526 }
527
528 /**
529 * Sets a condition when the provided mock is satisfied based on {@link Exchange}
530 * being sent to it when they are <b>done</b>.
531 * <p/>
532 * The idea is that you can use Mock for setting fine grained expectations
533 * and then use that together with this builder. The mock provided does <b>NOT</b>
534 * have to already exist in the route. You can just create a new pseudo mock
535 * and this builder will send the done {@link Exchange} to it. So its like
536 * adding the mock to the end of your route(s).
537 *
538 * @param mock the mock
539 * @return the builder
540 */
541 public NotifyBuilder whenDoneSatisfied(final MockEndpoint mock) {
542 return doWhenSatisfied(mock, false);
543 }
544
545 /**
546 * Sets a condition when the provided mock is satisfied based on {@link Exchange}
547 * being sent to it when they are <b>received</b>.
548 * <p/>
549 * The idea is that you can use Mock for setting fine grained expectations
550 * and then use that together with this builder. The mock provided does <b>NOT</b>
551 * have to already exist in the route. You can just create a new pseudo mock
552 * and this builder will send the done {@link Exchange} to it. So its like
553 * adding the mock to the end of your route(s).
554 *
555 * @param mock the mock
556 * @return the builder
557 */
558 public NotifyBuilder whenReceivedSatisfied(final MockEndpoint mock) {
559 return doWhenSatisfied(mock, true);
560 }
561
562 private NotifyBuilder doWhenSatisfied(final MockEndpoint mock, final boolean received) {
563 stack.push(new EventPredicateSupport() {
564
565 private Producer producer;
566
567 @Override
568 public boolean onExchangeCreated(Exchange exchange) {
569 if (received) {
570 sendToMock(exchange);
571 }
572 return true;
573 }
574
575 @Override
576 public boolean onExchangeFailure(Exchange exchange) {
577 if (!received) {
578 sendToMock(exchange);
579 }
580 return true;
581 }
582
583 @Override
584 public boolean onExchangeCompleted(Exchange exchange) {
585 if (!received) {
586 sendToMock(exchange);
587 }
588 return true;
589 }
590
591 private void sendToMock(Exchange exchange) {
592 // send the exchange when its completed to the mock
593 try {
594 if (producer == null) {
595 producer = mock.createProducer();
596 }
597 producer.process(exchange);
598 } catch (Exception e) {
599 throw ObjectHelper.wrapRuntimeCamelException(e);
600 }
601 }
602
603 public boolean matches() {
604 try {
605 return mock.await(0, TimeUnit.SECONDS);
606 } catch (InterruptedException e) {
607 throw ObjectHelper.wrapRuntimeCamelException(e);
608 }
609 }
610
611 @Override
612 public String toString() {
613 if (received) {
614 return "whenReceivedSatisfied(" + mock + ")";
615 } else {
616 return "whenDoneSatisfied(" + mock + ")";
617 }
618 }
619 });
620 return this;
621 }
622
623 /**
624 * Sets a condition when the provided mock is <b>not</b> satisfied based on {@link Exchange}
625 * being sent to it when they are <b>received</b>.
626 * <p/>
627 * The idea is that you can use Mock for setting fine grained expectations
628 * and then use that together with this builder. The mock provided does <b>NOT</b>
629 * have to already exist in the route. You can just create a new pseudo mock
630 * and this builder will send the done {@link Exchange} to it. So its like
631 * adding the mock to the end of your route(s).
632 *
633 * @param mock the mock
634 * @return the builder
635 */
636 public NotifyBuilder whenReceivedNotSatisfied(final MockEndpoint mock) {
637 return doWhenNotSatisfied(mock, true);
638 }
639
640 /**
641 * Sets a condition when the provided mock is <b>not</b> satisfied based on {@link Exchange}
642 * being sent to it when they are <b>done</b>.
643 * <p/>
644 * The idea is that you can use Mock for setting fine grained expectations
645 * and then use that together with this builder. The mock provided does <b>NOT</b>
646 * have to already exist in the route. You can just create a new pseudo mock
647 * and this builder will send the done {@link Exchange} to it. So its like
648 * adding the mock to the end of your route(s).
649 *
650 * @param mock the mock
651 * @return the builder
652 */
653 public NotifyBuilder whenDoneNotSatisfied(final MockEndpoint mock) {
654 return doWhenNotSatisfied(mock, false);
655 }
656
657 private NotifyBuilder doWhenNotSatisfied(final MockEndpoint mock, final boolean received) {
658 stack.push(new EventPredicateSupport() {
659
660 private Producer producer;
661
662 @Override
663 public boolean onExchangeCreated(Exchange exchange) {
664 if (received) {
665 sendToMock(exchange);
666 }
667 return true;
668 }
669
670 @Override
671 public boolean onExchangeFailure(Exchange exchange) {
672 if (!received) {
673 sendToMock(exchange);
674 }
675 return true;
676 }
677
678 @Override
679 public boolean onExchangeCompleted(Exchange exchange) {
680 if (!received) {
681 sendToMock(exchange);
682 }
683 return true;
684 }
685
686 private void sendToMock(Exchange exchange) {
687 // send the exchange when its completed to the mock
688 try {
689 if (producer == null) {
690 producer = mock.createProducer();
691 }
692 producer.process(exchange);
693 } catch (Exception e) {
694 throw ObjectHelper.wrapRuntimeCamelException(e);
695 }
696 }
697
698 public boolean matches() {
699 try {
700 return !mock.await(0, TimeUnit.SECONDS);
701 } catch (InterruptedException e) {
702 throw ObjectHelper.wrapRuntimeCamelException(e);
703 }
704 }
705
706 @Override
707 public String toString() {
708 if (received) {
709 return "whenReceivedNotSatisfied(" + mock + ")";
710 } else {
711 return "whenDoneNotSatisfied(" + mock + ")";
712 }
713 }
714 });
715 return this;
716 }
717
718 /**
719 * Sets a condition that the bodies is expected to be <b>received</b> in the order as well.
720 * <p/>
721 * This condition will discard any additional messages. If you need a more strict condition
722 * then use {@link #whenExactBodiesReceived(Object...)}
723 *
724 * @param bodies the expected bodies
725 * @return the builder
726 * @see #whenExactBodiesReceived(Object...)
727 */
728 public NotifyBuilder whenBodiesReceived(Object... bodies) {
729 List<Object> bodyList = new ArrayList<Object>();
730 bodyList.addAll(Arrays.asList(bodies));
731 return doWhenBodies(bodyList, true, false);
732 }
733
734 /**
735 * Sets a condition that the bodies is expected to be <b>done</b> in the order as well.
736 * <p/>
737 * This condition will discard any additional messages. If you need a more strict condition
738 * then use {@link #whenExactBodiesDone(Object...)}
739 *
740 * @param bodies the expected bodies
741 * @return the builder
742 * @see #whenExactBodiesDone(Object...)
743 */
744 public NotifyBuilder whenBodiesDone(Object... bodies) {
745 List<Object> bodyList = new ArrayList<Object>();
746 bodyList.addAll(Arrays.asList(bodies));
747 return doWhenBodies(bodyList, false, false);
748 }
749
750 /**
751 * Sets a condition that the bodies is expected to be <b>received</b> in the order as well.
752 * <p/>
753 * This condition is strict which means that it only expect that exact number of bodies
754 *
755 * @param bodies the expected bodies
756 * @return the builder
757 * @see #whenBodiesReceived(Object...)
758 */
759 public NotifyBuilder whenExactBodiesReceived(Object... bodies) {
760 List<Object> bodyList = new ArrayList<Object>();
761 bodyList.addAll(Arrays.asList(bodies));
762 return doWhenBodies(bodyList, true, true);
763 }
764
765 /**
766 * Sets a condition that the bodies is expected to be <b>done</b> in the order as well.
767 * <p/>
768 * This condition is strict which means that it only expect that exact number of bodies
769 *
770 * @param bodies the expected bodies
771 * @return the builder
772 * @see #whenExactBodiesDone(Object...)
773 */
774 public NotifyBuilder whenExactBodiesDone(Object... bodies) {
775 List<Object> bodyList = new ArrayList<Object>();
776 bodyList.addAll(Arrays.asList(bodies));
777 return doWhenBodies(bodyList, false, true);
778 }
779
780 private NotifyBuilder doWhenBodies(final List bodies, final boolean received, final boolean exact) {
781 stack.push(new EventPredicateSupport() {
782 private boolean matches;
783 private int current;
784
785 @Override
786 public boolean onExchangeCreated(Exchange exchange) {
787 if (received) {
788 matchBody(exchange);
789 }
790 return true;
791 }
792
793 @Override
794 public boolean onExchangeFailure(Exchange exchange) {
795 if (!received) {
796 matchBody(exchange);
797 }
798 return true;
799 }
800
801 @Override
802 public boolean onExchangeCompleted(Exchange exchange) {
803 if (!received) {
804 matchBody(exchange);
805 }
806 return true;
807 }
808
809 private void matchBody(Exchange exchange) {
810 current++;
811
812 if (current > bodies.size()) {
813 // out of bounds
814 return;
815 }
816
817 Object actual = exchange.getIn().getBody();
818 Object expected = bodies.get(current - 1);
819 matches = ObjectHelper.equal(expected, actual);
820 }
821
822 public boolean matches() {
823 if (exact) {
824 return matches && current == bodies.size();
825 } else {
826 return matches && current >= bodies.size();
827 }
828 }
829
830 @Override
831 public String toString() {
832 if (received) {
833 return "" + (exact ? "whenExactBodiesReceived(" : "whenBodiesReceived(") + bodies + ")";
834 } else {
835 return "" + (exact ? "whenExactBodiesDone(" : "whenBodiesDone(") + bodies + ")";
836 }
837 }
838 });
839 return this;
840 }
841
842
843 /**
844 * Prepares to append an additional expression using the <i>and</i> operator.
845 *
846 * @return the builder
847 */
848 public NotifyBuilder and() {
849 doCreate(EventOperation.and);
850 return this;
851 }
852
853 /**
854 * Prepares to append an additional expression using the <i>or</i> operator.
855 *
856 * @return the builder
857 */
858 public NotifyBuilder or() {
859 doCreate(EventOperation.or);
860 return this;
861 }
862
863 /**
864 * Prepares to append an additional expression using the <i>not</i> operator.
865 *
866 * @return the builder
867 */
868 public NotifyBuilder not() {
869 doCreate(EventOperation.not);
870 return this;
871 }
872
873 /**
874 * Creates the expression this builder should use for matching.
875 * <p/>
876 * You must call this method when you are finished building the expressions.
877 *
878 * @return the created builder ready for matching
879 */
880 public NotifyBuilder create() {
881 doCreate(EventOperation.and);
882 return this;
883 }
884
885 /**
886 * Does all the expression match?
887 * <p/>
888 * This operation will return immediately which means it can be used for testing at this very moment.
889 *
890 * @return <tt>true</tt> if matching, <tt>false</tt> otherwise
891 */
892 public boolean matches() {
893 return matches;
894 }
895
896 /**
897 * Does all the expression match?
898 * <p/>
899 * This operation will wait until the match is <tt>true</tt> or otherwise a timeout occur
900 * which means <tt>false</tt> will be returned.
901 *
902 * @param timeout the timeout value
903 * @param timeUnit the time unit
904 * @return <tt>true</tt> if matching, <tt>false</tt> otherwise due to timeout
905 */
906 public boolean matches(long timeout, TimeUnit timeUnit) {
907 try {
908 latch.await(timeout, timeUnit);
909 } catch (InterruptedException e) {
910 throw ObjectHelper.wrapRuntimeCamelException(e);
911 }
912 return matches();
913 }
914
915 @Override
916 public String toString() {
917 StringBuilder sb = new StringBuilder();
918 for (Iterator<EventPredicateHolder> it = predicates.iterator(); it.hasNext();) {
919 if (sb.length() > 0) {
920 sb.append(".");
921 }
922 sb.append(it.next().toString());
923 }
924 // a crude way of skipping the first invisible operation
925 return ObjectHelper.after(sb.toString(), "().");
926 }
927
928 private void doCreate(EventOperation newOperation) {
929 // init operation depending on the newOperation
930 if (operation == null) {
931 // if the first new operation is an or then this operation must be an or as well
932 // otherwise it should be and based
933 operation = newOperation == EventOperation.or ? EventOperation.or : EventOperation.and;
934 }
935
936 // we have some
937 if (!stack.isEmpty()) {
938 CompoundEventPredicate compound = new CompoundEventPredicate(stack);
939 stack.clear();
940 predicates.add(new EventPredicateHolder(operation, compound));
941 }
942
943 operation = newOperation;
944 }
945
946 /**
947 * Notifier which hooks into Camel to listen for {@link Exchange} relevant events for this builder
948 */
949 private final class ExchangeNotifier extends EventNotifierSupport {
950
951 public void notify(EventObject event) throws Exception {
952 if (event instanceof ExchangeCreatedEvent) {
953 onExchangeCreated((ExchangeCreatedEvent) event);
954 } else if (event instanceof ExchangeCompletedEvent) {
955 onExchangeCompleted((ExchangeCompletedEvent) event);
956 } else if (event instanceof ExchangeFailureEvent) {
957 onExchangeFailure((ExchangeFailureEvent) event);
958 }
959
960 // now compute whether we matched
961 computeMatches();
962 }
963
964 public boolean isEnabled(EventObject event) {
965 return true;
966 }
967
968 private void onExchangeCreated(ExchangeCreatedEvent event) {
969 for (EventPredicateHolder predicate : predicates) {
970 predicate.getPredicate().onExchangeCreated(event.getExchange());
971 }
972 }
973
974 private void onExchangeCompleted(ExchangeCompletedEvent event) {
975 for (EventPredicateHolder predicate : predicates) {
976 predicate.getPredicate().onExchangeCompleted(event.getExchange());
977 }
978 }
979
980 private void onExchangeFailure(ExchangeFailureEvent event) {
981 for (EventPredicateHolder predicate : predicates) {
982 predicate.getPredicate().onExchangeFailure(event.getExchange());
983 }
984 }
985
986 private synchronized void computeMatches() {
987 // use a temporary answer until we have computed the value to assign
988 Boolean answer = null;
989
990 for (EventPredicateHolder holder : predicates) {
991 EventOperation operation = holder.getOperation();
992 if (EventOperation.and == operation) {
993 if (holder.getPredicate().matches()) {
994 answer = true;
995 } else {
996 answer = false;
997 // and break out since its an AND so it must match
998 break;
999 }
1000 } else if (EventOperation.or == operation) {
1001 if (holder.getPredicate().matches()) {
1002 answer = true;
1003 }
1004 } else if (EventOperation.not == operation) {
1005 if (holder.getPredicate().matches()) {
1006 answer = false;
1007 // and break out since its a NOT so it must not match
1008 break;
1009 } else {
1010 answer = true;
1011 }
1012 }
1013 }
1014
1015 // if we did compute a value then assign that
1016 if (answer != null) {
1017 matches = answer;
1018 if (matches) {
1019 // signal completion
1020 latch.countDown();
1021 }
1022 }
1023 }
1024
1025 @Override
1026 protected void doStart() throws Exception {
1027 // we only care about Exchange events
1028 setIgnoreCamelContextEvents(true);
1029 setIgnoreRouteEvents(true);
1030 setIgnoreServiceEvents(true);
1031 }
1032
1033 @Override
1034 protected void doStop() throws Exception {
1035 }
1036 }
1037
1038 private enum EventOperation {
1039 and, or, not;
1040 }
1041
1042 private interface EventPredicate {
1043
1044 /**
1045 * Evaluates whether the predicate matched or not.
1046 *
1047 * @return <tt>true</tt> if matched, <tt>false</tt> otherwise
1048 */
1049 boolean matches();
1050
1051 /**
1052 * Callback for {@link Exchange} lifecycle
1053 *
1054 * @param exchange the exchange
1055 * @return <tt>true</tt> to allow continue evaluating, <tt>false</tt> to stop immediately
1056 */
1057 boolean onExchangeCreated(Exchange exchange);
1058
1059 /**
1060 * Callback for {@link Exchange} lifecycle
1061 *
1062 * @param exchange the exchange
1063 * @return <tt>true</tt> to allow continue evaluating, <tt>false</tt> to stop immediately
1064 */
1065 boolean onExchangeCompleted(Exchange exchange);
1066
1067 /**
1068 * Callback for {@link Exchange} lifecycle
1069 *
1070 * @param exchange the exchange
1071 * @return <tt>true</tt> to allow continue evaluating, <tt>false</tt> to stop immediately
1072 */
1073 boolean onExchangeFailure(Exchange exchange);
1074 }
1075
1076 private abstract class EventPredicateSupport implements EventPredicate {
1077
1078 public boolean onExchangeCreated(Exchange exchange) {
1079 return onExchange(exchange);
1080 }
1081
1082 public boolean onExchangeCompleted(Exchange exchange) {
1083 return onExchange(exchange);
1084 }
1085
1086 public boolean onExchangeFailure(Exchange exchange) {
1087 return onExchange(exchange);
1088 }
1089
1090 public boolean onExchange(Exchange exchange) {
1091 return true;
1092 }
1093 }
1094
1095 /**
1096 * To hold an operation and predicate
1097 */
1098 private final class EventPredicateHolder {
1099 private final EventOperation operation;
1100 private final EventPredicate predicate;
1101
1102 private EventPredicateHolder(EventOperation operation, EventPredicate predicate) {
1103 this.operation = operation;
1104 this.predicate = predicate;
1105 }
1106
1107 public EventOperation getOperation() {
1108 return operation;
1109 }
1110
1111 public EventPredicate getPredicate() {
1112 return predicate;
1113 }
1114
1115 @Override
1116 public String toString() {
1117 return operation.name() + "()." + predicate;
1118 }
1119 }
1120
1121 /**
1122 * To hold multiple predicates which are part of same expression
1123 */
1124 private final class CompoundEventPredicate implements EventPredicate {
1125
1126 private List<EventPredicate> predicates = new ArrayList<EventPredicate>();
1127
1128 private CompoundEventPredicate(Stack<EventPredicate> predicates) {
1129 this.predicates.addAll(predicates);
1130 }
1131
1132 public boolean matches() {
1133 for (EventPredicate predicate : predicates) {
1134 if (!predicate.matches()) {
1135 return false;
1136 }
1137 }
1138 return true;
1139 }
1140
1141 public boolean onExchangeCreated(Exchange exchange) {
1142 for (EventPredicate predicate : predicates) {
1143 if (!predicate.onExchangeCreated(exchange)) {
1144 return false;
1145 }
1146 }
1147 return true;
1148 }
1149
1150 public boolean onExchangeCompleted(Exchange exchange) {
1151 for (EventPredicate predicate : predicates) {
1152 if (!predicate.onExchangeCompleted(exchange)) {
1153 return false;
1154 }
1155 }
1156 return true;
1157 }
1158
1159 public boolean onExchangeFailure(Exchange exchange) {
1160 for (EventPredicate predicate : predicates) {
1161 if (!predicate.onExchangeFailure(exchange)) {
1162 return false;
1163 }
1164 }
1165 return true;
1166 }
1167
1168 @Override
1169 public String toString() {
1170 StringBuilder sb = new StringBuilder();
1171 for (Iterator<EventPredicate> it = predicates.iterator(); it.hasNext();) {
1172 if (sb.length() > 0) {
1173 sb.append(".");
1174 }
1175 sb.append(it.next().toString());
1176 }
1177 return sb.toString();
1178 }
1179 }
1180
1181 }