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.Arrays;
021import java.util.Iterator;
022import java.util.List;
023import javax.xml.bind.annotation.XmlAccessType;
024import javax.xml.bind.annotation.XmlAccessorType;
025import javax.xml.bind.annotation.XmlRootElement;
026import javax.xml.bind.annotation.XmlTransient;
027
028import org.apache.camel.Expression;
029import org.apache.camel.Predicate;
030import org.apache.camel.Processor;
031import org.apache.camel.builder.ExpressionBuilder;
032import org.apache.camel.processor.TryProcessor;
033import org.apache.camel.spi.AsPredicate;
034import org.apache.camel.spi.Metadata;
035import org.apache.camel.spi.RouteContext;
036import org.apache.camel.util.ExpressionToPredicateAdapter;
037
038/**
039 * Marks the beginning of a try, catch, finally block
040 *
041 * @version 
042 */
043@Metadata(label = "error")
044@XmlRootElement(name = "doTry")
045@XmlAccessorType(XmlAccessType.FIELD)
046public class TryDefinition extends OutputDefinition<TryDefinition> {
047    @XmlTransient
048    private List<CatchDefinition> catchClauses;
049    @XmlTransient
050    private FinallyDefinition finallyClause;
051    @XmlTransient
052    private boolean initialized;
053    @XmlTransient
054    private List<ProcessorDefinition<?>> outputsWithoutCatches;
055
056    public TryDefinition() {
057    }
058
059    @Override
060    public String toString() {
061        return "DoTry[" + getOutputs() + "]";
062    }
063
064    @Override
065    public String getLabel() {
066        return "doTry";
067    }
068
069    @Override
070    public Processor createProcessor(RouteContext routeContext) throws Exception {
071        Processor tryProcessor = createOutputsProcessor(routeContext, getOutputsWithoutCatches());
072        if (tryProcessor == null) {
073            throw new IllegalArgumentException("Definition has no children on " + this);
074        }
075
076        List<Processor> catchProcessors = new ArrayList<Processor>();
077        if (catchClauses != null) {
078            for (CatchDefinition catchClause : catchClauses) {
079                catchProcessors.add(createProcessor(routeContext, catchClause));
080            }
081        }
082
083        FinallyDefinition finallyDefinition = finallyClause;
084        if (finallyDefinition == null) {
085            finallyDefinition = new FinallyDefinition();
086            finallyDefinition.setParent(this);
087        }
088        Processor finallyProcessor = createProcessor(routeContext, finallyDefinition);
089
090        // must have either a catch or finally
091        if (finallyClause == null && catchClauses == null) {
092            throw new IllegalArgumentException("doTry must have one or more catch or finally blocks on " + this);
093        }
094
095        return new TryProcessor(tryProcessor, catchProcessors, finallyProcessor);
096    }
097
098    // Fluent API
099    // -------------------------------------------------------------------------
100
101    /**
102     * Handles the given exception
103     *
104     * @param exceptionType  the exception
105     * @return the try builder
106     */
107    @SuppressWarnings("unchecked")
108    public TryDefinition doCatch(Class<? extends Throwable> exceptionType) {
109        // this method is introduced to avoid compiler warnings about the
110        // generic Class arrays in the case we've got only one single Class
111        // to build a TryDefinition for
112        return doCatch(new Class[] {exceptionType});
113    }
114
115    /**
116     * Handles the given exception(s)
117     *
118     * @param exceptionType  the exception(s)
119     * @return the try builder
120     */
121    public TryDefinition doCatch(Class<? extends Throwable>... exceptionType) {
122        popBlock();
123        List<Class<? extends Throwable>> list = Arrays.asList(exceptionType);
124        CatchDefinition answer = new CatchDefinition(list);
125        addOutput(answer);
126        pushBlock(answer);
127        return this;
128    }
129
130    /**
131     * The finally block for a given handle
132     *
133     * @return  the try builder
134     */
135    public TryDefinition doFinally() {
136        popBlock();
137        FinallyDefinition answer = new FinallyDefinition();
138        addOutput(answer);
139        pushBlock(answer);
140        return this;
141    }
142
143    /**
144     * Sets an additional predicate that should be true before the onCatch is triggered.
145     * <p/>
146     * To be used for fine grained controlling whether a thrown exception should be intercepted
147     * by this exception type or not.
148     *
149     * @param predicate  predicate that determines true or false
150     * @return the builder
151     */
152    public TryDefinition onWhen(@AsPredicate Predicate predicate) {
153        // we must use a delegate so we can use the fluent builder based on TryDefinition
154        // to configure all with try .. catch .. finally
155        // set the onWhen predicate on all the catch definitions
156        Iterator<CatchDefinition> it = ProcessorDefinitionHelper.filterTypeInOutputs(getOutputs(), CatchDefinition.class);
157        while (it.hasNext()) {
158            CatchDefinition doCatch = it.next();
159            doCatch.setOnWhen(new WhenDefinition(predicate));
160        }
161        return this;
162    }
163
164    /**
165     * Sets whether the exchange should be marked as handled or not.
166     *
167     * @param handled  handled or not
168     * @return the builder
169     * @deprecated will be removed in Camel 3.0. Instead of using handled(false) you can re-throw the exception
170     * from a {@link Processor} or use the {@link ProcessorDefinition#throwException(Exception)}
171     */
172    @Deprecated
173    public TryDefinition handled(boolean handled) {
174        Expression expression = ExpressionBuilder.constantExpression(Boolean.toString(handled));
175        return handled(expression);
176    }
177
178    /**
179     * Sets whether the exchange should be marked as handled or not.
180     *
181     * @param handled  predicate that determines true or false
182     * @return the builder
183     * @deprecated will be removed in Camel 3.0. Instead of using handled(false) you can re-throw the exception
184     * from a {@link Processor} or use the {@link ProcessorDefinition#throwException(Exception)}
185     */
186    @Deprecated
187    public TryDefinition handled(@AsPredicate Predicate handled) {
188        // we must use a delegate so we can use the fluent builder based on TryDefinition
189        // to configure all with try .. catch .. finally
190        // set the handled on all the catch definitions
191        Iterator<CatchDefinition> it = ProcessorDefinitionHelper.filterTypeInOutputs(getOutputs(), CatchDefinition.class);
192        while (it.hasNext()) {
193            CatchDefinition doCatch = it.next();
194            doCatch.setHandledPolicy(handled);
195        }
196        return this;
197    }
198
199    /**
200     * Sets whether the exchange should be marked as handled or not.
201     *
202     * @param handled  expression that determines true or false
203     * @return the builder
204     * @deprecated will be removed in Camel 3.0. Instead of using handled(false) you can re-throw the exception
205     * from a {@link Processor} or use the {@link ProcessorDefinition#throwException(Exception)}
206     */
207    @Deprecated
208    public TryDefinition handled(@AsPredicate Expression handled) {
209        return handled(ExpressionToPredicateAdapter.toPredicate(handled));
210    }
211
212    // Properties
213    // -------------------------------------------------------------------------
214
215    public List<CatchDefinition> getCatchClauses() {
216        if (catchClauses == null) {
217            checkInitialized();
218        }
219        return catchClauses;
220    }
221
222    public FinallyDefinition getFinallyClause() {
223        if (finallyClause == null) {
224            checkInitialized();
225        }
226        return finallyClause;
227    }
228
229    public List<ProcessorDefinition<?>> getOutputsWithoutCatches() {
230        if (outputsWithoutCatches == null) {
231            checkInitialized();
232        }
233        return outputsWithoutCatches;
234    }
235
236    public void setOutputs(List<ProcessorDefinition<?>> outputs) {
237        initialized = false;
238        super.setOutputs(outputs);
239    }
240
241    @Override
242    public void addOutput(ProcessorDefinition<?> output) {
243        initialized = false;
244        super.addOutput(output);
245    }
246
247    @Override
248    protected void preCreateProcessor() {
249        // force re-creating initialization to ensure its up-to-date
250        initialized = false;
251        checkInitialized();
252    }
253
254    /**
255     * Checks whether or not this object has been initialized
256     */
257    protected void checkInitialized() {
258        if (!initialized) {
259            initialized = true;
260            outputsWithoutCatches = new ArrayList<ProcessorDefinition<?>>();
261            catchClauses = new ArrayList<CatchDefinition>();
262            finallyClause = null;
263
264            for (ProcessorDefinition<?> output : outputs) {
265                if (output instanceof CatchDefinition) {
266                    catchClauses.add((CatchDefinition)output);
267                } else if (output instanceof FinallyDefinition) {
268                    if (finallyClause != null) {
269                        throw new IllegalArgumentException("Multiple finally clauses added: " + finallyClause
270                                                           + " and " + output);
271                    } else {
272                        finallyClause = (FinallyDefinition)output;
273                    }
274                } else {
275                    outputsWithoutCatches.add(output);
276                }
277            }
278        }
279    }
280}