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    
022    import javax.xml.bind.annotation.XmlAccessType;
023    import javax.xml.bind.annotation.XmlAccessorType;
024    import javax.xml.bind.annotation.XmlAttribute;
025    import javax.xml.bind.annotation.XmlElement;
026    import javax.xml.bind.annotation.XmlElementRef;
027    import javax.xml.bind.annotation.XmlRootElement;
028    import javax.xml.bind.annotation.XmlTransient;
029    
030    import org.apache.camel.Expression;
031    import org.apache.camel.Predicate;
032    import org.apache.camel.Processor;
033    import org.apache.camel.builder.ExpressionClause;
034    import org.apache.camel.model.language.ExpressionDefinition;
035    import org.apache.camel.processor.Aggregator;
036    import org.apache.camel.processor.aggregate.AggregationCollection;
037    import org.apache.camel.processor.aggregate.AggregationStrategy;
038    import org.apache.camel.processor.aggregate.GroupedExchangeAggregationStrategy;
039    import org.apache.camel.processor.aggregate.UseLatestAggregationStrategy;
040    import org.apache.camel.spi.RouteContext;
041    
042    /**
043     * Represents an XML <aggregate/> element
044     *
045     * @version $Revision: 892244 $
046     */
047    @XmlRootElement(name = "aggregate")
048    @XmlAccessorType(XmlAccessType.FIELD)
049    public class AggregateDefinition extends ProcessorDefinition<AggregateDefinition> {
050        @XmlElement(name = "correlationExpression", required = false)
051        private ExpressionSubElementDefinition correlationExpression;
052        @XmlTransient
053        private ExpressionDefinition expression;
054        @XmlElementRef
055        private List<ProcessorDefinition> outputs = new ArrayList<ProcessorDefinition>();
056        @XmlTransient
057        private AggregationStrategy aggregationStrategy;
058        @XmlTransient
059        private AggregationCollection aggregationCollection;
060        @XmlAttribute(required = false)
061        private Integer batchSize;
062        @XmlAttribute(required = false)
063        private Integer outBatchSize;
064        @XmlAttribute(required = false)
065        private Long batchTimeout;
066        @XmlAttribute(required = false)
067        private String strategyRef;
068        @XmlAttribute(required = false)
069        private String collectionRef;    
070        @XmlAttribute(required = false)
071        private Boolean groupExchanges;
072        @XmlAttribute(required = false)
073        private Boolean batchSizeFromConsumer;
074        @XmlElement(name = "completionPredicate", required = false)
075        private ExpressionSubElementDefinition completionPredicate;
076    
077        public AggregateDefinition() {
078        }
079    
080        public AggregateDefinition(Predicate predicate) {
081            if (predicate != null) {
082                setExpression(new ExpressionDefinition(predicate));
083            }
084        }    
085        
086        public AggregateDefinition(Expression correlationExpression) {
087            if (correlationExpression != null) {
088                setExpression(new ExpressionDefinition(correlationExpression));
089            }
090        }
091    
092        public AggregateDefinition(ExpressionDefinition correlationExpression) {
093            this.expression = correlationExpression;
094        }
095    
096        public AggregateDefinition(Expression correlationExpression, AggregationStrategy aggregationStrategy) {
097            this(correlationExpression);
098            this.aggregationStrategy = aggregationStrategy;
099        }
100    
101        @Override
102        public String toString() {
103            String expressionString = (getExpression() != null) ? getExpression().getLabel() : "";     
104            return "Aggregate[" + expressionString + " -> " + getOutputs() + "]";
105        }
106    
107        @Override
108        public String getShortName() {
109            return "aggregate";
110        }
111    
112        @Override
113        public String getLabel() {
114            return "aggregate";
115        }
116    
117        @Override
118        public Processor createProcessor(RouteContext routeContext) throws Exception {
119            return createAggregator(routeContext);
120        }
121    
122        public ExpressionClause<AggregateDefinition> createAndSetExpression() {
123            ExpressionClause<AggregateDefinition> clause = new ExpressionClause<AggregateDefinition>(this);
124            this.setExpression(clause);
125            return clause;
126        }
127        
128        protected Aggregator createAggregator(RouteContext routeContext) throws Exception {
129            final Processor processor = routeContext.createProcessor(this);
130    
131            final Aggregator aggregator;
132            if (getAggregationCollection() == null) {
133                setAggregationCollection(createAggregationCollection(routeContext));
134            }
135            
136            if (aggregationCollection != null) {
137                // create the aggregator using the collection
138                // pre configure the collection if its expression and strategy is not set, then
139                // use the ones that is pre configured with this type
140                if (aggregationCollection.getCorrelationExpression() == null) {
141                    aggregationCollection.setCorrelationExpression(getExpression());
142                }
143                if (aggregationCollection.getAggregationStrategy() == null) {
144                    AggregationStrategy strategy = createAggregationStrategy(routeContext);
145                    aggregationCollection.setAggregationStrategy(strategy);
146                }
147                aggregator = new Aggregator(processor, aggregationCollection);
148            } else {
149                // create the aggregator using a default collection
150                AggregationStrategy strategy = createAggregationStrategy(routeContext);
151    
152                if (getExpression() == null) {
153                    throw new IllegalArgumentException("You need to specify an expression or "
154                                                       + "aggregation collection for this aggregator: " + this);
155                }
156                
157                Expression aggregateExpression = getExpression().createExpression(routeContext);           
158    
159                Predicate predicate = null;
160                if (getCompletionPredicate() != null) {
161                    predicate = getCompletionPredicate().createPredicate(routeContext);
162                }
163                if (predicate != null) {
164                    aggregator = new Aggregator(processor, aggregateExpression, strategy, predicate);
165                } else {
166                    aggregator = new Aggregator(processor, aggregateExpression, strategy);
167                }
168            }
169            
170            if (batchSize != null) {
171                aggregator.setBatchSize(batchSize);
172            }
173            if (batchTimeout != null) {
174                aggregator.setBatchTimeout(batchTimeout);
175            }
176            if (outBatchSize != null) {
177                aggregator.setOutBatchSize(outBatchSize);
178            }
179            if (groupExchanges != null) {
180                aggregator.setGroupExchanges(groupExchanges);
181            }
182            if (batchSizeFromConsumer != null) {
183                aggregator.setBatchConsumer(batchSizeFromConsumer);
184            }
185    
186            return aggregator;
187        }
188    
189        private AggregationStrategy createAggregationStrategy(RouteContext routeContext) {
190            AggregationStrategy strategy = getAggregationStrategy();
191            if (strategy == null && strategyRef != null) {
192                strategy = routeContext.lookup(strategyRef, AggregationStrategy.class);
193            }
194            // pick a default strategy
195            if (strategy == null) {
196                if (groupExchanges != null && groupExchanges) {
197                    // if grouped exchange is enabled then use special strategy for that
198                    strategy = new GroupedExchangeAggregationStrategy();
199                } else {
200                    // fallback to use latest
201                    strategy = new UseLatestAggregationStrategy();
202                }
203            }
204            return strategy;
205        }
206    
207        private AggregationCollection createAggregationCollection(RouteContext routeContext) {
208            AggregationCollection collection = getAggregationCollection();
209            if (collection == null && collectionRef != null) {
210                collection = routeContext.lookup(collectionRef, AggregationCollection.class);
211            }
212            return collection;
213        }    
214        
215        public AggregationCollection getAggregationCollection() {
216            return aggregationCollection;
217        }
218    
219        public void setAggregationCollection(AggregationCollection aggregationCollection) {
220            this.aggregationCollection = aggregationCollection;
221        }
222    
223        public AggregationStrategy getAggregationStrategy() {
224            return aggregationStrategy;
225        }
226    
227        public void setAggregationStrategy(AggregationStrategy aggregationStrategy) {
228            this.aggregationStrategy = aggregationStrategy;
229        }
230    
231        public Integer getBatchSize() {
232            return batchSize;
233        }
234    
235        public void setBatchSize(Integer batchSize) {
236            this.batchSize = batchSize;
237        }
238    
239        public Integer getOutBatchSize() {
240            return outBatchSize;
241        }
242    
243        public void setOutBatchSize(Integer outBatchSize) {
244            this.outBatchSize = outBatchSize;
245        }
246    
247        public Long getBatchTimeout() {
248            return batchTimeout;
249        }
250    
251        public void setBatchTimeout(Long batchTimeout) {
252            this.batchTimeout = batchTimeout;
253        }
254    
255        public String getStrategyRef() {
256            return strategyRef;
257        }
258    
259        public void setStrategyRef(String strategyRef) {
260            this.strategyRef = strategyRef;
261        }
262    
263        public String getCollectionRef() {
264            return collectionRef;
265        }
266    
267        public void setCollectionRef(String collectionRef) {
268            this.collectionRef = collectionRef;
269        }
270    
271        public void setCompletionPredicate(ExpressionSubElementDefinition completionPredicate) {
272            this.completionPredicate = completionPredicate;
273        }
274    
275        public ExpressionSubElementDefinition getCompletionPredicate() {
276            return completionPredicate;
277        }
278    
279        public Boolean getGroupExchanges() {
280            return groupExchanges;
281        }
282    
283        public void setGroupExchanges(Boolean groupExchanges) {
284            this.groupExchanges = groupExchanges;
285        }
286    
287        public Boolean getBatchSizeFromConsumer() {
288            return batchSizeFromConsumer;
289        }
290    
291        public void setBatchSizeFromConsumer(Boolean batchSizeFromConsumer) {
292            this.batchSizeFromConsumer = batchSizeFromConsumer;
293        }
294    
295        // Fluent API
296        //-------------------------------------------------------------------------
297    
298        /**
299         * Enables the batch completion mode where we aggregate from a {@link org.apache.camel.BatchConsumer}
300         * and aggregate the total number of exchanges the {@link org.apache.camel.BatchConsumer} has reported
301         * as total by setting the exchange property {@link org.apache.camel.Exchange#BATCH_SIZE}.
302         *
303         * @return builder
304         */
305        public AggregateDefinition batchSizeFromConsumer() {
306            setBatchSizeFromConsumer(true);
307            return this;
308        }
309    
310        /**
311         * Sets the in batch size for number of exchanges received
312         *
313         * @param batchSize  the batch size
314         * @return builder
315         */
316        public AggregateDefinition batchSize(int batchSize) {
317            setBatchSize(batchSize);
318            return this;
319        }
320    
321        /**
322         * Sets the out batch size for number of exchanges sent
323         *
324         * @param batchSize  the batch size
325         * @return builder
326         */
327        public AggregateDefinition outBatchSize(int batchSize) {
328            setOutBatchSize(batchSize);
329            return this;
330        }
331    
332        /**
333         * Sets the batch timeout
334         *
335         * @param batchTimeout  the timeout in millis
336         * @return the builder
337         */
338        public AggregateDefinition batchTimeout(long batchTimeout) {
339            setBatchTimeout(batchTimeout);
340            return this;
341        }
342    
343        /**
344         * Sets the aggregate collection to use
345         *
346         * @param aggregationCollection  the aggregate collection to use
347         * @return the builder
348         */
349        public AggregateDefinition aggregationCollection(AggregationCollection aggregationCollection) {
350            setAggregationCollection(aggregationCollection);
351            return this;
352        }
353    
354        /**
355         * Sets the aggregate strategy to use
356         *
357         * @param aggregationStrategy  the aggregate strategy to use
358         * @return the builder
359         */
360        public AggregateDefinition aggregationStrategy(AggregationStrategy aggregationStrategy) {
361            setAggregationStrategy(aggregationStrategy);
362            return this;
363        }
364    
365        /**
366         * Sets the aggregate collection to use
367         *
368         * @param collectionRef  reference to the aggregate collection to lookup in the registry
369         * @return the builder
370         */
371        public AggregateDefinition collectionRef(String collectionRef) {
372            setCollectionRef(collectionRef);
373            return this;
374        }
375    
376        /**
377         * Sets the aggregate strategy to use
378         *
379         * @param strategyRef  reference to the strategy to lookup in the registry
380         * @return the builder
381         */
382        public AggregateDefinition strategyRef(String strategyRef) {
383            setStrategyRef(strategyRef);
384            return this;
385        }
386    
387        /**
388         * Enables grouped exchanges, so the aggregator will group all aggregated exchanges into a single
389         * combined Exchange holding all the aggregated exchanges in a {@link java.util.List} as a exchange
390         * property with the key {@link org.apache.camel.Exchange#GROUPED_EXCHANGE}.
391         *
392         * @return the builder
393         */
394        public AggregateDefinition groupExchanges() {
395            setGroupExchanges(true);
396            return this;
397        }
398    
399        /**
400         * Sets the predicate used to determine if the aggregation is completed
401         *
402         * @return the clause used to create the predicate
403         */
404        public ExpressionClause<AggregateDefinition> completionPredicate() {
405            checkNoCompletedPredicate();
406            ExpressionClause<AggregateDefinition> clause = new ExpressionClause<AggregateDefinition>(this);
407            setCompletionPredicate(new ExpressionSubElementDefinition((Expression)clause));
408            return clause;
409        }
410    
411        /**
412         * Sets the predicate used to determine if the aggregation is completed
413         *
414         * @param predicate  the predicate
415         */
416        public AggregateDefinition completionPredicate(Predicate predicate) {
417            checkNoCompletedPredicate();
418            setCompletionPredicate(new ExpressionSubElementDefinition(predicate));
419            return this;
420        }
421    
422        protected void checkNoCompletedPredicate() {
423            if (getCompletionPredicate() != null) {
424                throw new IllegalArgumentException("There is already a completionPredicate defined for this aggregator: " + this);
425            }
426        }
427    
428        public void setCorrelationExpression(ExpressionSubElementDefinition correlationExpression) {
429            this.correlationExpression = correlationExpression;
430        }
431    
432        public ExpressionSubElementDefinition getCorrelationExpression() {
433            return correlationExpression;
434        }
435    
436        // Section - Methods from ExpressionNode
437        // Needed to copy methods from ExpressionNode here so that I could specify the
438        // correlation expression as optional in JAXB
439        
440        public ExpressionDefinition getExpression() {
441            if (expression == null && correlationExpression != null) {
442                expression = correlationExpression.getExpressionType();            
443            }
444            return expression;
445        }
446    
447        public void setExpression(ExpressionDefinition expression) {
448            this.expression = expression;
449        }
450    
451        public List<ProcessorDefinition> getOutputs() {
452            return outputs;
453        }
454    
455        public void setOutputs(List<ProcessorDefinition> outputs) {
456            this.outputs = outputs;
457        }
458    }