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.model;
018
019 import java.util.ArrayList;
020 import java.util.List;
021 import java.util.concurrent.ExecutorService;
022 import java.util.concurrent.ScheduledExecutorService;
023
024 import javax.xml.bind.annotation.XmlAccessType;
025 import javax.xml.bind.annotation.XmlAccessorType;
026 import javax.xml.bind.annotation.XmlAttribute;
027 import javax.xml.bind.annotation.XmlElement;
028 import javax.xml.bind.annotation.XmlElementRef;
029 import javax.xml.bind.annotation.XmlRootElement;
030 import javax.xml.bind.annotation.XmlTransient;
031
032 import org.apache.camel.Expression;
033 import org.apache.camel.Predicate;
034 import org.apache.camel.Processor;
035 import org.apache.camel.builder.ExpressionClause;
036 import org.apache.camel.model.language.ExpressionDefinition;
037 import org.apache.camel.processor.UnitOfWorkProcessor;
038 import org.apache.camel.processor.aggregate.AggregateProcessor;
039 import org.apache.camel.processor.aggregate.AggregationStrategy;
040 import org.apache.camel.processor.aggregate.GroupedExchangeAggregationStrategy;
041 import org.apache.camel.spi.AggregationRepository;
042 import org.apache.camel.spi.RouteContext;
043 import org.apache.camel.util.concurrent.SynchronousExecutorService;
044
045 /**
046 * Represents an XML <aggregate/> element
047 *
048 * @version
049 */
050 @XmlRootElement(name = "aggregate")
051 @XmlAccessorType(XmlAccessType.FIELD)
052 public class AggregateDefinition extends ProcessorDefinition<AggregateDefinition> implements ExecutorServiceAwareDefinition<AggregateDefinition> {
053 @XmlElement(name = "correlationExpression", required = true)
054 private ExpressionSubElementDefinition correlationExpression;
055 @XmlElement(name = "completionPredicate")
056 private ExpressionSubElementDefinition completionPredicate;
057 @XmlElement(name = "completionTimeout")
058 private ExpressionSubElementDefinition completionTimeoutExpression;
059 @XmlElement(name = "completionSize")
060 private ExpressionSubElementDefinition completionSizeExpression;
061 @XmlTransient
062 private ExpressionDefinition expression;
063 @XmlElementRef
064 private List<ProcessorDefinition<?>> outputs = new ArrayList<ProcessorDefinition<?>>();
065 @XmlTransient
066 private AggregationStrategy aggregationStrategy;
067 @XmlTransient
068 private ExecutorService executorService;
069 @XmlTransient
070 private ScheduledExecutorService timeoutCheckerExecutorService;
071 @XmlTransient
072 private AggregationRepository aggregationRepository;
073 @XmlAttribute
074 private Boolean parallelProcessing;
075 @XmlAttribute
076 private String executorServiceRef;
077 @XmlAttribute
078 private String timeoutCheckerExecutorServiceRef;
079 @XmlAttribute
080 private String aggregationRepositoryRef;
081 @XmlAttribute
082 private String strategyRef;
083 @XmlAttribute
084 private Integer completionSize;
085 @XmlAttribute
086 private Long completionInterval;
087 @XmlAttribute
088 private Long completionTimeout;
089 @XmlAttribute
090 private Boolean completionFromBatchConsumer;
091 @XmlAttribute
092 private Boolean groupExchanges;
093 @XmlAttribute
094 private Boolean eagerCheckCompletion;
095 @XmlAttribute
096 private Boolean ignoreInvalidCorrelationKeys;
097 @XmlAttribute
098 private Integer closeCorrelationKeyOnCompletion;
099 @XmlAttribute
100 private Boolean discardOnCompletionTimeout;
101 @XmlAttribute
102 private Boolean forceCompletionOnStop;
103
104 public AggregateDefinition() {
105 }
106
107 public AggregateDefinition(Predicate predicate) {
108 if (predicate != null) {
109 setExpression(new ExpressionDefinition(predicate));
110 }
111 }
112
113 public AggregateDefinition(Expression correlationExpression) {
114 if (correlationExpression != null) {
115 setExpression(new ExpressionDefinition(correlationExpression));
116 }
117 }
118
119 public AggregateDefinition(ExpressionDefinition correlationExpression) {
120 this.expression = correlationExpression;
121 }
122
123 public AggregateDefinition(Expression correlationExpression, AggregationStrategy aggregationStrategy) {
124 this(correlationExpression);
125 this.aggregationStrategy = aggregationStrategy;
126 }
127
128 @Override
129 public String toString() {
130 return "Aggregate[" + description() + " -> " + getOutputs() + "]";
131 }
132
133 protected String description() {
134 return getExpression() != null ? getExpression().getLabel() : "";
135 }
136
137 @Override
138 public String getShortName() {
139 return "aggregate";
140 }
141
142 @Override
143 public String getLabel() {
144 return "aggregate[" + description() + "]";
145 }
146
147 @Override
148 public Processor createProcessor(RouteContext routeContext) throws Exception {
149 return createAggregator(routeContext);
150 }
151
152 protected AggregateProcessor createAggregator(RouteContext routeContext) throws Exception {
153 Processor processor = this.createChildProcessor(routeContext, true);
154 // wrap the aggregated route in a unit of work processor
155 processor = new UnitOfWorkProcessor(routeContext, processor);
156
157 Expression correlation = getExpression().createExpression(routeContext);
158 AggregationStrategy strategy = createAggregationStrategy(routeContext);
159
160 boolean shutdownThreadPool = ProcessorDefinitionHelper.willCreateNewThreadPool(routeContext, this, isParallelProcessing());
161 ExecutorService threadPool = ProcessorDefinitionHelper.getConfiguredExecutorService(routeContext, "Aggregator", this, isParallelProcessing());
162 if (threadPool == null && !isParallelProcessing()) {
163 // executor service is mandatory for the Aggregator
164 // we do not run in parallel mode, but use a synchronous executor, so we run in current thread
165 threadPool = new SynchronousExecutorService();
166 shutdownThreadPool = true;
167 }
168
169 AggregateProcessor answer = new AggregateProcessor(routeContext.getCamelContext(), processor,
170 correlation, strategy, threadPool, shutdownThreadPool);
171
172 AggregationRepository repository = createAggregationRepository(routeContext);
173 if (repository != null) {
174 answer.setAggregationRepository(repository);
175 }
176
177 // this EIP supports using a shared timeout checker thread pool or fallback to create a new thread pool
178 boolean shutdownTimeoutThreadPool = false;
179 ScheduledExecutorService timeoutThreadPool = timeoutCheckerExecutorService;
180 if (timeoutThreadPool == null && timeoutCheckerExecutorServiceRef != null) {
181 // lookup existing thread pool
182 timeoutThreadPool = routeContext.getCamelContext().getRegistry().lookup(timeoutCheckerExecutorServiceRef, ScheduledExecutorService.class);
183 if (timeoutThreadPool == null) {
184 // then create a thread pool assuming the ref is a thread pool profile id
185 timeoutThreadPool = routeContext.getCamelContext().getExecutorServiceManager().newScheduledThreadPool(this,
186 AggregateProcessor.AGGREGATE_TIMEOUT_CHECKER, timeoutCheckerExecutorServiceRef);
187 if (timeoutThreadPool == null) {
188 throw new IllegalArgumentException("ExecutorServiceRef " + timeoutCheckerExecutorServiceRef + " not found in registry or as a thread pool profile.");
189 }
190 shutdownTimeoutThreadPool = true;
191 }
192 }
193 answer.setTimeoutCheckerExecutorService(timeoutThreadPool);
194 answer.setShutdownTimeoutCheckerExecutorService(shutdownTimeoutThreadPool);
195
196 // set other options
197 answer.setParallelProcessing(isParallelProcessing());
198 if (getCompletionPredicate() != null) {
199 Predicate predicate = getCompletionPredicate().createPredicate(routeContext);
200 answer.setCompletionPredicate(predicate);
201 }
202 if (getCompletionTimeoutExpression() != null) {
203 Expression expression = getCompletionTimeoutExpression().createExpression(routeContext);
204 answer.setCompletionTimeoutExpression(expression);
205 }
206 if (getCompletionTimeout() != null) {
207 answer.setCompletionTimeout(getCompletionTimeout());
208 }
209 if (getCompletionInterval() != null) {
210 answer.setCompletionInterval(getCompletionInterval());
211 }
212 if (getCompletionSizeExpression() != null) {
213 Expression expression = getCompletionSizeExpression().createExpression(routeContext);
214 answer.setCompletionSizeExpression(expression);
215 }
216 if (getCompletionSize() != null) {
217 answer.setCompletionSize(getCompletionSize());
218 }
219 if (getCompletionFromBatchConsumer() != null) {
220 answer.setCompletionFromBatchConsumer(isCompletionFromBatchConsumer());
221 }
222 if (getEagerCheckCompletion() != null) {
223 answer.setEagerCheckCompletion(isEagerCheckCompletion());
224 }
225 if (getIgnoreInvalidCorrelationKeys() != null) {
226 answer.setIgnoreInvalidCorrelationKeys(isIgnoreInvalidCorrelationKeys());
227 }
228 if (getCloseCorrelationKeyOnCompletion() != null) {
229 answer.setCloseCorrelationKeyOnCompletion(getCloseCorrelationKeyOnCompletion());
230 }
231 if (getDiscardOnCompletionTimeout() != null) {
232 answer.setDiscardOnCompletionTimeout(isDiscardOnCompletionTimeout());
233 }
234 if (getForceCompletionOnStop() != null) {
235 answer.setForceCompletionOnStop(getForceCompletionOnStop());
236 }
237
238 return answer;
239 }
240
241 @Override
242 protected void configureChild(ProcessorDefinition<?> output) {
243 if (expression != null && expression instanceof ExpressionClause) {
244 ExpressionClause<?> clause = (ExpressionClause<?>) expression;
245 if (clause.getExpressionType() != null) {
246 // if using the Java DSL then the expression may have been set using the
247 // ExpressionClause which is a fancy builder to define expressions and predicates
248 // using fluent builders in the DSL. However we need afterwards a callback to
249 // reset the expression to the expression type the ExpressionClause did build for us
250 expression = clause.getExpressionType();
251 // set the correlation expression from the expression type, as the model definition
252 // would then be accurate
253 correlationExpression = new ExpressionSubElementDefinition();
254 correlationExpression.setExpressionType(clause.getExpressionType());
255 }
256 }
257 }
258
259 private AggregationStrategy createAggregationStrategy(RouteContext routeContext) {
260 AggregationStrategy strategy = getAggregationStrategy();
261 if (strategy == null && strategyRef != null) {
262 strategy = routeContext.lookup(strategyRef, AggregationStrategy.class);
263 }
264
265 if (groupExchanges != null && groupExchanges) {
266 if (strategy != null || strategyRef != null) {
267 throw new IllegalArgumentException("Options groupExchanges and AggregationStrategy cannot be enabled at the same time");
268 }
269 if (eagerCheckCompletion != null && !eagerCheckCompletion) {
270 throw new IllegalArgumentException("Option eagerCheckCompletion cannot be false when groupExchanges has been enabled");
271 }
272 // set eager check to enabled by default when using grouped exchanges
273 setEagerCheckCompletion(true);
274 // if grouped exchange is enabled then use special strategy for that
275 strategy = new GroupedExchangeAggregationStrategy();
276 }
277
278 if (strategy == null) {
279 throw new IllegalArgumentException("AggregationStrategy or AggregationStrategyRef must be set on " + this);
280 }
281 return strategy;
282 }
283
284 private AggregationRepository createAggregationRepository(RouteContext routeContext) {
285 AggregationRepository repository = getAggregationRepository();
286 if (repository == null && aggregationRepositoryRef != null) {
287 repository = routeContext.lookup(aggregationRepositoryRef, AggregationRepository.class);
288 if (repository == null) {
289 throw new IllegalArgumentException("AggregationRepositoryRef " + aggregationRepositoryRef + " not found in registry.");
290 }
291 }
292 return repository;
293 }
294
295 public AggregationStrategy getAggregationStrategy() {
296 return aggregationStrategy;
297 }
298
299 public void setAggregationStrategy(AggregationStrategy aggregationStrategy) {
300 this.aggregationStrategy = aggregationStrategy;
301 }
302
303 public String getAggregationStrategyRef() {
304 return strategyRef;
305 }
306
307 public void setAggregationStrategyRef(String aggregationStrategyRef) {
308 this.strategyRef = aggregationStrategyRef;
309 }
310
311 public Integer getCompletionSize() {
312 return completionSize;
313 }
314
315 public void setCompletionSize(Integer completionSize) {
316 this.completionSize = completionSize;
317 }
318
319 public Long getCompletionInterval() {
320 return completionInterval;
321 }
322
323 public void setCompletionInterval(Long completionInterval) {
324 this.completionInterval = completionInterval;
325 }
326
327 public Long getCompletionTimeout() {
328 return completionTimeout;
329 }
330
331 public void setCompletionTimeout(Long completionTimeout) {
332 this.completionTimeout = completionTimeout;
333 }
334
335 public ExpressionSubElementDefinition getCompletionPredicate() {
336 return completionPredicate;
337 }
338
339 public void setCompletionPredicate(ExpressionSubElementDefinition completionPredicate) {
340 this.completionPredicate = completionPredicate;
341 }
342
343 public ExpressionSubElementDefinition getCompletionTimeoutExpression() {
344 return completionTimeoutExpression;
345 }
346
347 public void setCompletionTimeoutExpression(ExpressionSubElementDefinition completionTimeoutExpression) {
348 this.completionTimeoutExpression = completionTimeoutExpression;
349 }
350
351 public ExpressionSubElementDefinition getCompletionSizeExpression() {
352 return completionSizeExpression;
353 }
354
355 public void setCompletionSizeExpression(ExpressionSubElementDefinition completionSizeExpression) {
356 this.completionSizeExpression = completionSizeExpression;
357 }
358
359 public Boolean getGroupExchanges() {
360 return groupExchanges;
361 }
362
363 public boolean isGroupExchanges() {
364 return groupExchanges != null && groupExchanges;
365 }
366
367 public void setGroupExchanges(Boolean groupExchanges) {
368 this.groupExchanges = groupExchanges;
369 }
370
371 public Boolean getCompletionFromBatchConsumer() {
372 return completionFromBatchConsumer;
373 }
374
375 public boolean isCompletionFromBatchConsumer() {
376 return completionFromBatchConsumer != null && completionFromBatchConsumer;
377 }
378
379 public void setCompletionFromBatchConsumer(Boolean completionFromBatchConsumer) {
380 this.completionFromBatchConsumer = completionFromBatchConsumer;
381 }
382
383 public ExecutorService getExecutorService() {
384 return executorService;
385 }
386
387 public void setExecutorService(ExecutorService executorService) {
388 this.executorService = executorService;
389 }
390
391 public Boolean getParallelProcessing() {
392 return parallelProcessing;
393 }
394
395 public boolean isParallelProcessing() {
396 return parallelProcessing != null && parallelProcessing;
397 }
398
399 public void setParallelProcessing(boolean parallelProcessing) {
400 this.parallelProcessing = parallelProcessing;
401 }
402
403 public String getExecutorServiceRef() {
404 return executorServiceRef;
405 }
406
407 public void setExecutorServiceRef(String executorServiceRef) {
408 this.executorServiceRef = executorServiceRef;
409 }
410
411 public String getStrategyRef() {
412 return strategyRef;
413 }
414
415 public void setStrategyRef(String strategyRef) {
416 this.strategyRef = strategyRef;
417 }
418
419 public Boolean getEagerCheckCompletion() {
420 return eagerCheckCompletion;
421 }
422
423 public boolean isEagerCheckCompletion() {
424 return eagerCheckCompletion != null && eagerCheckCompletion;
425 }
426
427 public void setEagerCheckCompletion(Boolean eagerCheckCompletion) {
428 this.eagerCheckCompletion = eagerCheckCompletion;
429 }
430
431 public Boolean getIgnoreInvalidCorrelationKeys() {
432 return ignoreInvalidCorrelationKeys;
433 }
434
435 public boolean isIgnoreInvalidCorrelationKeys() {
436 return ignoreInvalidCorrelationKeys != null && ignoreInvalidCorrelationKeys;
437 }
438
439 public void setIgnoreInvalidCorrelationKeys(Boolean ignoreInvalidCorrelationKeys) {
440 this.ignoreInvalidCorrelationKeys = ignoreInvalidCorrelationKeys;
441 }
442
443 public Integer getCloseCorrelationKeyOnCompletion() {
444 return closeCorrelationKeyOnCompletion;
445 }
446
447 public void setCloseCorrelationKeyOnCompletion(Integer closeCorrelationKeyOnCompletion) {
448 this.closeCorrelationKeyOnCompletion = closeCorrelationKeyOnCompletion;
449 }
450
451 public AggregationRepository getAggregationRepository() {
452 return aggregationRepository;
453 }
454
455 public void setAggregationRepository(AggregationRepository aggregationRepository) {
456 this.aggregationRepository = aggregationRepository;
457 }
458
459 public String getAggregationRepositoryRef() {
460 return aggregationRepositoryRef;
461 }
462
463 public void setAggregationRepositoryRef(String aggregationRepositoryRef) {
464 this.aggregationRepositoryRef = aggregationRepositoryRef;
465 }
466
467 public Boolean getDiscardOnCompletionTimeout() {
468 return discardOnCompletionTimeout;
469 }
470
471 public boolean isDiscardOnCompletionTimeout() {
472 return discardOnCompletionTimeout != null && discardOnCompletionTimeout;
473 }
474
475 public void setDiscardOnCompletionTimeout(Boolean discardOnCompletionTimeout) {
476 this.discardOnCompletionTimeout = discardOnCompletionTimeout;
477 }
478
479 public void setTimeoutCheckerExecutorService(ScheduledExecutorService timeoutCheckerExecutorService) {
480 this.timeoutCheckerExecutorService = timeoutCheckerExecutorService;
481 }
482
483 public ScheduledExecutorService getTimeoutCheckerExecutorService() {
484 return timeoutCheckerExecutorService;
485 }
486
487 public void setTimeoutCheckerExecutorServiceRef(String timeoutCheckerExecutorServiceRef) {
488 this.timeoutCheckerExecutorServiceRef = timeoutCheckerExecutorServiceRef;
489 }
490
491 public String getTimeoutCheckerExecutorServiceRef() {
492 return timeoutCheckerExecutorServiceRef;
493 }
494
495 // Fluent API
496 //-------------------------------------------------------------------------
497
498 /**
499 * Use eager completion checking which means that the {{completionPredicate}} will use the incoming Exchange.
500 * At opposed to without eager completion checking the {{completionPredicate}} will use the aggregated Exchange.
501 *
502 * @return builder
503 */
504 public AggregateDefinition eagerCheckCompletion() {
505 setEagerCheckCompletion(true);
506 return this;
507 }
508
509 /**
510 * If a correlation key cannot be successfully evaluated it will be ignored by logging a {{DEBUG}} and then just
511 * ignore the incoming Exchange.
512 *
513 * @return builder
514 */
515 public AggregateDefinition ignoreInvalidCorrelationKeys() {
516 setIgnoreInvalidCorrelationKeys(true);
517 return this;
518 }
519
520 /**
521 * Closes a correlation key when its complete. Any <i>late</i> received exchanges which has a correlation key
522 * that has been closed, it will be defined and a {@link org.apache.camel.processor.aggregate.ClosedCorrelationKeyException}
523 * is thrown.
524 *
525 * @param capacity the maximum capacity of the closed correlation key cache.
526 * Use <tt>0</tt> or negative value for unbounded capacity.
527 * @return builder
528 */
529 public AggregateDefinition closeCorrelationKeyOnCompletion(int capacity) {
530 setCloseCorrelationKeyOnCompletion(capacity);
531 return this;
532 }
533
534 /**
535 * Discards the aggregated message on completion timeout.
536 * <p/>
537 * This means on timeout the aggregated message is dropped and not sent out of the aggregator.
538 *
539 * @return builder
540 */
541 public AggregateDefinition discardOnCompletionTimeout() {
542 setDiscardOnCompletionTimeout(true);
543 return this;
544 }
545
546 /**
547 * Enables the batch completion mode where we aggregate from a {@link org.apache.camel.BatchConsumer}
548 * and aggregate the total number of exchanges the {@link org.apache.camel.BatchConsumer} has reported
549 * as total by checking the exchange property {@link org.apache.camel.Exchange#BATCH_COMPLETE} when its complete.
550 *
551 * @return builder
552 */
553 public AggregateDefinition completionFromBatchConsumer() {
554 setCompletionFromBatchConsumer(true);
555 return this;
556 }
557
558 /**
559 * Sets the completion size, which is the number of aggregated exchanges which would
560 * cause the aggregate to consider the group as complete and send out the aggregated exchange.
561 *
562 * @param completionSize the completion size
563 * @return builder
564 */
565 public AggregateDefinition completionSize(int completionSize) {
566 setCompletionSize(completionSize);
567 return this;
568 }
569
570 /**
571 * Sets the completion size, which is the number of aggregated exchanges which would
572 * cause the aggregate to consider the group as complete and send out the aggregated exchange.
573 *
574 * @param completionSize the completion size as an {@link org.apache.camel.Expression} which is evaluated as a {@link Integer} type
575 * @return builder
576 */
577 public AggregateDefinition completionSize(Expression completionSize) {
578 setCompletionSizeExpression(new ExpressionSubElementDefinition(completionSize));
579 return this;
580 }
581
582 /**
583 * Sets the completion interval, which would cause the aggregate to consider the group as complete
584 * and send out the aggregated exchange.
585 *
586 * @param completionInterval the interval in millis
587 * @return the builder
588 */
589 public AggregateDefinition completionInterval(long completionInterval) {
590 setCompletionInterval(completionInterval);
591 return this;
592 }
593
594 /**
595 * Sets the completion timeout, which would cause the aggregate to consider the group as complete
596 * and send out the aggregated exchange.
597 *
598 * @param completionTimeout the timeout in millis
599 * @return the builder
600 */
601 public AggregateDefinition completionTimeout(long completionTimeout) {
602 setCompletionTimeout(completionTimeout);
603 return this;
604 }
605
606 /**
607 * Sets the completion timeout, which would cause the aggregate to consider the group as complete
608 * and send out the aggregated exchange.
609 *
610 * @param completionTimeout the timeout as an {@link Expression} which is evaluated as a {@link Long} type
611 * @return the builder
612 */
613 public AggregateDefinition completionTimeout(Expression completionTimeout) {
614 setCompletionTimeoutExpression(new ExpressionSubElementDefinition(completionTimeout));
615 return this;
616 }
617
618 /**
619 * Sets the aggregate strategy to use
620 *
621 * @param aggregationStrategy the aggregate strategy to use
622 * @return the builder
623 */
624 public AggregateDefinition aggregationStrategy(AggregationStrategy aggregationStrategy) {
625 setAggregationStrategy(aggregationStrategy);
626 return this;
627 }
628
629 /**
630 * Sets the aggregate strategy to use
631 *
632 * @param aggregationStrategyRef reference to the strategy to lookup in the registry
633 * @return the builder
634 */
635 public AggregateDefinition aggregationStrategyRef(String aggregationStrategyRef) {
636 setAggregationStrategyRef(aggregationStrategyRef);
637 return this;
638 }
639
640 /**
641 * Sets the custom aggregate repository to use.
642 * <p/>
643 * Will by default use {@link org.apache.camel.processor.aggregate.MemoryAggregationRepository}
644 *
645 * @param aggregationRepository the aggregate repository to use
646 * @return the builder
647 */
648 public AggregateDefinition aggregationRepository(AggregationRepository aggregationRepository) {
649 setAggregationRepository(aggregationRepository);
650 return this;
651 }
652
653 /**
654 * Sets the custom aggregate repository to use
655 * <p/>
656 * Will by default use {@link org.apache.camel.processor.aggregate.MemoryAggregationRepository}
657 *
658 * @param aggregationRepositoryRef reference to the repository to lookup in the registry
659 * @return the builder
660 */
661 public AggregateDefinition aggregationRepositoryRef(String aggregationRepositoryRef) {
662 setAggregationRepositoryRef(aggregationRepositoryRef);
663 return this;
664 }
665
666 /**
667 * Enables grouped exchanges, so the aggregator will group all aggregated exchanges into a single
668 * combined Exchange holding all the aggregated exchanges in a {@link java.util.List} as a exchange
669 * property with the key {@link org.apache.camel.Exchange#GROUPED_EXCHANGE}.
670 *
671 * @return the builder
672 */
673 public AggregateDefinition groupExchanges() {
674 setGroupExchanges(true);
675 // must use eager check when using grouped exchanges
676 setEagerCheckCompletion(true);
677 return this;
678 }
679
680 /**
681 * Sets the predicate used to determine if the aggregation is completed
682 *
683 * @param predicate the predicate
684 * @return the builder
685 */
686 public AggregateDefinition completionPredicate(Predicate predicate) {
687 checkNoCompletedPredicate();
688 setCompletionPredicate(new ExpressionSubElementDefinition(predicate));
689 return this;
690 }
691
692 /**
693 * Sets the force completion on stop flag, which considers the current group as complete
694 * and sends out the aggregated exchange when the stop event is executed
695 *
696 * @return builder
697 */
698 public AggregateDefinition forceCompletionOnStop() {
699 setForceCompletionOnStop(true);
700 return this;
701 }
702
703 public Boolean getForceCompletionOnStop() {
704 return forceCompletionOnStop;
705 }
706
707 public boolean isForceCompletionOnStop() {
708 return forceCompletionOnStop != null && forceCompletionOnStop;
709 }
710
711 public void setForceCompletionOnStop(Boolean forceCompletionOnStop) {
712 this.forceCompletionOnStop = forceCompletionOnStop;
713 }
714
715 /**
716 * Sending the aggregated output in parallel
717 *
718 * @return the builder
719 */
720 public AggregateDefinition parallelProcessing() {
721 setParallelProcessing(true);
722 return this;
723 }
724
725 public AggregateDefinition executorService(ExecutorService executorService) {
726 setExecutorService(executorService);
727 return this;
728 }
729
730 public AggregateDefinition executorServiceRef(String executorServiceRef) {
731 setExecutorServiceRef(executorServiceRef);
732 return this;
733 }
734
735 public AggregateDefinition timeoutCheckerExecutorService(ScheduledExecutorService executorService) {
736 setTimeoutCheckerExecutorService(executorService);
737 return this;
738 }
739
740 public AggregateDefinition timeoutCheckerExecutorServiceRef(String executorServiceRef) {
741 setTimeoutCheckerExecutorServiceRef(executorServiceRef);
742 return this;
743 }
744
745 protected void checkNoCompletedPredicate() {
746 if (getCompletionPredicate() != null) {
747 throw new IllegalArgumentException("There is already a completionPredicate defined for this aggregator: " + this);
748 }
749 }
750
751 public void setCorrelationExpression(ExpressionSubElementDefinition correlationExpression) {
752 this.correlationExpression = correlationExpression;
753 }
754
755 public ExpressionSubElementDefinition getCorrelationExpression() {
756 return correlationExpression;
757 }
758
759 // Section - Methods from ExpressionNode
760 // Needed to copy methods from ExpressionNode here so that I could specify the
761 // correlation expression as optional in JAXB
762
763 public ExpressionDefinition getExpression() {
764 if (expression == null && correlationExpression != null) {
765 expression = correlationExpression.getExpressionType();
766 }
767 return expression;
768 }
769
770 public void setExpression(ExpressionDefinition expression) {
771 this.expression = expression;
772 }
773
774 @Override
775 public List<ProcessorDefinition<?>> getOutputs() {
776 return outputs;
777 }
778
779 public boolean isOutputSupported() {
780 return true;
781 }
782
783 public void setOutputs(List<ProcessorDefinition<?>> outputs) {
784 this.outputs = outputs;
785 }
786
787 }