/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.camel.model;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ScheduledExecutorService;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementRef;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;
import org.apache.camel.CamelContextAware;
import org.apache.camel.Expression;
import org.apache.camel.Predicate;
import org.apache.camel.Processor;
import org.apache.camel.builder.AggregationStrategyClause;
import org.apache.camel.builder.ExpressionClause;
import org.apache.camel.builder.PredicateClause;
import org.apache.camel.model.language.ExpressionDefinition;
import org.apache.camel.processor.CamelInternalProcessor;
import org.apache.camel.processor.aggregate.AggregateController;
import org.apache.camel.processor.aggregate.AggregateProcessor;
import org.apache.camel.processor.aggregate.AggregationStrategy;
import org.apache.camel.processor.aggregate.AggregationStrategyBeanAdapter;
import org.apache.camel.processor.aggregate.ClosedCorrelationKeyException;
import org.apache.camel.processor.aggregate.GroupedExchangeAggregationStrategy;
import org.apache.camel.processor.aggregate.OptimisticLockRetryPolicy;
import org.apache.camel.spi.AggregationRepository;
import org.apache.camel.spi.AsPredicate;
import org.apache.camel.spi.Metadata;
import org.apache.camel.spi.RouteContext;
import org.apache.camel.util.concurrent.SynchronousExecutorService;
/**
* Aggregates many messages into a single message
*
* @version
*/
@Metadata(label = "eip,routing")
@XmlRootElement(name = "aggregate")
@XmlAccessorType(XmlAccessType.FIELD)
public class AggregateDefinition extends ProcessorDefinition<AggregateDefinition> implements ExecutorServiceAwareDefinition<AggregateDefinition> {
@XmlElement(name = "correlationExpression", required = true)
private ExpressionSubElementDefinition correlationExpression;
@XmlElement(name = "completionPredicate") @AsPredicate
private ExpressionSubElementDefinition completionPredicate;
@XmlElement(name = "completionTimeout")
private ExpressionSubElementDefinition completionTimeoutExpression;
@XmlElement(name = "completionSize")
private ExpressionSubElementDefinition completionSizeExpression;
@XmlElement(name = "optimisticLockRetryPolicy")
private OptimisticLockRetryPolicyDefinition optimisticLockRetryPolicyDefinition;
@XmlTransient
private ExpressionDefinition expression;
@XmlTransient
private AggregationStrategy aggregationStrategy;
@XmlTransient
private ExecutorService executorService;
@XmlTransient
private ScheduledExecutorService timeoutCheckerExecutorService;
@XmlTransient
private AggregationRepository aggregationRepository;
@XmlTransient
private OptimisticLockRetryPolicy optimisticLockRetryPolicy;
@XmlAttribute
private Boolean parallelProcessing;
@XmlAttribute
private Boolean optimisticLocking;
@XmlAttribute
private String executorServiceRef;
@XmlAttribute
private String timeoutCheckerExecutorServiceRef;
@XmlAttribute
private String aggregationRepositoryRef;
@XmlAttribute
private String strategyRef;
@XmlAttribute
private String strategyMethodName;
@XmlAttribute
private Boolean strategyMethodAllowNull;
@XmlAttribute
private Integer completionSize;
@XmlAttribute
private Long completionInterval;
@XmlAttribute
private Long completionTimeout;
@XmlAttribute
private Boolean completionFromBatchConsumer;
@XmlAttribute
@Deprecated
private Boolean groupExchanges;
@XmlAttribute
private Boolean eagerCheckCompletion;
@XmlAttribute
private Boolean ignoreInvalidCorrelationKeys;
@XmlAttribute
private Integer closeCorrelationKeyOnCompletion;
@XmlAttribute
private Boolean discardOnCompletionTimeout;
@XmlAttribute
private Boolean forceCompletionOnStop;
@XmlAttribute
private Boolean completeAllOnStop;
@XmlTransient
private AggregateController aggregateController;
@XmlAttribute
private String aggregateControllerRef;
@XmlElementRef
private List<ProcessorDefinition<?>> outputs = new ArrayList<ProcessorDefinition<?>>();
public AggregateDefinition() {
}
public AggregateDefinition(@AsPredicate Predicate predicate) {
this(ExpressionNodeHelper.toExpressionDefinition(predicate));
}
public AggregateDefinition(Expression expression) {
this(ExpressionNodeHelper.toExpressionDefinition(expression));
}
public AggregateDefinition(ExpressionDefinition correlationExpression) {
setExpression(correlationExpression);
ExpressionSubElementDefinition cor = new ExpressionSubElementDefinition();
cor.setExpressionType(correlationExpression);
setCorrelationExpression(cor);
}
public AggregateDefinition(Expression correlationExpression, AggregationStrategy aggregationStrategy) {
this(correlationExpression);
this.aggregationStrategy = aggregationStrategy;
}
@Override
public String toString() {
return "Aggregate[" + description() + " -> " + getOutputs() + "]";
}
protected String description() {
return getExpression() != null ? getExpression().getLabel() : "";
}
@Override
public String getLabel() {
return "aggregate[" + description() + "]";
}
@Override
public Processor createProcessor(RouteContext routeContext) throws Exception {
return createAggregator(routeContext);
}
protected AggregateProcessor createAggregator(RouteContext routeContext) throws Exception {
Processor childProcessor = this.createChildProcessor(routeContext, true);
// wrap the aggregate route in a unit of work processor
CamelInternalProcessor internal = new CamelInternalProcessor(childProcessor);
internal.addAdvice(new CamelInternalProcessor.UnitOfWorkProcessorAdvice(routeContext));
Expression correlation = getExpression().createExpression(routeContext);
AggregationStrategy strategy = createAggregationStrategy(routeContext);
boolean parallel = getParallelProcessing() != null && getParallelProcessing();
boolean shutdownThreadPool = ProcessorDefinitionHelper.willCreateNewThreadPool(routeContext, this, parallel);
ExecutorService threadPool = ProcessorDefinitionHelper.getConfiguredExecutorService(routeContext, "Aggregator", this, parallel);
if (threadPool == null && !parallel) {
// executor service is mandatory for the Aggregator
// we do not run in parallel mode, but use a synchronous executor, so we run in current thread
threadPool = new SynchronousExecutorService();
shutdownThreadPool = true;
}
AggregateProcessor answer = new AggregateProcessor(routeContext.getCamelContext(), internal,
correlation, strategy, threadPool, shutdownThreadPool);
AggregationRepository repository = createAggregationRepository(routeContext);
if (repository != null) {
answer.setAggregationRepository(repository);
}
if (getAggregateController() == null && getAggregateControllerRef() != null) {
setAggregateController(routeContext.mandatoryLookup(getAggregateControllerRef(), AggregateController.class));
}
// this EIP supports using a shared timeout checker thread pool or fallback to create a new thread pool
boolean shutdownTimeoutThreadPool = false;
ScheduledExecutorService timeoutThreadPool = timeoutCheckerExecutorService;
if (timeoutThreadPool == null && timeoutCheckerExecutorServiceRef != null) {
// lookup existing thread pool
timeoutThreadPool = routeContext.getCamelContext().getRegistry().lookupByNameAndType(timeoutCheckerExecutorServiceRef, ScheduledExecutorService.class);
if (timeoutThreadPool == null) {
// then create a thread pool assuming the ref is a thread pool profile id
timeoutThreadPool = routeContext.getCamelContext().getExecutorServiceManager().newScheduledThreadPool(this,
AggregateProcessor.AGGREGATE_TIMEOUT_CHECKER, timeoutCheckerExecutorServiceRef);
if (timeoutThreadPool == null) {
throw new IllegalArgumentException("ExecutorServiceRef " + timeoutCheckerExecutorServiceRef
+ " not found in registry (as an ScheduledExecutorService instance) or as a thread pool profile.");
}
shutdownTimeoutThreadPool = true;
}
}
answer.setTimeoutCheckerExecutorService(timeoutThreadPool);
answer.setShutdownTimeoutCheckerExecutorService(shutdownTimeoutThreadPool);
// set other options
answer.setParallelProcessing(parallel);
if (getOptimisticLocking() != null) {
answer.setOptimisticLocking(getOptimisticLocking());
}
if (getCompletionPredicate() != null) {
Predicate predicate = getCompletionPredicate().createPredicate(routeContext);
answer.setCompletionPredicate(predicate);
} else if (strategy instanceof Predicate) {
// if aggregation strategy implements predicate and was not configured then use as fallback
log.debug("Using AggregationStrategy as completion predicate: {}", strategy);
answer.setCompletionPredicate((Predicate) strategy);
}
if (getCompletionTimeoutExpression() != null) {
Expression expression = getCompletionTimeoutExpression().createExpression(routeContext);
answer.setCompletionTimeoutExpression(expression);
}
if (getCompletionTimeout() != null) {
answer.setCompletionTimeout(getCompletionTimeout());
}
if (getCompletionInterval() != null) {
answer.setCompletionInterval(getCompletionInterval());
}
if (getCompletionSizeExpression() != null) {
Expression expression = getCompletionSizeExpression().createExpression(routeContext);
answer.setCompletionSizeExpression(expression);
}
if (getCompletionSize() != null) {
answer.setCompletionSize(getCompletionSize());
}
if (getCompletionFromBatchConsumer() != null) {
answer.setCompletionFromBatchConsumer(getCompletionFromBatchConsumer());
}
if (getEagerCheckCompletion() != null) {
answer.setEagerCheckCompletion(getEagerCheckCompletion());
}
if (getIgnoreInvalidCorrelationKeys() != null) {
answer.setIgnoreInvalidCorrelationKeys(getIgnoreInvalidCorrelationKeys());
}
if (getCloseCorrelationKeyOnCompletion() != null) {
answer.setCloseCorrelationKeyOnCompletion(getCloseCorrelationKeyOnCompletion());
}
if (getDiscardOnCompletionTimeout() != null) {
answer.setDiscardOnCompletionTimeout(getDiscardOnCompletionTimeout());
}
if (getForceCompletionOnStop() != null) {
answer.setForceCompletionOnStop(getForceCompletionOnStop());
}
if (getCompleteAllOnStop() != null) {
answer.setCompleteAllOnStop(getCompleteAllOnStop());
}
if (optimisticLockRetryPolicy == null) {
if (getOptimisticLockRetryPolicyDefinition() != null) {
answer.setOptimisticLockRetryPolicy(getOptimisticLockRetryPolicyDefinition().createOptimisticLockRetryPolicy());
}
} else {
answer.setOptimisticLockRetryPolicy(optimisticLockRetryPolicy);
}
if (getAggregateController() != null) {
answer.setAggregateController(getAggregateController());
}
return answer;
}
@Override
public void configureChild(ProcessorDefinition<?> output) {
if (expression != null && expression instanceof ExpressionClause) {
ExpressionClause<?> clause = (ExpressionClause<?>) expression;
if (clause.getExpressionType() != null) {
// if using the Java DSL then the expression may have been set using the
// ExpressionClause which is a fancy builder to define expressions and predicates
// using fluent builders in the DSL. However we need afterwards a callback to
// reset the expression to the expression type the ExpressionClause did build for us
expression = clause.getExpressionType();
// set the correlation expression from the expression type, as the model definition
// would then be accurate
correlationExpression = new ExpressionSubElementDefinition();
correlationExpression.setExpressionType(clause.getExpressionType());
}
}
}
private AggregationStrategy createAggregationStrategy(RouteContext routeContext) {
AggregationStrategy strategy = getAggregationStrategy();
if (strategy == null && strategyRef != null) {
Object aggStrategy = routeContext.lookup(strategyRef, Object.class);
if (aggStrategy instanceof AggregationStrategy) {
strategy = (AggregationStrategy) aggStrategy;
} else if (aggStrategy != null) {
AggregationStrategyBeanAdapter adapter = new AggregationStrategyBeanAdapter(aggStrategy, getAggregationStrategyMethodName());
if (getStrategyMethodAllowNull() != null) {
adapter.setAllowNullNewExchange(getStrategyMethodAllowNull());
adapter.setAllowNullOldExchange(getStrategyMethodAllowNull());
}
strategy = adapter;
} else {
throw new IllegalArgumentException("Cannot find AggregationStrategy in Registry with name: " + strategyRef);
}
}
if (groupExchanges != null && groupExchanges) {
if (strategy != null || strategyRef != null) {
throw new IllegalArgumentException("Options groupExchanges and AggregationStrategy cannot be enabled at the same time");
}
if (eagerCheckCompletion != null && !eagerCheckCompletion) {
throw new IllegalArgumentException("Option eagerCheckCompletion cannot be false when groupExchanges has been enabled");
}
// set eager check to enabled by default when using grouped exchanges
setEagerCheckCompletion(true);
// if grouped exchange is enabled then use special strategy for that
strategy = new GroupedExchangeAggregationStrategy();
}
if (strategy == null) {
throw new IllegalArgumentException("AggregationStrategy or AggregationStrategyRef must be set on " + this);
}
if (strategy instanceof CamelContextAware) {
((CamelContextAware) strategy).setCamelContext(routeContext.getCamelContext());
}
return strategy;
}
private AggregationRepository createAggregationRepository(RouteContext routeContext) {
AggregationRepository repository = getAggregationRepository();
if (repository == null && aggregationRepositoryRef != null) {
repository = routeContext.mandatoryLookup(aggregationRepositoryRef, AggregationRepository.class);
}
return repository;
}
public AggregationStrategy getAggregationStrategy() {
return aggregationStrategy;
}
/**
* The AggregationStrategy to use.
* <p/>
* Configuring an AggregationStrategy is required, and is used to merge the incoming Exchange with the existing already merged exchanges.
* At first call the oldExchange parameter is null.
* On subsequent invocations the oldExchange contains the merged exchanges and newExchange is of course the new incoming Exchange.
*/
public void setAggregationStrategy(AggregationStrategy aggregationStrategy) {
this.aggregationStrategy = aggregationStrategy;
}
public String getAggregationStrategyRef() {
return strategyRef;
}
/**
* A reference to lookup the AggregationStrategy in the Registry.
* <p/>
* Configuring an AggregationStrategy is required, and is used to merge the incoming Exchange with the existing already merged exchanges.
* At first call the oldExchange parameter is null.
* On subsequent invocations the oldExchange contains the merged exchanges and newExchange is of course the new incoming Exchange.
*/
public void setAggregationStrategyRef(String aggregationStrategyRef) {
this.strategyRef = aggregationStrategyRef;
}
public String getStrategyRef() {
return strategyRef;
}
/**
* A reference to lookup the AggregationStrategy in the Registry.
* <p/>
* Configuring an AggregationStrategy is required, and is used to merge the incoming Exchange with the existing already merged exchanges.
* At first call the oldExchange parameter is null.
* On subsequent invocations the oldExchange contains the merged exchanges and newExchange is of course the new incoming Exchange.
*/
public void setStrategyRef(String strategyRef) {
this.strategyRef = strategyRef;
}
public String getAggregationStrategyMethodName() {
return strategyMethodName;
}
/**
* This option can be used to explicit declare the method name to use, when using POJOs as the AggregationStrategy.
*/
public void setAggregationStrategyMethodName(String strategyMethodName) {
this.strategyMethodName = strategyMethodName;
}
public Boolean getStrategyMethodAllowNull() {
return strategyMethodAllowNull;
}
public String getStrategyMethodName() {
return strategyMethodName;
}
/**
* This option can be used to explicit declare the method name to use, when using POJOs as the AggregationStrategy.
*/
public void setStrategyMethodName(String strategyMethodName) {
this.strategyMethodName = strategyMethodName;
}
/**
* If this option is false then the aggregate method is not used for the very first aggregation.
* If this option is true then null values is used as the oldExchange (at the very first aggregation),
* when using POJOs as the AggregationStrategy.
*/
public void setStrategyMethodAllowNull(Boolean strategyMethodAllowNull) {
this.strategyMethodAllowNull = strategyMethodAllowNull;
}
/**
* The expression used to calculate the correlation key to use for aggregation.
* The Exchange which has the same correlation key is aggregated together.
* If the correlation key could not be evaluated an Exception is thrown.
* You can disable this by using the ignoreBadCorrelationKeys option.
*/
public void setCorrelationExpression(ExpressionSubElementDefinition correlationExpression) {
this.correlationExpression = correlationExpression;
}
public ExpressionSubElementDefinition getCorrelationExpression() {
return correlationExpression;
}
public Integer getCompletionSize() {
return completionSize;
}
public void setCompletionSize(Integer completionSize) {
this.completionSize = completionSize;
}
public OptimisticLockRetryPolicyDefinition getOptimisticLockRetryPolicyDefinition() {
return optimisticLockRetryPolicyDefinition;
}
public void setOptimisticLockRetryPolicyDefinition(OptimisticLockRetryPolicyDefinition optimisticLockRetryPolicyDefinition) {
this.optimisticLockRetryPolicyDefinition = optimisticLockRetryPolicyDefinition;
}
public OptimisticLockRetryPolicy getOptimisticLockRetryPolicy() {
return optimisticLockRetryPolicy;
}
public void setOptimisticLockRetryPolicy(OptimisticLockRetryPolicy optimisticLockRetryPolicy) {
this.optimisticLockRetryPolicy = optimisticLockRetryPolicy;
}
public Long getCompletionInterval() {
return completionInterval;
}
public void setCompletionInterval(Long completionInterval) {
this.completionInterval = completionInterval;
}
public Long getCompletionTimeout() {
return completionTimeout;
}
public void setCompletionTimeout(Long completionTimeout) {
this.completionTimeout = completionTimeout;
}
public ExpressionSubElementDefinition getCompletionPredicate() {
return completionPredicate;
}
public void setCompletionPredicate(ExpressionSubElementDefinition completionPredicate) {
this.completionPredicate = completionPredicate;
}
public ExpressionSubElementDefinition getCompletionTimeoutExpression() {
return completionTimeoutExpression;
}
public void setCompletionTimeoutExpression(ExpressionSubElementDefinition completionTimeoutExpression) {
this.completionTimeoutExpression = completionTimeoutExpression;
}
public ExpressionSubElementDefinition getCompletionSizeExpression() {
return completionSizeExpression;
}
public void setCompletionSizeExpression(ExpressionSubElementDefinition completionSizeExpression) {
this.completionSizeExpression = completionSizeExpression;
}
public Boolean getGroupExchanges() {
return groupExchanges;
}
public void setGroupExchanges(Boolean groupExchanges) {
this.groupExchanges = groupExchanges;
}
public Boolean getCompletionFromBatchConsumer() {
return completionFromBatchConsumer;
}
public void setCompletionFromBatchConsumer(Boolean completionFromBatchConsumer) {
this.completionFromBatchConsumer = completionFromBatchConsumer;
}
public ExecutorService getExecutorService() {
return executorService;
}
public void setExecutorService(ExecutorService executorService) {
this.executorService = executorService;
}
public Boolean getOptimisticLocking() {
return optimisticLocking;
}
public void setOptimisticLocking(boolean optimisticLocking) {
this.optimisticLocking = optimisticLocking;
}
public Boolean getParallelProcessing() {
return parallelProcessing;
}
public void setParallelProcessing(boolean parallelProcessing) {
this.parallelProcessing = parallelProcessing;
}
public String getExecutorServiceRef() {
return executorServiceRef;
}
public void setExecutorServiceRef(String executorServiceRef) {
this.executorServiceRef = executorServiceRef;
}
public Boolean getEagerCheckCompletion() {
return eagerCheckCompletion;
}
public void setEagerCheckCompletion(Boolean eagerCheckCompletion) {
this.eagerCheckCompletion = eagerCheckCompletion;
}
public Boolean getIgnoreInvalidCorrelationKeys() {
return ignoreInvalidCorrelationKeys;
}
public void setIgnoreInvalidCorrelationKeys(Boolean ignoreInvalidCorrelationKeys) {
this.ignoreInvalidCorrelationKeys = ignoreInvalidCorrelationKeys;
}
public Integer getCloseCorrelationKeyOnCompletion() {
return closeCorrelationKeyOnCompletion;
}
public void setCloseCorrelationKeyOnCompletion(Integer closeCorrelationKeyOnCompletion) {
this.closeCorrelationKeyOnCompletion = closeCorrelationKeyOnCompletion;
}
public AggregationRepository getAggregationRepository() {
return aggregationRepository;
}
public void setAggregationRepository(AggregationRepository aggregationRepository) {
this.aggregationRepository = aggregationRepository;
}
public String getAggregationRepositoryRef() {
return aggregationRepositoryRef;
}
public void setAggregationRepositoryRef(String aggregationRepositoryRef) {
this.aggregationRepositoryRef = aggregationRepositoryRef;
}
public Boolean getDiscardOnCompletionTimeout() {
return discardOnCompletionTimeout;
}
public void setDiscardOnCompletionTimeout(Boolean discardOnCompletionTimeout) {
this.discardOnCompletionTimeout = discardOnCompletionTimeout;
}
public void setTimeoutCheckerExecutorService(ScheduledExecutorService timeoutCheckerExecutorService) {
this.timeoutCheckerExecutorService = timeoutCheckerExecutorService;
}
public ScheduledExecutorService getTimeoutCheckerExecutorService() {
return timeoutCheckerExecutorService;
}
public void setTimeoutCheckerExecutorServiceRef(String timeoutCheckerExecutorServiceRef) {
this.timeoutCheckerExecutorServiceRef = timeoutCheckerExecutorServiceRef;
}
public String getTimeoutCheckerExecutorServiceRef() {
return timeoutCheckerExecutorServiceRef;
}
public Boolean getForceCompletionOnStop() {
return forceCompletionOnStop;
}
public void setForceCompletionOnStop(Boolean forceCompletionOnStop) {
this.forceCompletionOnStop = forceCompletionOnStop;
}
public Boolean getCompleteAllOnStop() {
return completeAllOnStop;
}
public void setCompleteAllOnStop(Boolean completeAllOnStop) {
this.completeAllOnStop = completeAllOnStop;
}
public AggregateController getAggregateController() {
return aggregateController;
}
public void setAggregateController(AggregateController aggregateController) {
this.aggregateController = aggregateController;
}
public String getAggregateControllerRef() {
return aggregateControllerRef;
}
/**
* To use a {@link org.apache.camel.processor.aggregate.AggregateController} to allow external sources to control
* this aggregator.
*/
public void setAggregateControllerRef(String aggregateControllerRef) {
this.aggregateControllerRef = aggregateControllerRef;
}
// Fluent API
//-------------------------------------------------------------------------
/**
* Use eager completion checking which means that the {{completionPredicate}} will use the incoming Exchange.
* As opposed to without eager completion checking the {{completionPredicate}} will use the aggregated Exchange.
*
* @return builder
*/
public AggregateDefinition eagerCheckCompletion() {
setEagerCheckCompletion(true);
return this;
}
/**
* If a correlation key cannot be successfully evaluated it will be ignored by logging a {{DEBUG}} and then just
* ignore the incoming Exchange.
*
* @return builder
*/
public AggregateDefinition ignoreInvalidCorrelationKeys() {
setIgnoreInvalidCorrelationKeys(true);
return this;
}
/**
* Closes a correlation key when its complete. Any <i>late</i> received exchanges which has a correlation key
* that has been closed, it will be defined and a {@link ClosedCorrelationKeyException}
* is thrown.
*
* @param capacity the maximum capacity of the closed correlation key cache.
* Use <tt>0</tt> or negative value for unbounded capacity.
* @return builder
*/
public AggregateDefinition closeCorrelationKeyOnCompletion(int capacity) {
setCloseCorrelationKeyOnCompletion(capacity);
return this;
}
/**
* Discards the aggregated message on completion timeout.
* <p/>
* This means on timeout the aggregated message is dropped and not sent out of the aggregator.
*
* @return builder
*/
public AggregateDefinition discardOnCompletionTimeout() {
setDiscardOnCompletionTimeout(true);
return this;
}
/**
* Enables the batch completion mode where we aggregate from a {@link org.apache.camel.BatchConsumer}
* and aggregate the total number of exchanges the {@link org.apache.camel.BatchConsumer} has reported
* as total by checking the exchange property {@link org.apache.camel.Exchange#BATCH_COMPLETE} when its complete.
*
* @return builder
*/
public AggregateDefinition completionFromBatchConsumer() {
setCompletionFromBatchConsumer(true);
return this;
}
/**
* Number of messages aggregated before the aggregation is complete. This option can be set as either
* a fixed value or using an Expression which allows you to evaluate a size dynamically - will use Integer as result.
* If both are set Camel will fallback to use the fixed value if the Expression result was null or 0.
*
* @param completionSize the completion size, must be a positive number
* @return builder
*/
public AggregateDefinition completionSize(int completionSize) {
setCompletionSize(completionSize);
return this;
}
/**
* Number of messages aggregated before the aggregation is complete. This option can be set as either
* a fixed value or using an Expression which allows you to evaluate a size dynamically - will use Integer as result.
* If both are set Camel will fallback to use the fixed value if the Expression result was null or 0.
*
* @param completionSize the completion size as an {@link org.apache.camel.Expression} which is evaluated as a {@link Integer} type
* @return builder
*/
public AggregateDefinition completionSize(Expression completionSize) {
setCompletionSizeExpression(new ExpressionSubElementDefinition(completionSize));
return this;
}
/**
* A repeating period in millis by which the aggregator will complete all current aggregated exchanges.
* Camel has a background task which is triggered every period. You cannot use this option together
* with completionTimeout, only one of them can be used.
*
* @param completionInterval the interval in millis, must be a positive value
* @return the builder
*/
public AggregateDefinition completionInterval(long completionInterval) {
setCompletionInterval(completionInterval);
return this;
}
/**
* Time in millis that an aggregated exchange should be inactive before its complete (timeout).
* This option can be set as either a fixed value or using an Expression which allows you to evaluate
* a timeout dynamically - will use Long as result.
* If both are set Camel will fallback to use the fixed value if the Expression result was null or 0.
* You cannot use this option together with completionInterval, only one of the two can be used.
*
* @param completionTimeout the timeout in millis, must be a positive value
* @return the builder
*/
public AggregateDefinition completionTimeout(long completionTimeout) {
setCompletionTimeout(completionTimeout);
return this;
}
/**
* Time in millis that an aggregated exchange should be inactive before its complete (timeout).
* This option can be set as either a fixed value or using an Expression which allows you to evaluate
* a timeout dynamically - will use Long as result.
* If both are set Camel will fallback to use the fixed value if the Expression result was null or 0.
* You cannot use this option together with completionInterval, only one of the two can be used.
*
* @param completionTimeout the timeout as an {@link Expression} which is evaluated as a {@link Long} type
* @return the builder
*/
public AggregateDefinition completionTimeout(Expression completionTimeout) {
setCompletionTimeoutExpression(new ExpressionSubElementDefinition(completionTimeout));
return this;
}
/**
* Sets the AggregationStrategy to use with a fluent builder.
*/
public AggregationStrategyClause<AggregateDefinition> aggregationStrategy() {
AggregationStrategyClause<AggregateDefinition> clause = new AggregationStrategyClause<>(this);
setAggregationStrategy(clause);
return clause;
}
/**
* Sets the AggregationStrategy to use with a fluent builder.
*/
public AggregationStrategyClause<AggregateDefinition> strategy() {
return aggregationStrategy();
}
/**
* Sets the aggregate strategy to use
*
* @param aggregationStrategy the aggregate strategy to use
* @return the builder
*/
public AggregateDefinition strategy(AggregationStrategy aggregationStrategy) {
return aggregationStrategy(aggregationStrategy);
}
/**
* Sets the aggregate strategy to use
*
* @param aggregationStrategy the aggregate strategy to use
* @return the builder
*/
public AggregateDefinition aggregationStrategy(AggregationStrategy aggregationStrategy) {
setAggregationStrategy(aggregationStrategy);
return this;
}
/**
* Sets the aggregate strategy to use
*
* @param aggregationStrategyRef reference to the strategy to lookup in the registry
* @return the builder
*/
public AggregateDefinition aggregationStrategyRef(String aggregationStrategyRef) {
setAggregationStrategyRef(aggregationStrategyRef);
return this;
}
/**
* Sets the method name to use when using a POJO as {@link AggregationStrategy}.
*
* @param methodName the method name to call
* @return the builder
*/
public AggregateDefinition aggregationStrategyMethodName(String methodName) {
setAggregationStrategyMethodName(methodName);
return this;
}
/**
* Sets allowing null when using a POJO as {@link AggregationStrategy}.
*
* @return the builder
*/
public AggregateDefinition aggregationStrategyMethodAllowNull() {
setStrategyMethodAllowNull(true);
return this;
}
/**
* Sets the custom aggregate repository to use.
* <p/>
* Will by default use {@link org.apache.camel.processor.aggregate.MemoryAggregationRepository}
*
* @param aggregationRepository the aggregate repository to use
* @return the builder
*/
public AggregateDefinition aggregationRepository(AggregationRepository aggregationRepository) {
setAggregationRepository(aggregationRepository);
return this;
}
/**
* Sets the custom aggregate repository to use
* <p/>
* Will by default use {@link org.apache.camel.processor.aggregate.MemoryAggregationRepository}
*
* @param aggregationRepositoryRef reference to the repository to lookup in the registry
* @return the builder
*/
public AggregateDefinition aggregationRepositoryRef(String aggregationRepositoryRef) {
setAggregationRepositoryRef(aggregationRepositoryRef);
return this;
}
/**
* Enables grouped exchanges, so the aggregator will group all aggregated exchanges into a single
* combined Exchange holding all the aggregated exchanges in a {@link java.util.List}.
*
* @deprecated use {@link GroupedExchangeAggregationStrategy} as aggregation strategy instead.
*/
@Deprecated
public AggregateDefinition groupExchanges() {
setGroupExchanges(true);
// must use eager check when using grouped exchanges
setEagerCheckCompletion(true);
return this;
}
/**
* A Predicate to indicate when an aggregated exchange is complete.
* If this is not specified and the AggregationStrategy object implements Predicate,
* the aggregationStrategy object will be used as the completionPredicate.
*/
public AggregateDefinition completionPredicate(@AsPredicate Predicate predicate) {
checkNoCompletedPredicate();
setCompletionPredicate(new ExpressionSubElementDefinition(predicate));
return this;
}
/**
* A Predicate to indicate when an aggregated exchange is complete.
* If this is not specified and the AggregationStrategy object implements Predicate,
* the aggregationStrategy object will be used as the completionPredicate.
*/
@AsPredicate
public PredicateClause<AggregateDefinition> completionPredicate() {
PredicateClause<AggregateDefinition> clause = new PredicateClause<>(this);
completionPredicate(clause);
return clause;
}
/**
* A Predicate to indicate when an aggregated exchange is complete.
* If this is not specified and the AggregationStrategy object implements Predicate,
* the aggregationStrategy object will be used as the completionPredicate.
*/
@AsPredicate
public PredicateClause<AggregateDefinition> completion() {
return completionPredicate();
}
/**
* A Predicate to indicate when an aggregated exchange is complete.
* If this is not specified and the AggregationStrategy object implements Predicate,
* the aggregationStrategy object will be used as the completionPredicate.
*/
public AggregateDefinition completion(@AsPredicate Predicate predicate) {
return completionPredicate(predicate);
}
/**
* Indicates to complete all current aggregated exchanges when the context is stopped
*/
public AggregateDefinition forceCompletionOnStop() {
setForceCompletionOnStop(true);
return this;
}
/**
* Indicates to wait to complete all current and partial (pending) aggregated exchanges when the context is stopped.
* <p/>
* This also means that we will wait for all pending exchanges which are stored in the aggregation repository
* to complete so the repository is empty before we can stop.
* <p/>
* You may want to enable this when using the memory based aggregation repository that is memory based only,
* and do not store data on disk. When this option is enabled, then the aggregator is waiting to complete
* all those exchanges before its stopped, when stopping CamelContext or the route using it.
*/
public AggregateDefinition completeAllOnStop() {
setCompleteAllOnStop(true);
return this;
}
/**
* When aggregated are completed they are being send out of the aggregator.
* This option indicates whether or not Camel should use a thread pool with multiple threads for concurrency.
* If no custom thread pool has been specified then Camel creates a default pool with 10 concurrent threads.
*/
public AggregateDefinition parallelProcessing() {
setParallelProcessing(true);
return this;
}
/**
* When aggregated are completed they are being send out of the aggregator.
* This option indicates whether or not Camel should use a thread pool with multiple threads for concurrency.
* If no custom thread pool has been specified then Camel creates a default pool with 10 concurrent threads.
*/
public AggregateDefinition parallelProcessing(boolean parallelProcessing) {
setParallelProcessing(parallelProcessing);
return this;
}
/**
* Turns on using optimistic locking, which requires the aggregationRepository being used,
* is supporting this by implementing {@link org.apache.camel.spi.OptimisticLockingAggregationRepository}.
*/
public AggregateDefinition optimisticLocking() {
setOptimisticLocking(true);
return this;
}
/**
* Allows to configure retry settings when using optimistic locking.
*/
public AggregateDefinition optimisticLockRetryPolicy(OptimisticLockRetryPolicy policy) {
setOptimisticLockRetryPolicy(policy);
return this;
}
/**
* If using parallelProcessing you can specify a custom thread pool to be used.
* In fact also if you are not using parallelProcessing this custom thread pool is used to send out aggregated exchanges as well.
*/
public AggregateDefinition executorService(ExecutorService executorService) {
setExecutorService(executorService);
return this;
}
/**
* If using parallelProcessing you can specify a custom thread pool to be used.
* In fact also if you are not using parallelProcessing this custom thread pool is used to send out aggregated exchanges as well.
*/
public AggregateDefinition executorServiceRef(String executorServiceRef) {
setExecutorServiceRef(executorServiceRef);
return this;
}
/**
* If using either of the completionTimeout, completionTimeoutExpression, or completionInterval options a
* background thread is created to check for the completion for every aggregator.
* Set this option to provide a custom thread pool to be used rather than creating a new thread for every aggregator.
*/
public AggregateDefinition timeoutCheckerExecutorService(ScheduledExecutorService executorService) {
setTimeoutCheckerExecutorService(executorService);
return this;
}
/**
* If using either of the completionTimeout, completionTimeoutExpression, or completionInterval options a
* background thread is created to check for the completion for every aggregator.
* Set this option to provide a custom thread pool to be used rather than creating a new thread for every aggregator.
*/
public AggregateDefinition timeoutCheckerExecutorServiceRef(String executorServiceRef) {
setTimeoutCheckerExecutorServiceRef(executorServiceRef);
return this;
}
/**
* To use a {@link org.apache.camel.processor.aggregate.AggregateController} to allow external sources to control
* this aggregator.
*/
public AggregateDefinition aggregateController(AggregateController aggregateController) {
setAggregateController(aggregateController);
return this;
}
// Section - Methods from ExpressionNode
// Needed to copy methods from ExpressionNode here so that I could specify the
// correlation expression as optional in JAXB
public ExpressionDefinition getExpression() {
if (expression == null && correlationExpression != null) {
expression = correlationExpression.getExpressionType();
}
return expression;
}
public void setExpression(ExpressionDefinition expression) {
this.expression = expression;
}
protected void checkNoCompletedPredicate() {
if (getCompletionPredicate() != null) {
throw new IllegalArgumentException("There is already a completionPredicate defined for this aggregator: " + this);
}
}
@Override
public List<ProcessorDefinition<?>> getOutputs() {
return outputs;
}
public boolean isOutputSupported() {
return true;
}
public void setOutputs(List<ProcessorDefinition<?>> outputs) {
this.outputs = outputs;
}
}