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     */
017    package org.apache.camel.model;
018    
019    import java.util.ArrayList;
020    import java.util.Arrays;
021    import java.util.Iterator;
022    import java.util.List;
023    
024    import javax.xml.bind.annotation.XmlAccessType;
025    import javax.xml.bind.annotation.XmlAccessorType;
026    import javax.xml.bind.annotation.XmlRootElement;
027    import javax.xml.bind.annotation.XmlTransient;
028    
029    import org.apache.camel.Expression;
030    import org.apache.camel.Predicate;
031    import org.apache.camel.Processor;
032    import org.apache.camel.builder.ExpressionBuilder;
033    import org.apache.camel.builder.ExpressionClause;
034    import org.apache.camel.processor.CatchProcessor;
035    import org.apache.camel.processor.TryProcessor;
036    import org.apache.camel.spi.RouteContext;
037    import org.apache.camel.util.CastUtils;
038    
039    import static org.apache.camel.builder.PredicateBuilder.toPredicate;
040    
041    /**
042     * Represents an XML <try/> element
043     *
044     * @version $Revision: 883288 $
045     */
046    @XmlRootElement(name = "doTry")
047    @XmlAccessorType(XmlAccessType.FIELD)
048    public class TryDefinition extends OutputDefinition<TryDefinition> {
049        @XmlTransient
050        private List<CatchDefinition> catchClauses;
051        @XmlTransient
052        private FinallyDefinition finallyClause;
053        @XmlTransient
054        private boolean initialized;
055        @XmlTransient
056        private List<ProcessorDefinition> outputsWithoutCatches;
057    
058        @Override
059        public String toString() {
060            return "DoTry[" + getOutputs() + "]";
061        }
062    
063        @Override
064        public String getShortName() {
065            return "doTry";
066        }
067    
068        @Override
069        public String getLabel() {
070            return "doTry";
071        }
072    
073        @Override
074        public Processor createProcessor(RouteContext routeContext) throws Exception {
075            Processor tryProcessor = createOutputsProcessor(routeContext, getOutputsWithoutCatches());
076    
077            Processor finallyProcessor = null;
078            if (finallyClause != null) {
079                finallyProcessor = finallyClause.createProcessor(routeContext);
080            }
081    
082            List<CatchProcessor> catchProcessors = new ArrayList<CatchProcessor>();
083            if (catchClauses != null) {
084                for (CatchDefinition catchClause : catchClauses) {
085                    catchProcessors.add(catchClause.createProcessor(routeContext));
086                }
087            }
088    
089            return new TryProcessor(tryProcessor, catchProcessors, finallyProcessor);
090        }
091    
092        // Fluent API
093        // -------------------------------------------------------------------------
094    
095        /**
096         * Handles the given exception(s)
097         *
098         * @param exceptionType  the exception(s)
099         * @return the try builder
100         */
101        public TryDefinition doCatch(Class... exceptionType) {
102            popBlock();
103            List<Class> list = CastUtils.cast(Arrays.asList(exceptionType));
104            CatchDefinition answer = new CatchDefinition(list);
105            addOutput(answer);
106            pushBlock(answer);
107            return this;
108        }
109    
110        /**
111         * The finally block for a given handle
112         *
113         * @return  the try builder
114         */
115        public TryDefinition doFinally() {
116            popBlock();
117            FinallyDefinition answer = new FinallyDefinition();
118            addOutput(answer);
119            pushBlock(answer);
120            return this;
121        }
122    
123        /**
124         * Sets an additional predicate that should be true before the onCatch is triggered.
125         * <p/>
126         * To be used for fine grained controlling whether a thrown exception should be intercepted
127         * by this exception type or not.
128         *
129         * @param predicate  predicate that determines true or false
130         * @return the builder
131         */
132        public TryDefinition onWhen(Predicate predicate) {
133            // we must use a delegate so we can use the fluent builder based on TryDefinition
134            // to configure all with try .. catch .. finally
135            // set the onWhen predicate on all the catch definitions
136            Iterator<CatchDefinition> it = ProcessorDefinitionHelper.filterTypeInOutputs(getOutputs(), CatchDefinition.class);
137            while (it.hasNext()) {
138                CatchDefinition doCatch = it.next();
139                doCatch.setOnWhen(new WhenDefinition(predicate));
140            }
141            return this;
142        }
143    
144        /**
145         * Creates an expression to configure an additional predicate that should be true before the
146         * onCatch is triggered.
147         * <p/>
148         * To be used for fine grained controlling whether a thrown exception should be intercepted
149         * by this exception type or not.
150         *
151         * @return the expression clause to configure
152         */
153        public ExpressionClause<TryDefinition> onWhen() {
154            // we must use a delegate so we can use the fluent builder based on TryDefinition
155            // to configure all with try .. catch .. finally
156            WhenDefinition answer = new WhenDefinition();
157            // set the onWhen definition on all the catch definitions
158            Iterator<CatchDefinition> it = ProcessorDefinitionHelper.filterTypeInOutputs(getOutputs(), CatchDefinition.class);
159            while (it.hasNext()) {
160                CatchDefinition doCatch = it.next();
161                doCatch.setOnWhen(answer);
162            }
163            // return a expression clause as builder to set the predicate on the onWhen definition
164            ExpressionClause<TryDefinition> clause = new ExpressionClause<TryDefinition>(this);
165            answer.setExpression(clause);
166            return clause;
167        }
168    
169        /**
170         * Sets whether the exchange should be marked as handled or not.
171         *
172         * @param handled  handled or not
173         * @return the builder
174         */
175        public TryDefinition handled(boolean handled) {
176            Expression expression = ExpressionBuilder.constantExpression(Boolean.toString(handled));
177            return handled(expression);
178        }
179    
180        /**
181         * Sets whether the exchange should be marked as handled or not.
182         *
183         * @param handled  predicate that determines true or false
184         * @return the builder
185         */
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         */
204        public TryDefinition handled(Expression handled) {
205            return handled(toPredicate(handled));
206        }
207    
208        // Properties
209        // -------------------------------------------------------------------------
210    
211        public List<CatchDefinition> getCatchClauses() {
212            if (catchClauses == null) {
213                checkInitialized();
214            }
215            return catchClauses;
216        }
217    
218        public FinallyDefinition getFinallyClause() {
219            if (finallyClause == null) {
220                checkInitialized();
221            }
222            return finallyClause;
223        }
224    
225        public List<ProcessorDefinition> getOutputsWithoutCatches() {
226            if (outputsWithoutCatches == null) {
227                checkInitialized();
228            }
229            return outputsWithoutCatches;
230        }
231    
232        public void setOutputs(List<ProcessorDefinition> outputs) {
233            initialized = false;
234            super.setOutputs(outputs);
235        }
236    
237        @Override
238        public void addOutput(ProcessorDefinition output) {
239            initialized = false;
240            super.addOutput(output);
241        }
242    
243        /**
244         * Checks whether or not this object has been initialized
245         */
246        protected void checkInitialized() {
247            if (!initialized) {
248                initialized = true;
249                outputsWithoutCatches = new ArrayList<ProcessorDefinition>();
250                catchClauses = new ArrayList<CatchDefinition>();
251                finallyClause = null;
252    
253                for (ProcessorDefinition output : outputs) {
254                    if (output instanceof CatchDefinition) {
255                        catchClauses.add((CatchDefinition)output);
256                    } else if (output instanceof FinallyDefinition) {
257                        if (finallyClause != null) {
258                            throw new IllegalArgumentException("Multiple finally clauses added: " + finallyClause
259                                                               + " and " + output);
260                        } else {
261                            finallyClause = (FinallyDefinition)output;
262                        }
263                    } else {
264                        outputsWithoutCatches.add(output);
265                    }
266                }
267            }
268        }
269    }