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