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.model;
018
019import java.util.ArrayList;
020import java.util.List;
021import java.util.concurrent.ExecutorService;
022import java.util.concurrent.ScheduledExecutorService;
023import java.util.function.Supplier;
024
025import javax.xml.bind.annotation.XmlAccessType;
026import javax.xml.bind.annotation.XmlAccessorType;
027import javax.xml.bind.annotation.XmlAttribute;
028import javax.xml.bind.annotation.XmlElement;
029import javax.xml.bind.annotation.XmlElementRef;
030import javax.xml.bind.annotation.XmlRootElement;
031import javax.xml.bind.annotation.XmlTransient;
032
033import org.apache.camel.AggregationStrategy;
034import org.apache.camel.Expression;
035import org.apache.camel.ExpressionFactory;
036import org.apache.camel.Predicate;
037import org.apache.camel.builder.AggregationStrategyClause;
038import org.apache.camel.builder.ExpressionClause;
039import org.apache.camel.builder.PredicateClause;
040import org.apache.camel.model.language.ExpressionDefinition;
041import org.apache.camel.processor.aggregate.AggregateController;
042import org.apache.camel.processor.aggregate.OptimisticLockRetryPolicy;
043import org.apache.camel.spi.AggregationRepository;
044import org.apache.camel.spi.AsPredicate;
045import org.apache.camel.spi.Metadata;
046
047/**
048 * Aggregates many messages into a single message
049 */
050@Metadata(label = "eip,routing")
051@XmlRootElement(name = "aggregate")
052@XmlAccessorType(XmlAccessType.FIELD)
053public class AggregateDefinition extends ProcessorDefinition<AggregateDefinition> implements OutputNode, ExecutorServiceAwareDefinition<AggregateDefinition> {
054    @XmlElement(name = "correlationExpression", required = true)
055    private ExpressionSubElementDefinition correlationExpression;
056    @XmlElement(name = "completionPredicate")
057    @AsPredicate
058    private ExpressionSubElementDefinition completionPredicate;
059    @XmlElement(name = "completionTimeoutExpression")
060    private ExpressionSubElementDefinition completionTimeoutExpression;
061    @XmlElement(name = "completionSizeExpression")
062    private ExpressionSubElementDefinition completionSizeExpression;
063    @XmlElement(name = "optimisticLockRetryPolicy")
064    private OptimisticLockRetryPolicyDefinition optimisticLockRetryPolicyDefinition;
065    @XmlTransient
066    private ExpressionDefinition expression;
067    @XmlTransient
068    private AggregationStrategy aggregationStrategy;
069    @XmlTransient
070    private ExecutorService executorService;
071    @XmlTransient
072    private ScheduledExecutorService timeoutCheckerExecutorService;
073    @XmlTransient
074    private AggregationRepository aggregationRepository;
075    @XmlTransient
076    private OptimisticLockRetryPolicy optimisticLockRetryPolicy;
077    @XmlAttribute
078    private Boolean parallelProcessing;
079    @XmlAttribute
080    private Boolean optimisticLocking;
081    @XmlAttribute
082    private String executorServiceRef;
083    @XmlAttribute
084    private String timeoutCheckerExecutorServiceRef;
085    @XmlAttribute
086    private String aggregationRepositoryRef;
087    @XmlAttribute
088    private String strategyRef;
089    @XmlAttribute
090    private String strategyMethodName;
091    @XmlAttribute
092    private Boolean strategyMethodAllowNull;
093    @XmlAttribute
094    private Integer completionSize;
095    @XmlAttribute
096    private Long completionInterval;
097    @XmlAttribute
098    private Long completionTimeout;
099    @XmlAttribute
100    @Metadata(defaultValue = "1000")
101    private Long completionTimeoutCheckerInterval = 1000L;
102    @XmlAttribute
103    private Boolean completionFromBatchConsumer;
104    @XmlAttribute
105    private Boolean completionOnNewCorrelationGroup;
106    @XmlAttribute
107    private Boolean eagerCheckCompletion;
108    @XmlAttribute
109    private Boolean ignoreInvalidCorrelationKeys;
110    @XmlAttribute
111    private Integer closeCorrelationKeyOnCompletion;
112    @XmlAttribute
113    private Boolean discardOnCompletionTimeout;
114    @XmlAttribute
115    private Boolean discardOnAggregationFailure;
116    @XmlAttribute
117    private Boolean forceCompletionOnStop;
118    @XmlAttribute
119    private Boolean completeAllOnStop;
120    @XmlTransient
121    private AggregateController aggregateController;
122    @XmlAttribute
123    private String aggregateControllerRef;
124    @XmlElementRef
125    private List<ProcessorDefinition<?>> outputs = new ArrayList<>();
126
127    public AggregateDefinition() {
128    }
129
130    public AggregateDefinition(@AsPredicate Predicate predicate) {
131        this(ExpressionNodeHelper.toExpressionDefinition(predicate));
132    }
133
134    public AggregateDefinition(Expression expression) {
135        this(ExpressionNodeHelper.toExpressionDefinition(expression));
136    }
137
138    public AggregateDefinition(ExpressionDefinition correlationExpression) {
139        setExpression(correlationExpression);
140
141        ExpressionSubElementDefinition cor = new ExpressionSubElementDefinition();
142        cor.setExpressionType(correlationExpression);
143        setCorrelationExpression(cor);
144    }
145
146    public AggregateDefinition(Expression correlationExpression, AggregationStrategy aggregationStrategy) {
147        this(correlationExpression);
148        this.aggregationStrategy = aggregationStrategy;
149    }
150
151    @Override
152    public String toString() {
153        return "Aggregate[" + description() + " -> " + getOutputs() + "]";
154    }
155
156    protected String description() {
157        return getExpression() != null ? getExpression().getLabel() : "";
158    }
159
160    @Override
161    public String getShortName() {
162        return "aggregate";
163    }
164
165    @Override
166    public String getLabel() {
167        return "aggregate[" + description() + "]";
168    }
169
170    @Override
171    public void configureChild(ProcessorDefinition<?> output) {
172        Expression exp = getExpression();
173        if (getExpression() != null && getExpression().getExpressionValue() != null) {
174            exp = getExpression().getExpressionValue();
175        }
176
177        if (exp instanceof ExpressionClause) {
178            ExpressionClause<?> clause = (ExpressionClause<?>)exp;
179            if (clause.getExpressionType() != null) {
180                // if using the Java DSL then the expression may have been set
181                // using the
182                // ExpressionClause which is a fancy builder to define
183                // expressions and predicates
184                // using fluent builders in the DSL. However we need afterwards
185                // a callback to
186                // reset the expression to the expression type the
187                // ExpressionClause did build for us
188                ExpressionFactory model = clause.getExpressionType();
189                if (model instanceof ExpressionDefinition) {
190                    correlationExpression = new ExpressionSubElementDefinition();
191                    correlationExpression.setExpressionType((ExpressionDefinition)model);
192                }
193            }
194        }
195    }
196
197    public AggregationStrategy getAggregationStrategy() {
198        return aggregationStrategy;
199    }
200
201    /**
202     * The AggregationStrategy to use.
203     * <p/>
204     * Configuring an AggregationStrategy is required, and is used to merge the
205     * incoming Exchange with the existing already merged exchanges. At first
206     * call the oldExchange parameter is null. On subsequent invocations the
207     * oldExchange contains the merged exchanges and newExchange is of course
208     * the new incoming Exchange.
209     */
210    public void setAggregationStrategy(AggregationStrategy aggregationStrategy) {
211        this.aggregationStrategy = aggregationStrategy;
212    }
213
214    public String getAggregationStrategyRef() {
215        return strategyRef;
216    }
217
218    /**
219     * A reference to lookup the AggregationStrategy in the Registry.
220     * <p/>
221     * Configuring an AggregationStrategy is required, and is used to merge the
222     * incoming Exchange with the existing already merged exchanges. At first
223     * call the oldExchange parameter is null. On subsequent invocations the
224     * oldExchange contains the merged exchanges and newExchange is of course
225     * the new incoming Exchange.
226     */
227    public void setAggregationStrategyRef(String aggregationStrategyRef) {
228        this.strategyRef = aggregationStrategyRef;
229    }
230
231    public String getStrategyRef() {
232        return strategyRef;
233    }
234
235    /**
236     * A reference to lookup the AggregationStrategy in the Registry.
237     * <p/>
238     * Configuring an AggregationStrategy is required, and is used to merge the
239     * incoming Exchange with the existing already merged exchanges. At first
240     * call the oldExchange parameter is null. On subsequent invocations the
241     * oldExchange contains the merged exchanges and newExchange is of course
242     * the new incoming Exchange.
243     */
244    public void setStrategyRef(String strategyRef) {
245        this.strategyRef = strategyRef;
246    }
247
248    public String getAggregationStrategyMethodName() {
249        return strategyMethodName;
250    }
251
252    /**
253     * This option can be used to explicit declare the method name to use, when
254     * using POJOs as the AggregationStrategy.
255     */
256    public void setAggregationStrategyMethodName(String strategyMethodName) {
257        this.strategyMethodName = strategyMethodName;
258    }
259
260    public Boolean getStrategyMethodAllowNull() {
261        return strategyMethodAllowNull;
262    }
263
264    public String getStrategyMethodName() {
265        return strategyMethodName;
266    }
267
268    /**
269     * This option can be used to explicit declare the method name to use, when
270     * using POJOs as the AggregationStrategy.
271     */
272    public void setStrategyMethodName(String strategyMethodName) {
273        this.strategyMethodName = strategyMethodName;
274    }
275
276    /**
277     * If this option is false then the aggregate method is not used for the
278     * very first aggregation. If this option is true then null values is used
279     * as the oldExchange (at the very first aggregation), when using POJOs as
280     * the AggregationStrategy.
281     */
282    public void setStrategyMethodAllowNull(Boolean strategyMethodAllowNull) {
283        this.strategyMethodAllowNull = strategyMethodAllowNull;
284    }
285
286    /**
287     * The expression used to calculate the correlation key to use for
288     * aggregation. The Exchange which has the same correlation key is
289     * aggregated together. If the correlation key could not be evaluated an
290     * Exception is thrown. You can disable this by using the
291     * ignoreBadCorrelationKeys option.
292     */
293    public void setCorrelationExpression(ExpressionSubElementDefinition correlationExpression) {
294        this.correlationExpression = correlationExpression;
295    }
296
297    public ExpressionSubElementDefinition getCorrelationExpression() {
298        return correlationExpression;
299    }
300
301    public Integer getCompletionSize() {
302        return completionSize;
303    }
304
305    public void setCompletionSize(Integer completionSize) {
306        this.completionSize = completionSize;
307    }
308
309    public OptimisticLockRetryPolicyDefinition getOptimisticLockRetryPolicyDefinition() {
310        return optimisticLockRetryPolicyDefinition;
311    }
312
313    public void setOptimisticLockRetryPolicyDefinition(OptimisticLockRetryPolicyDefinition optimisticLockRetryPolicyDefinition) {
314        this.optimisticLockRetryPolicyDefinition = optimisticLockRetryPolicyDefinition;
315    }
316
317    public OptimisticLockRetryPolicy getOptimisticLockRetryPolicy() {
318        return optimisticLockRetryPolicy;
319    }
320
321    public void setOptimisticLockRetryPolicy(OptimisticLockRetryPolicy optimisticLockRetryPolicy) {
322        this.optimisticLockRetryPolicy = optimisticLockRetryPolicy;
323    }
324
325    public Long getCompletionInterval() {
326        return completionInterval;
327    }
328
329    public void setCompletionInterval(Long completionInterval) {
330        this.completionInterval = completionInterval;
331    }
332
333    public Long getCompletionTimeout() {
334        return completionTimeout;
335    }
336
337    public void setCompletionTimeout(Long completionTimeout) {
338        this.completionTimeout = completionTimeout;
339    }
340
341    public Long getCompletionTimeoutCheckerInterval() {
342        return completionTimeoutCheckerInterval;
343    }
344
345    public void setCompletionTimeoutCheckerInterval(Long completionTimeoutCheckerInterval) {
346        this.completionTimeoutCheckerInterval = completionTimeoutCheckerInterval;
347    }
348
349    public ExpressionSubElementDefinition getCompletionPredicate() {
350        return completionPredicate;
351    }
352
353    public void setCompletionPredicate(ExpressionSubElementDefinition completionPredicate) {
354        this.completionPredicate = completionPredicate;
355    }
356
357    public ExpressionSubElementDefinition getCompletionTimeoutExpression() {
358        return completionTimeoutExpression;
359    }
360
361    /**
362     * Time in millis that an aggregated exchange should be inactive before its
363     * complete (timeout). This option can be set as either a fixed value or
364     * using an Expression which allows you to evaluate a timeout dynamically -
365     * will use Long as result. If both are set Camel will fallback to use the
366     * fixed value if the Expression result was null or 0. You cannot use this
367     * option together with completionInterval, only one of the two can be used.
368     * <p/>
369     * By default the timeout checker runs every second, you can use the
370     * completionTimeoutCheckerInterval option to configure how frequently to
371     * run the checker. The timeout is an approximation and there is no
372     * guarantee that the a timeout is triggered exactly after the timeout
373     * value. It is not recommended to use very low timeout values or checker
374     * intervals.
375     *
376     * @param completionTimeoutExpression the timeout as an {@link Expression}
377     *            which is evaluated as a {@link Long} type
378     */
379    public void setCompletionTimeoutExpression(ExpressionSubElementDefinition completionTimeoutExpression) {
380        this.completionTimeoutExpression = completionTimeoutExpression;
381    }
382
383    public ExpressionSubElementDefinition getCompletionSizeExpression() {
384        return completionSizeExpression;
385    }
386
387    /**
388     * Number of messages aggregated before the aggregation is complete. This
389     * option can be set as either a fixed value or using an Expression which
390     * allows you to evaluate a size dynamically - will use Integer as result.
391     * If both are set Camel will fallback to use the fixed value if the
392     * Expression result was null or 0.
393     *
394     * @param completionSizeExpression the completion size as an
395     *            {@link org.apache.camel.Expression} which is evaluated as a
396     *            {@link Integer} type
397     */
398    public void setCompletionSizeExpression(ExpressionSubElementDefinition completionSizeExpression) {
399        this.completionSizeExpression = completionSizeExpression;
400    }
401
402    public Boolean getCompletionFromBatchConsumer() {
403        return completionFromBatchConsumer;
404    }
405
406    public void setCompletionFromBatchConsumer(Boolean completionFromBatchConsumer) {
407        this.completionFromBatchConsumer = completionFromBatchConsumer;
408    }
409
410    public Boolean getCompletionOnNewCorrelationGroup() {
411        return completionOnNewCorrelationGroup;
412    }
413
414    public void setCompletionOnNewCorrelationGroup(Boolean completionOnNewCorrelationGroup) {
415        this.completionOnNewCorrelationGroup = completionOnNewCorrelationGroup;
416    }
417
418    @Override
419    public ExecutorService getExecutorService() {
420        return executorService;
421    }
422
423    @Override
424    public void setExecutorService(ExecutorService executorService) {
425        this.executorService = executorService;
426    }
427
428    public Boolean getOptimisticLocking() {
429        return optimisticLocking;
430    }
431
432    public void setOptimisticLocking(boolean optimisticLocking) {
433        this.optimisticLocking = optimisticLocking;
434    }
435
436    public Boolean getParallelProcessing() {
437        return parallelProcessing;
438    }
439
440    public void setParallelProcessing(boolean parallelProcessing) {
441        this.parallelProcessing = parallelProcessing;
442    }
443
444    @Override
445    public String getExecutorServiceRef() {
446        return executorServiceRef;
447    }
448
449    @Override
450    public void setExecutorServiceRef(String executorServiceRef) {
451        this.executorServiceRef = executorServiceRef;
452    }
453
454    public Boolean getEagerCheckCompletion() {
455        return eagerCheckCompletion;
456    }
457
458    public void setEagerCheckCompletion(Boolean eagerCheckCompletion) {
459        this.eagerCheckCompletion = eagerCheckCompletion;
460    }
461
462    public Boolean getIgnoreInvalidCorrelationKeys() {
463        return ignoreInvalidCorrelationKeys;
464    }
465
466    public void setIgnoreInvalidCorrelationKeys(Boolean ignoreInvalidCorrelationKeys) {
467        this.ignoreInvalidCorrelationKeys = ignoreInvalidCorrelationKeys;
468    }
469
470    public Integer getCloseCorrelationKeyOnCompletion() {
471        return closeCorrelationKeyOnCompletion;
472    }
473
474    public void setCloseCorrelationKeyOnCompletion(Integer closeCorrelationKeyOnCompletion) {
475        this.closeCorrelationKeyOnCompletion = closeCorrelationKeyOnCompletion;
476    }
477
478    public AggregationRepository getAggregationRepository() {
479        return aggregationRepository;
480    }
481
482    public void setAggregationRepository(AggregationRepository aggregationRepository) {
483        this.aggregationRepository = aggregationRepository;
484    }
485
486    public String getAggregationRepositoryRef() {
487        return aggregationRepositoryRef;
488    }
489
490    public void setAggregationRepositoryRef(String aggregationRepositoryRef) {
491        this.aggregationRepositoryRef = aggregationRepositoryRef;
492    }
493
494    public Boolean getDiscardOnCompletionTimeout() {
495        return discardOnCompletionTimeout;
496    }
497
498    public void setDiscardOnCompletionTimeout(Boolean discardOnCompletionTimeout) {
499        this.discardOnCompletionTimeout = discardOnCompletionTimeout;
500    }
501
502    public Boolean getDiscardOnAggregationFailure() {
503        return discardOnAggregationFailure;
504    }
505
506    public void setDiscardOnAggregationFailure(Boolean discardOnAggregationFailure) {
507        this.discardOnAggregationFailure = discardOnAggregationFailure;
508    }
509
510    public void setTimeoutCheckerExecutorService(ScheduledExecutorService timeoutCheckerExecutorService) {
511        this.timeoutCheckerExecutorService = timeoutCheckerExecutorService;
512    }
513
514    public ScheduledExecutorService getTimeoutCheckerExecutorService() {
515        return timeoutCheckerExecutorService;
516    }
517
518    public void setTimeoutCheckerExecutorServiceRef(String timeoutCheckerExecutorServiceRef) {
519        this.timeoutCheckerExecutorServiceRef = timeoutCheckerExecutorServiceRef;
520    }
521
522    public String getTimeoutCheckerExecutorServiceRef() {
523        return timeoutCheckerExecutorServiceRef;
524    }
525
526    public Boolean getForceCompletionOnStop() {
527        return forceCompletionOnStop;
528    }
529
530    public void setForceCompletionOnStop(Boolean forceCompletionOnStop) {
531        this.forceCompletionOnStop = forceCompletionOnStop;
532    }
533
534    public Boolean getCompleteAllOnStop() {
535        return completeAllOnStop;
536    }
537
538    public void setCompleteAllOnStop(Boolean completeAllOnStop) {
539        this.completeAllOnStop = completeAllOnStop;
540    }
541
542    public AggregateController getAggregateController() {
543        return aggregateController;
544    }
545
546    public void setAggregateController(AggregateController aggregateController) {
547        this.aggregateController = aggregateController;
548    }
549
550    public String getAggregateControllerRef() {
551        return aggregateControllerRef;
552    }
553
554    /**
555     * To use a {@link org.apache.camel.processor.aggregate.AggregateController}
556     * to allow external sources to control this aggregator.
557     */
558    public void setAggregateControllerRef(String aggregateControllerRef) {
559        this.aggregateControllerRef = aggregateControllerRef;
560    }
561
562    // Fluent API
563    // -------------------------------------------------------------------------
564
565    /**
566     * Use eager completion checking which means that the completionPredicate
567     * will use the incoming Exchange. As opposed to without eager completion
568     * checking the completionPredicate will use the aggregated Exchange.
569     *
570     * @return builder
571     */
572    public AggregateDefinition eagerCheckCompletion() {
573        setEagerCheckCompletion(true);
574        return this;
575    }
576
577    /**
578     * If a correlation key cannot be successfully evaluated it will be ignored
579     * by logging a DEBUG and then just ignore the incoming Exchange.
580     *
581     * @return builder
582     */
583    public AggregateDefinition ignoreInvalidCorrelationKeys() {
584        setIgnoreInvalidCorrelationKeys(true);
585        return this;
586    }
587
588    /**
589     * Closes a correlation key when its complete. Any <i>late</i> received
590     * exchanges which has a correlation key that has been closed, it will be
591     * defined and a ClosedCorrelationKeyException is thrown.
592     *
593     * @param capacity the maximum capacity of the closed correlation key cache.
594     *            Use <tt>0</tt> or negative value for unbounded capacity.
595     * @return builder
596     */
597    public AggregateDefinition closeCorrelationKeyOnCompletion(int capacity) {
598        setCloseCorrelationKeyOnCompletion(capacity);
599        return this;
600    }
601
602    /**
603     * Discards the aggregated message on completion timeout.
604     * <p/>
605     * This means on timeout the aggregated message is dropped and not sent out
606     * of the aggregator.
607     *
608     * @return builder
609     */
610    public AggregateDefinition discardOnCompletionTimeout() {
611        setDiscardOnCompletionTimeout(true);
612        return this;
613    }
614
615    /**
616     * Discards the aggregated message when aggregation failed (an exception was
617     * thrown from {@link AggregationStrategy}. This means the partly aggregated
618     * message is dropped and not sent out of the aggregator.
619     * <p/>
620     * This option cannot be used together with completionFromBatchConsumer.
621     *
622     * @return builder
623     */
624    public AggregateDefinition discardOnAggregationFailure() {
625        setDiscardOnAggregationFailure(true);
626        return this;
627    }
628
629    /**
630     * Enables the batch completion mode where we aggregate from a
631     * {@link org.apache.camel.BatchConsumer} and aggregate the total number of
632     * exchanges the {@link org.apache.camel.BatchConsumer} has reported as
633     * total by checking the exchange property
634     * {@link org.apache.camel.Exchange#BATCH_COMPLETE} when its complete.
635     * <p/>
636     * This option cannot be used together with discardOnAggregationFailure.
637     *
638     * @return builder
639     */
640    public AggregateDefinition completionFromBatchConsumer() {
641        setCompletionFromBatchConsumer(true);
642        return this;
643    }
644
645    /**
646     * Enables completion on all previous groups when a new incoming correlation
647     * group. This can for example be used to complete groups with same
648     * correlation keys when they are in consecutive order. Notice when this is
649     * enabled then only 1 correlation group can be in progress as when a new
650     * correlation group starts, then the previous groups is forced completed.
651     *
652     * @return builder
653     */
654    public AggregateDefinition completionOnNewCorrelationGroup() {
655        setCompletionOnNewCorrelationGroup(true);
656        return this;
657    }
658
659    /**
660     * Number of messages aggregated before the aggregation is complete. This
661     * option can be set as either a fixed value or using an Expression which
662     * allows you to evaluate a size dynamically - will use Integer as result.
663     * If both are set Camel will fallback to use the fixed value if the
664     * Expression result was null or 0.
665     *
666     * @param completionSize the completion size, must be a positive number
667     * @return builder
668     */
669    public AggregateDefinition completionSize(int completionSize) {
670        setCompletionSize(completionSize);
671        return this;
672    }
673
674    /**
675     * Number of messages aggregated before the aggregation is complete. This
676     * option can be set as either a fixed value or using an Expression which
677     * allows you to evaluate a size dynamically - will use Integer as result.
678     * If both are set Camel will fallback to use the fixed value if the
679     * Expression result was null or 0.
680     *
681     * @param completionSize the completion size as an
682     *            {@link org.apache.camel.Expression} which is evaluated as a
683     *            {@link Integer} type
684     * @return builder
685     */
686    public AggregateDefinition completionSize(Expression completionSize) {
687        setCompletionSizeExpression(new ExpressionSubElementDefinition(completionSize));
688        return this;
689    }
690
691    /**
692     * A repeating period in millis by which the aggregator will complete all
693     * current aggregated exchanges. Camel has a background task which is
694     * triggered every period. You cannot use this option together with
695     * completionTimeout, only one of them can be used.
696     *
697     * @param completionInterval the interval in millis, must be a positive
698     *            value
699     * @return the builder
700     */
701    public AggregateDefinition completionInterval(long completionInterval) {
702        setCompletionInterval(completionInterval);
703        return this;
704    }
705
706    /**
707     * Time in millis that an aggregated exchange should be inactive before its
708     * complete (timeout). This option can be set as either a fixed value or
709     * using an Expression which allows you to evaluate a timeout dynamically -
710     * will use Long as result. If both are set Camel will fallback to use the
711     * fixed value if the Expression result was null or 0. You cannot use this
712     * option together with completionInterval, only one of the two can be used.
713     * <p/>
714     * By default the timeout checker runs every second, you can use the
715     * completionTimeoutCheckerInterval option to configure how frequently to
716     * run the checker. The timeout is an approximation and there is no
717     * guarantee that the a timeout is triggered exactly after the timeout
718     * value. It is not recommended to use very low timeout values or checker
719     * intervals.
720     *
721     * @param completionTimeout the timeout in millis, must be a positive value
722     * @return the builder
723     */
724    public AggregateDefinition completionTimeout(long completionTimeout) {
725        setCompletionTimeout(completionTimeout);
726        return this;
727    }
728
729    /**
730     * Time in millis that an aggregated exchange should be inactive before its
731     * complete (timeout). This option can be set as either a fixed value or
732     * using an Expression which allows you to evaluate a timeout dynamically -
733     * will use Long as result. If both are set Camel will fallback to use the
734     * fixed value if the Expression result was null or 0. You cannot use this
735     * option together with completionInterval, only one of the two can be used.
736     * <p/>
737     * By default the timeout checker runs every second, you can use the
738     * completionTimeoutCheckerInterval option to configure how frequently to
739     * run the checker. The timeout is an approximation and there is no
740     * guarantee that the a timeout is triggered exactly after the timeout
741     * value. It is not recommended to use very low timeout values or checker
742     * intervals.
743     *
744     * @param completionTimeout the timeout as an {@link Expression} which is
745     *            evaluated as a {@link Long} type
746     * @return the builder
747     */
748    public AggregateDefinition completionTimeout(Expression completionTimeout) {
749        setCompletionTimeoutExpression(new ExpressionSubElementDefinition(completionTimeout));
750        return this;
751    }
752
753    /**
754     * Interval in millis that is used by the background task that checks for
755     * timeouts ({@link org.apache.camel.TimeoutMap}).
756     * <p/>
757     * By default the timeout checker runs every second. The timeout is an
758     * approximation and there is no guarantee that the a timeout is triggered
759     * exactly after the timeout value. It is not recommended to use very low
760     * timeout values or checker intervals.
761     *
762     * @param completionTimeoutCheckerInterval the interval in millis, must be a
763     *            positive value
764     * @return the builder
765     */
766    public AggregateDefinition completionTimeoutCheckerInterval(long completionTimeoutCheckerInterval) {
767        setCompletionTimeoutCheckerInterval(completionTimeoutCheckerInterval);
768        return this;
769    }
770
771    /**
772     * Sets the AggregationStrategy to use with a fluent builder.
773     */
774    public AggregationStrategyClause<AggregateDefinition> aggregationStrategy() {
775        AggregationStrategyClause<AggregateDefinition> clause = new AggregationStrategyClause<>(this);
776        setAggregationStrategy(clause);
777        return clause;
778    }
779
780    /**
781     * Sets the AggregationStrategy to use with a fluent builder.
782     *
783     * @deprecated use {@link #aggregationStrategy()}
784     */
785    @Deprecated
786    public AggregationStrategyClause<AggregateDefinition> strategy() {
787        return aggregationStrategy();
788    }
789
790    /**
791     * Sets the aggregate strategy to use
792     *
793     * @param aggregationStrategy the aggregate strategy to use
794     * @return the builder
795     * @deprecated use {@link #aggregationStrategy(AggregationStrategy)}
796     */
797    @Deprecated
798    public AggregateDefinition strategy(AggregationStrategy aggregationStrategy) {
799        return aggregationStrategy(aggregationStrategy);
800    }
801
802    /**
803     * Sets the aggregate strategy to use
804     *
805     * @param aggregationStrategy the aggregate strategy to use
806     * @return the builder
807     */
808    public AggregateDefinition aggregationStrategy(AggregationStrategy aggregationStrategy) {
809        setAggregationStrategy(aggregationStrategy);
810        return this;
811    }
812
813    /**
814     * Sets the aggregate strategy to use
815     *
816     * @param aggregationStrategy the aggregate strategy to use
817     * @return the builder
818     */
819    public AggregateDefinition aggregationStrategy(Supplier<AggregationStrategy> aggregationStrategy) {
820        setAggregationStrategy(aggregationStrategy.get());
821        return this;
822    }
823
824    /**
825     * Sets the aggregate strategy to use
826     *
827     * @param aggregationStrategyRef reference to the strategy to lookup in the
828     *            registry
829     * @return the builder
830     */
831    public AggregateDefinition aggregationStrategyRef(String aggregationStrategyRef) {
832        setAggregationStrategyRef(aggregationStrategyRef);
833        return this;
834    }
835
836    /**
837     * Sets the method name to use when using a POJO as
838     * {@link AggregationStrategy}.
839     *
840     * @param methodName the method name to call
841     * @return the builder
842     */
843    public AggregateDefinition aggregationStrategyMethodName(String methodName) {
844        setAggregationStrategyMethodName(methodName);
845        return this;
846    }
847
848    /**
849     * Sets allowing null when using a POJO as {@link AggregationStrategy}.
850     *
851     * @return the builder
852     */
853    public AggregateDefinition aggregationStrategyMethodAllowNull() {
854        setStrategyMethodAllowNull(true);
855        return this;
856    }
857
858    /**
859     * Sets the custom aggregate repository to use.
860     * <p/>
861     * Will by default use
862     * {@link org.apache.camel.processor.aggregate.MemoryAggregationRepository}
863     *
864     * @param aggregationRepository the aggregate repository to use
865     * @return the builder
866     */
867    public AggregateDefinition aggregationRepository(AggregationRepository aggregationRepository) {
868        setAggregationRepository(aggregationRepository);
869        return this;
870    }
871
872    /**
873     * Sets the custom aggregate repository to use.
874     * <p/>
875     * Will by default use
876     * {@link org.apache.camel.processor.aggregate.MemoryAggregationRepository}
877     *
878     * @param aggregationRepository the aggregate repository to use
879     * @return the builder
880     */
881    public AggregateDefinition aggregationRepository(Supplier<AggregationRepository> aggregationRepository) {
882        setAggregationRepository(aggregationRepository.get());
883        return this;
884    }
885
886    /**
887     * Sets the custom aggregate repository to use
888     * <p/>
889     * Will by default use
890     * {@link org.apache.camel.processor.aggregate.MemoryAggregationRepository}
891     *
892     * @param aggregationRepositoryRef reference to the repository to lookup in
893     *            the registry
894     * @return the builder
895     */
896    public AggregateDefinition aggregationRepositoryRef(String aggregationRepositoryRef) {
897        setAggregationRepositoryRef(aggregationRepositoryRef);
898        return this;
899    }
900
901    /**
902     * A Predicate to indicate when an aggregated exchange is complete. If this
903     * is not specified and the AggregationStrategy object implements Predicate,
904     * the aggregationStrategy object will be used as the completionPredicate.
905     */
906    public AggregateDefinition completionPredicate(@AsPredicate Predicate predicate) {
907        checkNoCompletedPredicate();
908        setCompletionPredicate(new ExpressionSubElementDefinition(predicate));
909        return this;
910    }
911
912    /**
913     * A Predicate to indicate when an aggregated exchange is complete. If this
914     * is not specified and the AggregationStrategy object implements Predicate,
915     * the aggregationStrategy object will be used as the completionPredicate.
916     */
917    @AsPredicate
918    public PredicateClause<AggregateDefinition> completionPredicate() {
919        PredicateClause<AggregateDefinition> clause = new PredicateClause<>(this);
920        completionPredicate(clause);
921        return clause;
922    }
923
924    /**
925     * A Predicate to indicate when an aggregated exchange is complete. If this
926     * is not specified and the AggregationStrategy object implements Predicate,
927     * the aggregationStrategy object will be used as the completionPredicate.
928     */
929    @AsPredicate
930    public PredicateClause<AggregateDefinition> completion() {
931        return completionPredicate();
932    }
933
934    /**
935     * A Predicate to indicate when an aggregated exchange is complete. If this
936     * is not specified and the AggregationStrategy object implements Predicate,
937     * the aggregationStrategy object will be used as the completionPredicate.
938     */
939    public AggregateDefinition completion(@AsPredicate Predicate predicate) {
940        return completionPredicate(predicate);
941    }
942
943    /**
944     * Indicates to complete all current aggregated exchanges when the context
945     * is stopped
946     */
947    public AggregateDefinition forceCompletionOnStop() {
948        setForceCompletionOnStop(true);
949        return this;
950    }
951
952    /**
953     * Indicates to wait to complete all current and partial (pending)
954     * aggregated exchanges when the context is stopped.
955     * <p/>
956     * This also means that we will wait for all pending exchanges which are
957     * stored in the aggregation repository to complete so the repository is
958     * empty before we can stop.
959     * <p/>
960     * You may want to enable this when using the memory based aggregation
961     * repository that is memory based only, and do not store data on disk. When
962     * this option is enabled, then the aggregator is waiting to complete all
963     * those exchanges before its stopped, when stopping CamelContext or the
964     * route using it.
965     */
966    public AggregateDefinition completeAllOnStop() {
967        setCompleteAllOnStop(true);
968        return this;
969    }
970
971    /**
972     * When aggregated are completed they are being send out of the aggregator.
973     * This option indicates whether or not Camel should use a thread pool with
974     * multiple threads for concurrency. If no custom thread pool has been
975     * specified then Camel creates a default pool with 10 concurrent threads.
976     */
977    public AggregateDefinition parallelProcessing() {
978        setParallelProcessing(true);
979        return this;
980    }
981
982    /**
983     * When aggregated are completed they are being send out of the aggregator.
984     * This option indicates whether or not Camel should use a thread pool with
985     * multiple threads for concurrency. If no custom thread pool has been
986     * specified then Camel creates a default pool with 10 concurrent threads.
987     */
988    public AggregateDefinition parallelProcessing(boolean parallelProcessing) {
989        setParallelProcessing(parallelProcessing);
990        return this;
991    }
992
993    /**
994     * Turns on using optimistic locking, which requires the
995     * aggregationRepository being used, is supporting this by implementing
996     * {@link org.apache.camel.spi.OptimisticLockingAggregationRepository}.
997     */
998    public AggregateDefinition optimisticLocking() {
999        setOptimisticLocking(true);
1000        return this;
1001    }
1002
1003    /**
1004     * Allows to configure retry settings when using optimistic locking.
1005     */
1006    public AggregateDefinition optimisticLockRetryPolicy(OptimisticLockRetryPolicy policy) {
1007        setOptimisticLockRetryPolicy(policy);
1008        return this;
1009    }
1010
1011    /**
1012     * If using parallelProcessing you can specify a custom thread pool to be
1013     * used. In fact also if you are not using parallelProcessing this custom
1014     * thread pool is used to send out aggregated exchanges as well.
1015     */
1016    @Override
1017    public AggregateDefinition executorService(ExecutorService executorService) {
1018        setExecutorService(executorService);
1019        return this;
1020    }
1021
1022    /**
1023     * If using parallelProcessing you can specify a custom thread pool to be
1024     * used. In fact also if you are not using parallelProcessing this custom
1025     * thread pool is used to send out aggregated exchanges as well.
1026     */
1027    @Override
1028    public AggregateDefinition executorServiceRef(String executorServiceRef) {
1029        setExecutorServiceRef(executorServiceRef);
1030        return this;
1031    }
1032
1033    /**
1034     * If using either of the completionTimeout, completionTimeoutExpression, or
1035     * completionInterval options a background thread is created to check for
1036     * the completion for every aggregator. Set this option to provide a custom
1037     * thread pool to be used rather than creating a new thread for every
1038     * aggregator.
1039     */
1040    public AggregateDefinition timeoutCheckerExecutorService(ScheduledExecutorService executorService) {
1041        setTimeoutCheckerExecutorService(executorService);
1042        return this;
1043    }
1044
1045    /**
1046     * If using either of the completionTimeout, completionTimeoutExpression, or
1047     * completionInterval options a background thread is created to check for
1048     * the completion for every aggregator. Set this option to provide a custom
1049     * thread pool to be used rather than creating a new thread for every
1050     * aggregator.
1051     */
1052    public AggregateDefinition timeoutCheckerExecutorService(Supplier<ScheduledExecutorService> executorService) {
1053        setTimeoutCheckerExecutorService(executorService.get());
1054        return this;
1055    }
1056
1057    /**
1058     * If using either of the completionTimeout, completionTimeoutExpression, or
1059     * completionInterval options a background thread is created to check for
1060     * the completion for every aggregator. Set this option to provide a custom
1061     * thread pool to be used rather than creating a new thread for every
1062     * aggregator.
1063     */
1064    public AggregateDefinition timeoutCheckerExecutorServiceRef(String executorServiceRef) {
1065        setTimeoutCheckerExecutorServiceRef(executorServiceRef);
1066        return this;
1067    }
1068
1069    /**
1070     * To use a {@link org.apache.camel.processor.aggregate.AggregateController}
1071     * to allow external sources to control this aggregator.
1072     */
1073    public AggregateDefinition aggregateController(AggregateController aggregateController) {
1074        setAggregateController(aggregateController);
1075        return this;
1076    }
1077
1078    /**
1079     * To use a {@link org.apache.camel.processor.aggregate.AggregateController}
1080     * to allow external sources to control this aggregator.
1081     */
1082    public AggregateDefinition aggregateController(Supplier<AggregateController> aggregateController) {
1083        setAggregateController(aggregateController.get());
1084        return this;
1085    }
1086
1087    // Section - Methods from ExpressionNode
1088    // Needed to copy methods from ExpressionNode here so that I could specify
1089    // the
1090    // correlation expression as optional in JAXB
1091
1092    public ExpressionDefinition getExpression() {
1093        if (expression == null && correlationExpression != null) {
1094            expression = correlationExpression.getExpressionType();
1095        }
1096        return expression;
1097    }
1098
1099    public void setExpression(ExpressionDefinition expression) {
1100        this.expression = expression;
1101    }
1102
1103    public void setExpression(Expression expression) {
1104        setExpression(new ExpressionDefinition(expression));
1105    }
1106
1107    protected void checkNoCompletedPredicate() {
1108        if (getCompletionPredicate() != null) {
1109            throw new IllegalArgumentException("There is already a completionPredicate defined for this aggregator: " + this);
1110        }
1111    }
1112
1113    @Override
1114    public List<ProcessorDefinition<?>> getOutputs() {
1115        return outputs;
1116    }
1117
1118    public void setOutputs(List<ProcessorDefinition<?>> outputs) {
1119        this.outputs = outputs;
1120    }
1121
1122}