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