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