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