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