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;
023
024import javax.xml.bind.annotation.XmlAccessType;
025import javax.xml.bind.annotation.XmlAccessorType;
026import javax.xml.bind.annotation.XmlElementRef;
027import javax.xml.bind.annotation.XmlRootElement;
028import javax.xml.bind.annotation.XmlTransient;
029
030import org.apache.camel.Predicate;
031import org.apache.camel.spi.AsPredicate;
032import org.apache.camel.spi.Metadata;
033
034/**
035 * Marks the beginning of a try, catch, finally block
036 */
037@Metadata(label = "error")
038@XmlRootElement(name = "doTry")
039@XmlAccessorType(XmlAccessType.FIELD)
040public class TryDefinition extends OutputDefinition<TryDefinition> {
041    @XmlTransient
042    private List<CatchDefinition> catchClauses;
043    @XmlTransient
044    private FinallyDefinition finallyClause;
045    @XmlTransient
046    private boolean initialized;
047    @XmlTransient
048    private List<ProcessorDefinition<?>> outputsWithoutCatches;
049
050    public TryDefinition() {
051    }
052
053    @Override
054    public String toString() {
055        return "DoTry[" + getOutputs() + "]";
056    }
057
058    @Override
059    public String getShortName() {
060        return "doTry";
061    }
062
063    @Override
064    public String getLabel() {
065        return "doTry";
066    }
067
068    // Fluent API
069    // -------------------------------------------------------------------------
070
071    /**
072     * Handles the given exception
073     *
074     * @param exceptionType the exception
075     * @return the try builder
076     */
077    @SuppressWarnings("unchecked")
078    public TryDefinition doCatch(Class<? extends Throwable> exceptionType) {
079        // this method is introduced to avoid compiler warnings about the
080        // generic Class arrays in the case we've got only one single Class
081        // to build a TryDefinition for
082        return doCatch(new Class[] {exceptionType});
083    }
084
085    /**
086     * Handles the given exception(s)
087     *
088     * @param exceptionType the exception(s)
089     * @return the try builder
090     */
091    public TryDefinition doCatch(Class<? extends Throwable>... exceptionType) {
092        popBlock();
093        List<Class<? extends Throwable>> list = Arrays.asList(exceptionType);
094        CatchDefinition answer = new CatchDefinition(list);
095        addOutput(answer);
096        pushBlock(answer);
097        return this;
098    }
099
100    /**
101     * The finally block for a given handle
102     *
103     * @return the try builder
104     */
105    public TryDefinition doFinally() {
106        popBlock();
107        FinallyDefinition answer = new FinallyDefinition();
108        addOutput(answer);
109        pushBlock(answer);
110        return this;
111    }
112
113    /**
114     * Sets an additional predicate that should be true before the onCatch is
115     * triggered.
116     * <p/>
117     * To be used for fine grained controlling whether a thrown exception should
118     * be intercepted by this exception type or not.
119     *
120     * @param predicate predicate that determines true or false
121     * @return the builder
122     */
123    public TryDefinition onWhen(@AsPredicate Predicate predicate) {
124        // we must use a delegate so we can use the fluent builder based on
125        // TryDefinition
126        // to configure all with try .. catch .. finally
127        // set the onWhen predicate on all the catch definitions
128        Iterator<CatchDefinition> it = ProcessorDefinitionHelper.filterTypeInOutputs(getOutputs(), CatchDefinition.class);
129        while (it.hasNext()) {
130            CatchDefinition doCatch = it.next();
131            doCatch.setOnWhen(new WhenDefinition(predicate));
132        }
133        return this;
134    }
135
136    // Properties
137    // -------------------------------------------------------------------------
138
139    public List<CatchDefinition> getCatchClauses() {
140        if (catchClauses == null) {
141            checkInitialized();
142        }
143        return catchClauses;
144    }
145
146    public FinallyDefinition getFinallyClause() {
147        if (finallyClause == null) {
148            checkInitialized();
149        }
150        return finallyClause;
151    }
152
153    public List<ProcessorDefinition<?>> getOutputsWithoutCatches() {
154        if (outputsWithoutCatches == null) {
155            checkInitialized();
156        }
157        return outputsWithoutCatches;
158    }
159
160    @Override
161    public List<ProcessorDefinition<?>> getOutputs() {
162        return super.getOutputs();
163    }
164
165    @XmlElementRef
166    @Override
167    public void setOutputs(List<ProcessorDefinition<?>> outputs) {
168        initialized = false;
169        super.setOutputs(outputs);
170    }
171
172    @Override
173    public void addOutput(ProcessorDefinition<?> output) {
174        initialized = false;
175        super.addOutput(output);
176    }
177
178    @Override
179    public void preCreateProcessor() {
180        // force re-creating initialization to ensure its up-to-date
181        initialized = false;
182        checkInitialized();
183    }
184
185    /**
186     * Checks whether or not this object has been initialized
187     */
188    protected void checkInitialized() {
189        if (!initialized) {
190            initialized = true;
191            outputsWithoutCatches = new ArrayList<>();
192            catchClauses = new ArrayList<>();
193            finallyClause = null;
194
195            for (ProcessorDefinition<?> output : outputs) {
196                if (output instanceof CatchDefinition) {
197                    catchClauses.add((CatchDefinition)output);
198                } else if (output instanceof FinallyDefinition) {
199                    if (finallyClause != null) {
200                        throw new IllegalArgumentException("Multiple finally clauses added: " + finallyClause + " and " + output);
201                    } else {
202                        finallyClause = (FinallyDefinition)output;
203                    }
204                } else {
205                    outputsWithoutCatches.add(output);
206                }
207            }
208        }
209    }
210}