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