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