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