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.Collection;
020    
021    import javax.xml.bind.annotation.XmlAccessType;
022    import javax.xml.bind.annotation.XmlAccessorType;
023    import javax.xml.bind.annotation.XmlAttribute;
024    import javax.xml.bind.annotation.XmlElement;
025    import javax.xml.bind.annotation.XmlRootElement;
026    import javax.xml.bind.annotation.XmlTransient;
027    
028    import org.apache.camel.Endpoint;
029    import org.apache.camel.Exchange;
030    import org.apache.camel.Expression;
031    import org.apache.camel.Predicate;
032    import org.apache.camel.Processor;
033    import org.apache.camel.Route;
034    import org.apache.camel.builder.ExpressionClause;
035    import org.apache.camel.model.language.ExpressionType;
036    import org.apache.camel.processor.Aggregator;
037    import org.apache.camel.processor.aggregate.AggregationCollection;
038    import org.apache.camel.processor.aggregate.AggregationStrategy;
039    import org.apache.camel.processor.aggregate.UseLatestAggregationStrategy;
040    import org.apache.camel.spi.RouteContext;
041    
042    /**
043     * Represents an XML <aggregator/> element
044     *
045     * @version $Revision: 61188 $
046     */
047    @XmlRootElement(name = "aggregator")
048    @XmlAccessorType(XmlAccessType.FIELD)
049    public class AggregatorType extends ExpressionNode {
050        @XmlTransient
051        private AggregationStrategy aggregationStrategy;
052        @XmlTransient
053        private AggregationCollection aggregationCollection;
054        @XmlAttribute(required = false)
055        private Integer batchSize;
056        @XmlAttribute(required = false)
057        private Integer outBatchSize;
058        @XmlAttribute(required = false)
059        private Long batchTimeout;
060        @XmlAttribute(required = false)
061        private String strategyRef;
062        @XmlAttribute(required = false)
063        private String collectionRef;    
064        @XmlElement(name = "completedPredicate", required = false)
065        private ExpressionSubElementType completedPredicate;
066    
067        public AggregatorType() {
068        }
069    
070        public AggregatorType(Expression correlationExpression) {
071            super(correlationExpression);
072        }
073    
074        public AggregatorType(ExpressionType correlationExpression) {
075            super(correlationExpression);
076        }
077    
078        public AggregatorType(Expression correlationExpression, AggregationStrategy aggregationStrategy) {
079            super(correlationExpression);
080            this.aggregationStrategy = aggregationStrategy;
081        }
082    
083        @Override
084        public String toString() {
085            return "Aggregator[" + getExpression() + " -> " + getOutputs() + "]";
086        }
087    
088        @Override
089        public String getShortName() {
090            return "aggregator";
091        }
092    
093        @SuppressWarnings("unchecked")
094        @Override
095        public void addRoutes(RouteContext routeContext, Collection<Route> routes) throws Exception {
096            final Aggregator aggregator = createAggregator(routeContext);
097            doAddRoute(routeContext, routes, aggregator);
098        }
099        
100        private void doAddRoute(RouteContext routeContext, Collection<Route> routes, final Aggregator aggregator)
101            throws Exception {
102            Route route = new Route<Exchange>(aggregator.getEndpoint(), aggregator) {
103                @Override
104                public String toString() {
105                    return "AggregatorRoute[" + getEndpoint() + " -> " + aggregator.getProcessor() + "]";
106                }
107            };
108    
109            routes.add(route);
110        }
111     
112        @Override
113        public Processor createProcessor(RouteContext routeContext) throws Exception {
114            final Aggregator aggregator = createAggregator(routeContext);
115            
116            doAddRoute(routeContext, routeContext.getCamelContext().getRoutes(), aggregator);
117            routeContext.setIsRouteAdded(true);
118            return aggregator;
119        }
120    
121        protected Aggregator createAggregator(RouteContext routeContext) throws Exception {
122            Endpoint from = routeContext.getEndpoint();
123            final Processor processor = routeContext.createProcessor(this);
124    
125            final Aggregator aggregator;
126            if (getAggregationCollection() == null) {
127                setAggregationCollection(createAggregationCollection(routeContext));
128            }
129            
130            if (aggregationCollection != null) {
131                // create the aggregator using the collection
132                // pre configure the collection if its expression and strategy is not set, then
133                // use the ones that is pre configured with this type
134                if (aggregationCollection.getCorrelationExpression() == null) {
135                    aggregationCollection.setCorrelationExpression(getExpression());
136                }
137                if (aggregationCollection.getAggregationStrategy() == null) {
138                    AggregationStrategy strategy = createAggregationStrategy(routeContext);
139                    aggregationCollection.setAggregationStrategy(strategy);
140                }
141                aggregator = new Aggregator(from, processor, aggregationCollection);
142            } else {
143                // create the aggregator using a default collection
144                AggregationStrategy strategy = createAggregationStrategy(routeContext);
145    
146                Expression aggregateExpression = getExpression().createExpression(routeContext);
147    
148                Predicate predicate = null;
149                if (getCompletedPredicate() != null) {
150                    predicate = getCompletedPredicate().createPredicate(routeContext);
151                }
152                if (predicate != null) {
153                    aggregator = new Aggregator(from, processor, aggregateExpression, strategy, predicate);
154                } else {
155                    aggregator = new Aggregator(from, processor, aggregateExpression, strategy);
156                }
157            }
158            
159            if (batchSize != null) {
160                aggregator.setBatchSize(batchSize);
161            }
162            
163            if (batchTimeout != null) {
164                aggregator.setBatchTimeout(batchTimeout);
165            }
166    
167            if (outBatchSize != null) {
168                aggregator.setOutBatchSize(outBatchSize);
169            }
170            
171            return aggregator;
172        }
173    
174        private AggregationStrategy createAggregationStrategy(RouteContext routeContext) {
175            AggregationStrategy strategy = getAggregationStrategy();
176            if (strategy == null && strategyRef != null) {
177                strategy = routeContext.lookup(strategyRef, AggregationStrategy.class);
178            }
179            if (strategy == null) {
180                // fallback to use latest
181                strategy = new UseLatestAggregationStrategy();
182            }
183            return strategy;
184        }
185    
186        private AggregationCollection createAggregationCollection(RouteContext routeContext) {
187            AggregationCollection collection = getAggregationCollection();
188            if (collection == null && collectionRef != null) {
189                collection = routeContext.lookup(collectionRef, AggregationCollection.class);
190            }
191            return collection;
192        }    
193        
194        public AggregationCollection getAggregationCollection() {
195            return aggregationCollection;
196        }
197    
198        public void setAggregationCollection(AggregationCollection aggregationCollection) {
199            this.aggregationCollection = aggregationCollection;
200        }
201    
202        public AggregationStrategy getAggregationStrategy() {
203            return aggregationStrategy;
204        }
205    
206        public void setAggregationStrategy(AggregationStrategy aggregationStrategy) {
207            this.aggregationStrategy = aggregationStrategy;
208        }
209    
210        public Integer getBatchSize() {
211            return batchSize;
212        }
213    
214        public void setBatchSize(Integer batchSize) {
215            this.batchSize = batchSize;
216        }
217    
218        public Integer getOutBatchSize() {
219            return outBatchSize;
220        }
221    
222        public void setOutBatchSize(Integer outBatchSize) {
223            this.outBatchSize = outBatchSize;
224        }
225    
226        public Long getBatchTimeout() {
227            return batchTimeout;
228        }
229    
230        public void setBatchTimeout(Long batchTimeout) {
231            this.batchTimeout = batchTimeout;
232        }
233    
234        public String getStrategyRef() {
235            return strategyRef;
236        }
237    
238        public void setStrategyRef(String strategyRef) {
239            this.strategyRef = strategyRef;
240        }
241    
242        public void setCompletedPredicate(ExpressionSubElementType completedPredicate) {
243            this.completedPredicate = completedPredicate;
244        }
245    
246        public ExpressionSubElementType getCompletedPredicate() {
247            return completedPredicate;
248        }
249    
250        // Fluent API
251        //-------------------------------------------------------------------------
252        public AggregatorType batchSize(int batchSize) {
253            setBatchSize(batchSize);
254            return this;
255        }
256    
257        public AggregatorType outBatchSize(int batchSize) {
258            setOutBatchSize(batchSize);
259            return this;
260        }
261    
262        public AggregatorType batchTimeout(long batchTimeout) {
263            setBatchTimeout(batchTimeout);
264            return this;
265        }
266    
267        public AggregatorType aggregationCollection(AggregationCollection aggregationCollection) {
268            setAggregationCollection(aggregationCollection);
269            return this;
270        }
271    
272        public AggregatorType aggregationStrategy(AggregationStrategy aggregationStrategy) {
273            setAggregationStrategy(aggregationStrategy);
274            return this;
275        }
276    
277        public AggregatorType strategyRef(String strategyRef) {
278            setStrategyRef(strategyRef);
279            return this;
280        }
281    
282        /**
283         * Sets the predicate used to determine if the aggregation is completed
284         *
285         * @return the clause used to create the predicate
286         */
287        public ExpressionClause<AggregatorType> completedPredicate() {
288            checkNoCompletedPredicate();
289            ExpressionClause<AggregatorType> clause = new ExpressionClause<AggregatorType>(this);
290            setCompletedPredicate(new ExpressionSubElementType((Expression)clause));
291            return clause;
292        }
293    
294        /**
295         * Sets the predicate used to determine if the aggregation is completed
296         */
297        public AggregatorType completedPredicate(Predicate predicate) {
298            checkNoCompletedPredicate();
299            setCompletedPredicate(new ExpressionSubElementType(predicate));
300            return this;
301        }
302    
303        protected void checkNoCompletedPredicate() {
304            if (getCompletedPredicate() != null) {
305                throw new IllegalArgumentException("There is already a completedPredicate defined for this aggregator: " + this);
306            }
307        }
308    }