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 }