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