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 */
017package org.apache.camel.reifier;
018
019import java.util.concurrent.ExecutorService;
020import java.util.concurrent.ScheduledExecutorService;
021
022import org.apache.camel.AggregationStrategy;
023import org.apache.camel.CamelContextAware;
024import org.apache.camel.Expression;
025import org.apache.camel.Predicate;
026import org.apache.camel.Processor;
027import org.apache.camel.Route;
028import org.apache.camel.model.AggregateDefinition;
029import org.apache.camel.model.OptimisticLockRetryPolicyDefinition;
030import org.apache.camel.model.ProcessorDefinition;
031import org.apache.camel.processor.CamelInternalProcessor;
032import org.apache.camel.processor.aggregate.AggregateController;
033import org.apache.camel.processor.aggregate.AggregateProcessor;
034import org.apache.camel.processor.aggregate.AggregationStrategyBeanAdapter;
035import org.apache.camel.processor.aggregate.OptimisticLockRetryPolicy;
036import org.apache.camel.spi.AggregationRepository;
037import org.apache.camel.util.concurrent.SynchronousExecutorService;
038
039public class AggregateReifier extends ProcessorReifier<AggregateDefinition> {
040
041    public AggregateReifier(Route route, ProcessorDefinition<?> definition) {
042        super(route, AggregateDefinition.class.cast(definition));
043    }
044
045    @Override
046    public Processor createProcessor() throws Exception {
047        return createAggregator();
048    }
049
050    protected AggregateProcessor createAggregator() throws Exception {
051        Processor childProcessor = this.createChildProcessor(true);
052
053        // wrap the aggregate route in a unit of work processor
054        CamelInternalProcessor internal = new CamelInternalProcessor(camelContext, childProcessor);
055        internal.addAdvice(new CamelInternalProcessor.UnitOfWorkProcessorAdvice(route, camelContext));
056
057        Expression correlation = createExpression(definition.getExpression());
058        AggregationStrategy strategy = createAggregationStrategy();
059
060        boolean parallel = parseBoolean(definition.getParallelProcessing(), false);
061        boolean shutdownThreadPool = willCreateNewThreadPool(definition, parallel);
062        ExecutorService threadPool = getConfiguredExecutorService("Aggregator", definition, parallel);
063        if (threadPool == null && !parallel) {
064            // executor service is mandatory for the Aggregator
065            // we do not run in parallel mode, but use a synchronous executor,
066            // so we run in current thread
067            threadPool = new SynchronousExecutorService();
068            shutdownThreadPool = true;
069        }
070
071        AggregateProcessor answer = new AggregateProcessor(camelContext, internal, correlation, strategy, threadPool, shutdownThreadPool);
072
073        AggregationRepository repository = createAggregationRepository();
074        if (repository != null) {
075            answer.setAggregationRepository(repository);
076        }
077
078        if (definition.getAggregateController() == null && definition.getAggregateControllerRef() != null) {
079            definition.setAggregateController(mandatoryLookup(definition.getAggregateControllerRef(), AggregateController.class));
080        }
081
082        // this EIP supports using a shared timeout checker thread pool or
083        // fallback to create a new thread pool
084        boolean shutdownTimeoutThreadPool = false;
085        ScheduledExecutorService timeoutThreadPool = definition.getTimeoutCheckerExecutorService();
086        if (timeoutThreadPool == null && definition.getTimeoutCheckerExecutorServiceRef() != null) {
087            // lookup existing thread pool
088            timeoutThreadPool = lookup(definition.getTimeoutCheckerExecutorServiceRef(), ScheduledExecutorService.class);
089            if (timeoutThreadPool == null) {
090                // then create a thread pool assuming the ref is a thread pool
091                // profile id
092                timeoutThreadPool = camelContext.getExecutorServiceManager().newScheduledThreadPool(this, AggregateProcessor.AGGREGATE_TIMEOUT_CHECKER,
093                                                                                                                      definition.getTimeoutCheckerExecutorServiceRef());
094                if (timeoutThreadPool == null) {
095                    throw new IllegalArgumentException("ExecutorServiceRef " + definition.getTimeoutCheckerExecutorServiceRef()
096                                                       + " not found in registry (as an ScheduledExecutorService instance) or as a thread pool profile.");
097                }
098                shutdownTimeoutThreadPool = true;
099            }
100        }
101        answer.setTimeoutCheckerExecutorService(timeoutThreadPool);
102        answer.setShutdownTimeoutCheckerExecutorService(shutdownTimeoutThreadPool);
103
104        if (parseBoolean(definition.getCompletionFromBatchConsumer(), false)
105                && parseBoolean(definition.getDiscardOnAggregationFailure(), false)) {
106            throw new IllegalArgumentException("Cannot use both completionFromBatchConsumer and discardOnAggregationFailure on: " + definition);
107        }
108
109        // set other options
110        answer.setParallelProcessing(parallel);
111        Boolean optimisticLocking = parseBoolean(definition.getOptimisticLocking());
112        if (optimisticLocking != null) {
113            answer.setOptimisticLocking(optimisticLocking);
114        }
115        if (definition.getCompletionPredicate() != null) {
116            Predicate predicate = createPredicate(definition.getCompletionPredicate());
117            answer.setCompletionPredicate(predicate);
118        } else if (strategy instanceof Predicate) {
119            // if aggregation strategy implements predicate and was not
120            // configured then use as fallback
121            log.debug("Using AggregationStrategy as completion predicate: {}", strategy);
122            answer.setCompletionPredicate((Predicate)strategy);
123        }
124        if (definition.getCompletionTimeoutExpression() != null) {
125            Expression expression = createExpression(definition.getCompletionTimeoutExpression());
126            answer.setCompletionTimeoutExpression(expression);
127        }
128        Long completionTimeout = parseDuration(definition.getCompletionTimeout());
129        if (completionTimeout != null) {
130            answer.setCompletionTimeout(completionTimeout);
131        }
132        Long completionInterval = parseDuration(definition.getCompletionInterval());
133        if (completionInterval != null) {
134            answer.setCompletionInterval(completionInterval);
135        }
136        if (definition.getCompletionSizeExpression() != null) {
137            Expression expression = createExpression(definition.getCompletionSizeExpression());
138            answer.setCompletionSizeExpression(expression);
139        }
140        Integer completionSize = parseInt(definition.getCompletionSize());
141        if (completionSize != null) {
142            answer.setCompletionSize(completionSize);
143        }
144        Boolean completionFromBatchConsumer = parseBoolean(definition.getCompletionFromBatchConsumer());
145        if (completionFromBatchConsumer != null) {
146            answer.setCompletionFromBatchConsumer(completionFromBatchConsumer);
147        }
148        Boolean completionOnNewCorrelationGroup = parseBoolean(definition.getCompletionOnNewCorrelationGroup());
149        if (completionOnNewCorrelationGroup != null) {
150            answer.setCompletionOnNewCorrelationGroup(completionOnNewCorrelationGroup);
151        }
152        Boolean eagerCheckCompletion = parseBoolean(definition.getEagerCheckCompletion());
153        if (eagerCheckCompletion != null) {
154            answer.setEagerCheckCompletion(eagerCheckCompletion);
155        }
156        Boolean ignoreInvalidCorrelationKeys = parseBoolean(definition.getIgnoreInvalidCorrelationKeys());
157        if (ignoreInvalidCorrelationKeys != null) {
158            answer.setIgnoreInvalidCorrelationKeys(ignoreInvalidCorrelationKeys);
159        }
160        Integer closeCorrelationKeyOnCompletion = parseInt(definition.getCloseCorrelationKeyOnCompletion());
161        if (closeCorrelationKeyOnCompletion != null) {
162            answer.setCloseCorrelationKeyOnCompletion(closeCorrelationKeyOnCompletion);
163        }
164        Boolean discardOnCompletionTimeout = parseBoolean(definition.getDiscardOnCompletionTimeout());
165        if (discardOnCompletionTimeout != null) {
166            answer.setDiscardOnCompletionTimeout(discardOnCompletionTimeout);
167        }
168        Boolean discardOnAggregationFailure = parseBoolean(definition.getDiscardOnAggregationFailure());
169        if (discardOnAggregationFailure != null) {
170            answer.setDiscardOnAggregationFailure(discardOnAggregationFailure);
171        }
172        Boolean forceCompletionOnStop = parseBoolean(definition.getForceCompletionOnStop());
173        if (forceCompletionOnStop != null) {
174            answer.setForceCompletionOnStop(forceCompletionOnStop);
175        }
176        Boolean completeAllOnStop = parseBoolean(definition.getCompleteAllOnStop());
177        if (completeAllOnStop != null) {
178            answer.setCompleteAllOnStop(completeAllOnStop);
179        }
180        if (definition.getOptimisticLockRetryPolicy() == null) {
181            if (definition.getOptimisticLockRetryPolicyDefinition() != null) {
182                answer.setOptimisticLockRetryPolicy(createOptimisticLockRetryPolicy(definition.getOptimisticLockRetryPolicyDefinition()));
183            }
184        } else {
185            answer.setOptimisticLockRetryPolicy(definition.getOptimisticLockRetryPolicy());
186        }
187        if (definition.getAggregateController() != null) {
188            answer.setAggregateController(definition.getAggregateController());
189        }
190        Long completionTimeoutCheckerInterval = parseDuration(definition.getCompletionTimeoutCheckerInterval());
191        if (completionTimeoutCheckerInterval != null) {
192            answer.setCompletionTimeoutCheckerInterval(completionTimeoutCheckerInterval);
193        }
194        return answer;
195    }
196
197    public OptimisticLockRetryPolicy createOptimisticLockRetryPolicy(OptimisticLockRetryPolicyDefinition definition) {
198        OptimisticLockRetryPolicy policy = new OptimisticLockRetryPolicy();
199        if (definition.getMaximumRetries() != null) {
200            policy.setMaximumRetries(parseInt(definition.getMaximumRetries()));
201        }
202        if (definition.getRetryDelay() != null) {
203            policy.setRetryDelay(parseDuration(definition.getRetryDelay()));
204        }
205        if (definition.getMaximumRetryDelay() != null) {
206            policy.setMaximumRetryDelay(parseDuration(definition.getMaximumRetryDelay()));
207        }
208        if (definition.getExponentialBackOff() != null) {
209            policy.setExponentialBackOff(parseBoolean(definition.getExponentialBackOff(), false));
210        }
211        if (definition.getRandomBackOff() != null) {
212            policy.setRandomBackOff(parseBoolean(definition.getRandomBackOff(), false));
213        }
214        return policy;
215    }
216
217    private AggregationStrategy createAggregationStrategy() {
218        AggregationStrategy strategy = definition.getAggregationStrategy();
219        if (strategy == null && definition.getStrategyRef() != null) {
220            Object aggStrategy = lookup(definition.getStrategyRef(), Object.class);
221            if (aggStrategy instanceof AggregationStrategy) {
222                strategy = (AggregationStrategy)aggStrategy;
223            } else if (aggStrategy != null) {
224                AggregationStrategyBeanAdapter adapter = new AggregationStrategyBeanAdapter(aggStrategy, definition.getAggregationStrategyMethodName());
225                if (definition.getStrategyMethodAllowNull() != null) {
226                    adapter.setAllowNullNewExchange(parseBoolean(definition.getStrategyMethodAllowNull(), false));
227                    adapter.setAllowNullOldExchange(parseBoolean(definition.getStrategyMethodAllowNull(), false));
228                }
229                strategy = adapter;
230            } else {
231                throw new IllegalArgumentException("Cannot find AggregationStrategy in Registry with name: " + definition.getStrategyRef());
232            }
233        }
234
235        if (strategy == null) {
236            throw new IllegalArgumentException("AggregationStrategy or AggregationStrategyRef must be set on " + this);
237        }
238
239        if (strategy instanceof CamelContextAware) {
240            ((CamelContextAware)strategy).setCamelContext(camelContext);
241        }
242
243        return strategy;
244    }
245
246    private AggregationRepository createAggregationRepository() {
247        AggregationRepository repository = definition.getAggregationRepository();
248        if (repository == null && definition.getAggregationRepositoryRef() != null) {
249            repository = mandatoryLookup(definition.getAggregationRepositoryRef(), AggregationRepository.class);
250        }
251        return repository;
252    }
253
254}