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.List;
021import javax.xml.bind.annotation.XmlAccessType;
022import javax.xml.bind.annotation.XmlAccessorType;
023import javax.xml.bind.annotation.XmlElement;
024import javax.xml.bind.annotation.XmlElementRef;
025import javax.xml.bind.annotation.XmlRootElement;
026import javax.xml.bind.annotation.XmlTransient;
027
028import org.apache.camel.CamelContext;
029import org.apache.camel.Expression;
030import org.apache.camel.Predicate;
031import org.apache.camel.Processor;
032import org.apache.camel.builder.ExpressionBuilder;
033import org.apache.camel.processor.CatchProcessor;
034import org.apache.camel.spi.Metadata;
035import org.apache.camel.spi.RouteContext;
036import org.apache.camel.util.ExpressionToPredicateAdapter;
037
038/**
039 * Catches exceptions as part of a try, catch, finally block
040 *
041 * @version 
042 */
043@Metadata(label = "error")
044@XmlRootElement(name = "doCatch")
045@XmlAccessorType(XmlAccessType.FIELD)
046public class CatchDefinition extends ProcessorDefinition<CatchDefinition> {
047    @XmlElement(name = "exception")
048    private List<String> exceptions = new ArrayList<String>();
049    @XmlElement(name = "onWhen")
050    private WhenDefinition onWhen;
051    @XmlElement(name = "handled")
052    private ExpressionSubElementDefinition handled;
053    @XmlElementRef
054    private List<ProcessorDefinition<?>> outputs = new ArrayList<ProcessorDefinition<?>>();
055    @XmlTransient
056    private List<Class<? extends Throwable>> exceptionClasses;
057    @XmlTransient
058    private Predicate handledPolicy;
059
060    public CatchDefinition() {
061    }
062
063    public CatchDefinition(List<Class<? extends Throwable>> exceptionClasses) {
064        this.exceptionClasses = exceptionClasses;
065    }
066
067    public CatchDefinition(Class<? extends Throwable> exceptionType) {
068        exceptionClasses = new ArrayList<Class<? extends Throwable>>();
069        exceptionClasses.add(exceptionType);
070    }
071
072    @Override
073    public String toString() {
074        return "DoCatch[ " + getExceptionClasses() + " -> " + getOutputs() + "]";
075    }
076
077    @Override
078    public String getLabel() {
079        return "doCatch[ " + getExceptionClasses() + "]";
080    }
081
082    @Override
083    public CatchProcessor createProcessor(RouteContext routeContext) throws Exception {
084        // create and load exceptions if not done
085        if (exceptionClasses == null) {
086            exceptionClasses = createExceptionClasses(routeContext.getCamelContext());
087        }
088
089        // must have at least one exception
090        if (exceptionClasses.isEmpty()) {
091            throw new IllegalArgumentException("At least one Exception must be configured to catch");
092        }
093
094        // parent must be a try
095        if (!(getParent() instanceof TryDefinition)) {
096            throw new IllegalArgumentException("This doCatch should have a doTry as its parent on " + this);
097        }
098
099        // do catch does not mandate a child processor
100        Processor childProcessor = this.createChildProcessor(routeContext, false);
101
102        Predicate when = null;
103        if (onWhen != null) {
104            when = onWhen.getExpression().createPredicate(routeContext);
105        }
106
107        Predicate handle = handledPolicy;
108        if (handled != null) {
109            handle = handled.createPredicate(routeContext);
110        }
111
112        return new CatchProcessor(exceptionClasses, childProcessor, when, handle);
113    }
114
115    @Override
116    public List<ProcessorDefinition<?>> getOutputs() {
117        return outputs;
118    }
119
120    public void setOutputs(List<ProcessorDefinition<?>> outputs) {
121        this.outputs = outputs;
122    }
123
124    public boolean isOutputSupported() {
125        return true;
126    }
127
128    public List<Class<? extends Throwable>> getExceptionClasses() {
129        return exceptionClasses;
130    }
131
132    public void setExceptionClasses(List<Class<? extends Throwable>> exceptionClasses) {
133        this.exceptionClasses = exceptionClasses;
134    }
135    
136    // Fluent API
137    //-------------------------------------------------------------------------
138    /**
139     * The exceptions to catch.
140     *
141     * @param exceptionClasses  a list of the exception classes
142     * @return the builder
143     */
144    public CatchDefinition exceptionClasses(List<Class<? extends Throwable>> exceptionClasses) {
145        setExceptionClasses(exceptionClasses);
146        return this;
147    }
148
149    /**
150     * The exception(s) to catch.
151     *
152     * @param exceptions  one or more exceptions
153     * @return the builder
154     */
155    public CatchDefinition exception(Class<? extends Throwable>... exceptions) {
156        if (exceptionClasses == null) {
157            exceptionClasses = new ArrayList<Class<? extends Throwable>>();
158        }
159        if (exceptions != null) {
160            for (Class<? extends Throwable> exception : exceptions) {
161                exceptionClasses.add(exception);
162            }
163        }
164        return this;
165    }
166    
167    /**
168     * Sets an additional predicate that should be true before the onCatch is triggered.
169     * <p/>
170     * To be used for fine grained controlling whether a thrown exception should be intercepted
171     * by this exception type or not.
172     *
173     * @param predicate  predicate that determines true or false
174     * @return the builder
175     */
176    public CatchDefinition onWhen(Predicate predicate) {
177        setOnWhen(new WhenDefinition(predicate));
178        return this;
179    }
180
181    /**
182     * Sets whether the exchange should be marked as handled or not.
183     *
184     * @param handled  handled or not
185     * @return the builder
186     * @deprecated will be removed in Camel 3.0. Instead of using handled(false) you can re-throw the exception
187     * from a {@link Processor} or use the {@link ProcessorDefinition#throwException(Exception)}
188     */
189    @Deprecated
190    public CatchDefinition handled(boolean handled) {
191        Expression expression = ExpressionBuilder.constantExpression(Boolean.toString(handled));
192        return handled(expression);
193    }
194
195    /**
196     * Sets whether the exchange should be marked as handled or not.
197     *
198     * @param handled  predicate that determines true or false
199     * @return the builder
200     * @deprecated will be removed in Camel 3.0. Instead of using handled(false) you can re-throw the exception
201     * from a {@link Processor} or use the {@link ProcessorDefinition#throwException(Exception)}
202     */
203    @Deprecated
204    public CatchDefinition handled(Predicate handled) {
205        setHandledPolicy(handled);
206        return this;
207    }
208
209    /**
210     * Sets whether the exchange should be marked as handled or not.
211     *
212     * @param handled  expression that determines true or false
213     * @return the builder
214     * @deprecated will be removed in Camel 3.0. Instead of using handled(false) you can re-throw the exception
215     * from a {@link Processor} or use the {@link ProcessorDefinition#throwException(Exception)}
216     */
217    @Deprecated
218    public CatchDefinition handled(Expression handled) {
219        setHandledPolicy(ExpressionToPredicateAdapter.toPredicate(handled));
220        return this;
221    }
222
223    /**
224     * Sets the exception class that the CatchType want to catch
225     *
226     * @param exception  the exception of class
227     * @return the builder
228     */
229    public CatchDefinition exceptionClasses(Class<? extends Throwable> exception) {
230        List<Class<? extends Throwable>> list = getExceptionClasses();
231        list.add(exception);
232        return this;
233    }
234
235    public List<String> getExceptions() {
236        return exceptions;
237    }
238
239    public void setExceptions(List<String> exceptions) {
240        this.exceptions = exceptions;
241    }
242
243    public WhenDefinition getOnWhen() {
244        return onWhen;
245    }
246
247    public void setOnWhen(WhenDefinition onWhen) {
248        this.onWhen = onWhen;
249    }
250
251    public Predicate getHandledPolicy() {
252        return handledPolicy;
253    }
254
255    public void setHandledPolicy(Predicate handledPolicy) {
256        this.handledPolicy = handledPolicy;
257    }
258
259    public ExpressionSubElementDefinition getHandled() {
260        return handled;
261    }
262
263    public void setHandled(ExpressionSubElementDefinition handled) {
264        this.handled = handled;
265    }
266
267    protected List<Class<? extends Throwable>> createExceptionClasses(CamelContext context) throws ClassNotFoundException {
268        // must use the class resolver from CamelContext to load classes to ensure it can
269        // be loaded in all kind of environments such as JEE servers and OSGi etc.
270        List<String> list = getExceptions();
271        List<Class<? extends Throwable>> answer = new ArrayList<Class<? extends Throwable>>(list.size());
272        for (String name : list) {
273            Class<Throwable> type = context.getClassResolver().resolveMandatoryClass(name, Throwable.class);
274            answer.add(type);
275        }
276        return answer;
277    }
278}