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    @SafeVarargs
092    public final TryDefinition doCatch(Class<? extends Throwable>... exceptionType) {
093        popBlock();
094        List<Class<? extends Throwable>> list = Arrays.asList(exceptionType);
095        CatchDefinition answer = new CatchDefinition(list);
096        addOutput(answer);
097        pushBlock(answer);
098        return this;
099    }
100
101    /**
102     * The finally block for a given handle
103     *
104     * @return the try builder
105     */
106    public TryDefinition doFinally() {
107        popBlock();
108        FinallyDefinition answer = new FinallyDefinition();
109        addOutput(answer);
110        pushBlock(answer);
111        return this;
112    }
113
114    /**
115     * Sets an additional predicate that should be true before the onCatch is
116     * triggered.
117     * <p/>
118     * To be used for fine grained controlling whether a thrown exception should
119     * be intercepted by this exception type or not.
120     *
121     * @param predicate predicate that determines true or false
122     * @return the builder
123     */
124    public TryDefinition onWhen(@AsPredicate Predicate predicate) {
125        // we must use a delegate so we can use the fluent builder based on
126        // TryDefinition
127        // to configure all with try .. catch .. finally
128        // set the onWhen predicate on all the catch definitions
129        Iterator<CatchDefinition> it = ProcessorDefinitionHelper.filterTypeInOutputs(getOutputs(), CatchDefinition.class);
130        while (it.hasNext()) {
131            CatchDefinition doCatch = it.next();
132            doCatch.setOnWhen(new WhenDefinition(predicate));
133        }
134        return this;
135    }
136
137    // Properties
138    // -------------------------------------------------------------------------
139
140    public List<CatchDefinition> getCatchClauses() {
141        if (catchClauses == null) {
142            checkInitialized();
143        }
144        return catchClauses;
145    }
146
147    public FinallyDefinition getFinallyClause() {
148        if (finallyClause == null) {
149            checkInitialized();
150        }
151        return finallyClause;
152    }
153
154    public List<ProcessorDefinition<?>> getOutputsWithoutCatches() {
155        if (outputsWithoutCatches == null) {
156            checkInitialized();
157        }
158        return outputsWithoutCatches;
159    }
160
161    @Override
162    public List<ProcessorDefinition<?>> getOutputs() {
163        return super.getOutputs();
164    }
165
166    @XmlElementRef
167    @Override
168    public void setOutputs(List<ProcessorDefinition<?>> outputs) {
169        initialized = false;
170        super.setOutputs(outputs);
171    }
172
173    @Override
174    public void addOutput(ProcessorDefinition<?> output) {
175        initialized = false;
176        super.addOutput(output);
177    }
178
179    @Override
180    public void preCreateProcessor() {
181        // force re-creating initialization to ensure its up-to-date
182        initialized = false;
183        checkInitialized();
184    }
185
186    /**
187     * Checks whether or not this object has been initialized
188     */
189    protected void checkInitialized() {
190        if (!initialized) {
191            initialized = true;
192            outputsWithoutCatches = new ArrayList<>();
193            catchClauses = new ArrayList<>();
194            finallyClause = null;
195
196            for (ProcessorDefinition<?> output : outputs) {
197                if (output instanceof CatchDefinition) {
198                    catchClauses.add((CatchDefinition)output);
199                } else if (output instanceof FinallyDefinition) {
200                    if (finallyClause != null) {
201                        throw new IllegalArgumentException("Multiple finally clauses added: " + finallyClause + " and " + output);
202                    } else {
203                        finallyClause = (FinallyDefinition)output;
204                    }
205                } else {
206                    outputsWithoutCatches.add(output);
207                }
208            }
209        }
210    }
211}