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.model;
018
019import java.util.ArrayList;
020import java.util.Collection;
021import java.util.HashMap;
022import java.util.Iterator;
023import java.util.List;
024import java.util.Map;
025import java.util.concurrent.ExecutorService;
026import javax.xml.bind.annotation.XmlAccessType;
027import javax.xml.bind.annotation.XmlAccessorType;
028import javax.xml.bind.annotation.XmlAttribute;
029import javax.xml.bind.annotation.XmlElement;
030import javax.xml.bind.annotation.XmlElementRef;
031import javax.xml.bind.annotation.XmlRootElement;
032import javax.xml.bind.annotation.XmlTransient;
033
034import org.apache.camel.Predicate;
035import org.apache.camel.Processor;
036import org.apache.camel.processor.CamelInternalProcessor;
037import org.apache.camel.processor.OnCompletionProcessor;
038import org.apache.camel.spi.AsPredicate;
039import org.apache.camel.spi.Metadata;
040import org.apache.camel.spi.RouteContext;
041
042/**
043 * Route to be executed when normal route processing completes
044 *
045 * @version 
046 */
047@Metadata(label = "configuration")
048@XmlRootElement(name = "onCompletion")
049@XmlAccessorType(XmlAccessType.FIELD)
050public class OnCompletionDefinition extends ProcessorDefinition<OnCompletionDefinition> implements ExecutorServiceAwareDefinition<OnCompletionDefinition> {
051    @XmlAttribute @Metadata(defaultValue = "AfterConsumer")
052    private OnCompletionMode mode;
053    @XmlAttribute
054    private Boolean onCompleteOnly;
055    @XmlAttribute
056    private Boolean onFailureOnly;
057    @XmlElement(name = "onWhen") @AsPredicate
058    private WhenDefinition onWhen;
059    @XmlAttribute
060    private Boolean parallelProcessing;
061    @XmlAttribute
062    private String executorServiceRef;
063    @XmlAttribute(name = "useOriginalMessage")
064    private Boolean useOriginalMessagePolicy;
065    @XmlElementRef
066    private List<ProcessorDefinition<?>> outputs = new ArrayList<>();
067    @XmlTransient
068    private ExecutorService executorService;
069    @XmlTransient
070    private Boolean routeScoped;
071    // TODO: in Camel 3.0 the OnCompletionDefinition should not contain state and OnCompletion processors
072    @XmlTransient
073    private final Map<String, Processor> onCompletions = new HashMap<>();
074
075    public OnCompletionDefinition() {
076    }
077
078    public boolean isRouteScoped() {
079        // is context scoped by default
080        return routeScoped != null ? routeScoped : false;
081    }
082
083    public Processor getOnCompletion(String routeId) {
084        return onCompletions.get(routeId);
085    }
086
087    public Collection<Processor> getOnCompletions() {
088        return onCompletions.values();
089    }
090
091    @Override
092    public String toString() {
093        return "onCompletion[" + getOutputs() + "]";
094    }
095
096    @Override
097    public String getLabel() {
098        return "onCompletion";
099    }
100
101    @Override
102    public boolean isAbstract() {
103        return true;
104    }
105
106    @Override
107    public boolean isTopLevelOnly() {
108        return true;
109    }
110
111    @Override
112    public Processor createProcessor(RouteContext routeContext) throws Exception {
113        // assign whether this was a route scoped onCompletion or not
114        // we need to know this later when setting the parent, as only route scoped should have parent
115        // Note: this logic can possible be removed when the Camel routing engine decides at runtime
116        // to apply onCompletion in a more dynamic fashion than current code base
117        // and therefore is in a better position to decide among context/route scoped OnCompletion at runtime
118        if (routeScoped == null) {
119            routeScoped = super.getParent() != null;
120        }
121
122        boolean isOnCompleteOnly = getOnCompleteOnly() != null && getOnCompleteOnly();
123        boolean isOnFailureOnly = getOnFailureOnly() != null && getOnFailureOnly();
124        boolean isParallelProcessing = getParallelProcessing() != null && getParallelProcessing();
125        boolean original = getUseOriginalMessagePolicy() != null && getUseOriginalMessagePolicy();
126
127        if (isOnCompleteOnly && isOnFailureOnly) {
128            throw new IllegalArgumentException("Both onCompleteOnly and onFailureOnly cannot be true. Only one of them can be true. On node: " + this);
129        }
130        if (original) {
131            // ensure allow original is turned on
132            routeContext.setAllowUseOriginalMessage(true);
133        }
134
135        String routeId = routeContext.getRoute().idOrCreate(routeContext.getCamelContext().getNodeIdFactory());
136
137        Processor childProcessor = this.createChildProcessor(routeContext, true);
138
139        // wrap the on completion route in a unit of work processor
140        CamelInternalProcessor internal = new CamelInternalProcessor(childProcessor);
141        internal.addAdvice(new CamelInternalProcessor.UnitOfWorkProcessorAdvice(routeContext));
142
143        onCompletions.put(routeId, internal);
144
145        Predicate when = null;
146        if (onWhen != null) {
147            when = onWhen.getExpression().createPredicate(routeContext);
148        }
149
150        boolean shutdownThreadPool = ProcessorDefinitionHelper.willCreateNewThreadPool(routeContext, this, isParallelProcessing);
151        ExecutorService threadPool = ProcessorDefinitionHelper.getConfiguredExecutorService(routeContext, "OnCompletion", this, isParallelProcessing);
152
153        // should be after consumer by default
154        boolean afterConsumer = mode == null || mode == OnCompletionMode.AfterConsumer;
155
156        OnCompletionProcessor answer = new OnCompletionProcessor(routeContext.getCamelContext(), internal,
157                threadPool, shutdownThreadPool, isOnCompleteOnly, isOnFailureOnly, when, original, afterConsumer);
158        return answer;
159    }
160
161    /**
162     * Removes all existing {@link org.apache.camel.model.OnCompletionDefinition} from the definition.
163     * <p/>
164     * This is used to let route scoped <tt>onCompletion</tt> overrule any global <tt>onCompletion</tt>.
165     * Hence we remove all existing as they are global.
166     *
167     * @param definition the parent definition that is the route
168     */
169    public void removeAllOnCompletionDefinition(ProcessorDefinition<?> definition) {
170        for (Iterator<ProcessorDefinition<?>> it = definition.getOutputs().iterator(); it.hasNext();) {
171            ProcessorDefinition<?> out = it.next();
172            if (out instanceof OnCompletionDefinition) {
173                it.remove();
174            }
175        }
176    }
177
178    @Override
179    public ProcessorDefinition<?> end() {
180        // pop parent block, as we added our self as block to parent when synchronized was defined in the route
181        getParent().popBlock();
182        return super.end();
183    }
184
185    /**
186     * Sets the mode to be after route is done (default due backwards compatible).
187     * <p/>
188     * This executes the on completion work <i>after</i> the route consumer have written response
189     * back to the callee (if its InOut mode).
190     *
191     * @return the builder
192     */
193    public OnCompletionDefinition modeAfterConsumer() {
194        setMode(OnCompletionMode.AfterConsumer);
195        return this;
196    }
197
198    /**
199     * Sets the mode to be before consumer is done.
200     * <p/>
201     * This allows the on completion work to execute <i>before</i> the route consumer, writes any response
202     * back to the callee (if its InOut mode).
203     *
204     * @return the builder
205     */
206    public OnCompletionDefinition modeBeforeConsumer() {
207        setMode(OnCompletionMode.BeforeConsumer);
208        return this;
209    }
210
211    /**
212     * Will only synchronize when the {@link org.apache.camel.Exchange} completed successfully (no errors).
213     *
214     * @return the builder
215     */
216    public OnCompletionDefinition onCompleteOnly() {
217        boolean isOnFailureOnly = getOnFailureOnly() != null && getOnFailureOnly();
218        if (isOnFailureOnly) {
219            throw new IllegalArgumentException("Both onCompleteOnly and onFailureOnly cannot be true. Only one of them can be true. On node: " + this);
220        }
221        // must define return type as OutputDefinition and not this type to avoid end user being able
222        // to invoke onFailureOnly/onCompleteOnly more than once
223        setOnCompleteOnly(Boolean.TRUE);
224        setOnFailureOnly(Boolean.FALSE);
225        return this;
226    }
227
228    /**
229     * Will only synchronize when the {@link org.apache.camel.Exchange} ended with failure (exception or FAULT message).
230     *
231     * @return the builder
232     */
233    public OnCompletionDefinition onFailureOnly() {
234        boolean isOnCompleteOnly = getOnCompleteOnly() != null && getOnCompleteOnly();
235        if (isOnCompleteOnly) {
236            throw new IllegalArgumentException("Both onCompleteOnly and onFailureOnly cannot be true. Only one of them can be true. On node: " + this);
237        }
238        // must define return type as OutputDefinition and not this type to avoid end user being able
239        // to invoke onFailureOnly/onCompleteOnly more than once
240        setOnCompleteOnly(Boolean.FALSE);
241        setOnFailureOnly(Boolean.TRUE);
242        return this;
243    }
244
245    /**
246     * Sets an additional predicate that should be true before the onCompletion is triggered.
247     * <p/>
248     * To be used for fine grained controlling whether a completion callback should be invoked or not
249     *
250     * @param predicate predicate that determines true or false
251     * @return the builder
252     */
253    public OnCompletionDefinition onWhen(@AsPredicate Predicate predicate) {
254        setOnWhen(new WhenDefinition(predicate));
255        return this;
256    }
257
258    /**
259     * Will use the original input body when an {@link org.apache.camel.Exchange} for this on completion.
260     * <p/>
261     * By default this feature is off.
262     *
263     * @return the builder
264     */
265    public OnCompletionDefinition useOriginalBody() {
266        setUseOriginalMessagePolicy(Boolean.TRUE);
267        return this;
268    }
269
270    /**
271     * To use a custom Thread Pool to be used for parallel processing.
272     * Notice if you set this option, then parallel processing is automatic implied, and you do not have to enable that option as well.
273     */
274    public OnCompletionDefinition executorService(ExecutorService executorService) {
275        setExecutorService(executorService);
276        return this;
277    }
278
279    /**
280     * Refers to a custom Thread Pool to be used for parallel processing.
281     * Notice if you set this option, then parallel processing is automatic implied, and you do not have to enable that option as well.
282     */
283    public OnCompletionDefinition executorServiceRef(String executorServiceRef) {
284        setExecutorServiceRef(executorServiceRef);
285        return this;
286    }
287
288    /**
289     * If enabled then the on completion process will run asynchronously by a separate thread from a thread pool.
290     * By default this is false, meaning the on completion process will run synchronously using the same caller thread as from the route.
291     *
292     * @return the builder
293     */
294    public OnCompletionDefinition parallelProcessing() {
295        setParallelProcessing(true);
296        return this;
297    }
298
299    /**
300     * If enabled then the on completion process will run asynchronously by a separate thread from a thread pool.
301     * By default this is false, meaning the on completion process will run synchronously using the same caller thread as from the route.
302     *
303     * @return the builder
304     */
305    public OnCompletionDefinition parallelProcessing(boolean parallelProcessing) {
306        setParallelProcessing(parallelProcessing);
307        return this;
308    }
309
310    public List<ProcessorDefinition<?>> getOutputs() {
311        return outputs;
312    }
313
314    public void setOutputs(List<ProcessorDefinition<?>> outputs) {
315        this.outputs = outputs;
316    }
317
318    public boolean isOutputSupported() {
319        return true;
320    }
321
322    public OnCompletionMode getMode() {
323        return mode;
324    }
325
326    /**
327     * Sets the on completion mode.
328     * <p/>
329     * The default value is AfterConsumer
330     */
331    public void setMode(OnCompletionMode mode) {
332        this.mode = mode;
333    }
334
335    public Boolean getOnCompleteOnly() {
336        return onCompleteOnly;
337    }
338
339    public void setOnCompleteOnly(Boolean onCompleteOnly) {
340        this.onCompleteOnly = onCompleteOnly;
341    }
342
343    public Boolean getOnFailureOnly() {
344        return onFailureOnly;
345    }
346
347    public void setOnFailureOnly(Boolean onFailureOnly) {
348        this.onFailureOnly = onFailureOnly;
349    }
350
351    public WhenDefinition getOnWhen() {
352        return onWhen;
353    }
354
355    public void setOnWhen(WhenDefinition onWhen) {
356        this.onWhen = onWhen;
357    }
358
359    public ExecutorService getExecutorService() {
360        return executorService;
361    }
362
363    public void setExecutorService(ExecutorService executorService) {
364        this.executorService = executorService;
365    }
366
367    public String getExecutorServiceRef() {
368        return executorServiceRef;
369    }
370
371    public void setExecutorServiceRef(String executorServiceRef) {
372        this.executorServiceRef = executorServiceRef;
373    }
374
375    public Boolean getUseOriginalMessagePolicy() {
376        return useOriginalMessagePolicy;
377    }
378
379    /**
380     * Will use the original input body when an {@link org.apache.camel.Exchange} for this on completion.
381     * <p/>
382     * By default this feature is off.
383     */
384    public void setUseOriginalMessagePolicy(Boolean useOriginalMessagePolicy) {
385        this.useOriginalMessagePolicy = useOriginalMessagePolicy;
386    }
387
388    public Boolean getParallelProcessing() {
389        return parallelProcessing;
390    }
391
392    public void setParallelProcessing(Boolean parallelProcessing) {
393        this.parallelProcessing = parallelProcessing;
394    }
395
396}