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