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