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