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.processor;
018    
019    import java.util.Iterator;
020    import java.util.List;
021    
022    import org.apache.camel.Exchange;
023    import org.apache.camel.Predicate;
024    import org.apache.camel.Processor;
025    import org.apache.camel.Traceable;
026    import org.apache.camel.util.ObjectHelper;
027    
028    /**
029     * A processor which catches exceptions.
030     *
031     * @version 
032     */
033    public class CatchProcessor extends DelegateAsyncProcessor implements Traceable {
034        private final List<Class<? extends Throwable>> exceptions;
035        private final Predicate onWhen;
036        private final Predicate handled;
037    
038        public CatchProcessor(List<Class<? extends Throwable>> exceptions, Processor processor, Predicate onWhen, Predicate handled) {
039            super(processor);
040            this.exceptions = exceptions;
041            this.onWhen = onWhen;
042            this.handled = handled;
043        }
044    
045        @Override
046        public String toString() {
047            return "Catch[" + exceptions + " -> " + getProcessor() + "]";
048        }
049    
050        public String getTraceLabel() {
051            return "catch";
052        }
053    
054        /**
055         * Returns with the exception that is caught by this processor.
056         * 
057         * This method traverses exception causes, so sometimes the exception
058         * returned from this method might be one of causes of the parameter
059         * passed.
060         *
061         * @param exchange  the current exchange
062         * @param exception the thrown exception
063         * @return Throwable that this processor catches. <tt>null</tt> if nothing matches.
064         */
065        public Throwable catches(Exchange exchange, Throwable exception) {
066            // use the exception iterator to walk the caused by hierarchy
067            Iterator<Throwable> it = ObjectHelper.createExceptionIterator(exception);
068            while (it.hasNext()) {
069                Throwable e = it.next();
070                // see if we catch this type
071                for (Class<?> type : exceptions) {
072                    if (type.isInstance(e) && matchesWhen(exchange)) {
073                        return e;
074                    }
075                }
076            }
077    
078            // not found
079            return null;
080        }
081    
082    
083        /**
084         * Whether this catch processor handles the exception it have caught
085         *
086         * @param exchange  the current exchange
087         * @return <tt>true</tt> if this processor handles it, <tt>false</tt> otherwise.
088         */
089        public boolean handles(Exchange exchange) {
090            if (handled == null) {
091                // handle by default
092                return true;
093            }
094    
095            return handled.matches(exchange);
096        }
097    
098        public List<Class<? extends Throwable>> getExceptions() {
099            return exceptions;
100        }
101    
102        /**
103         * Strategy method for matching the exception type with the current exchange.
104         * <p/>
105         * This default implementation will match as:
106         * <ul>
107         *   <li>Always true if no when predicate on the exception type
108         *   <li>Otherwise the when predicate is matches against the current exchange
109         * </ul>
110         *
111         * @param exchange the current {@link org.apache.camel.Exchange}
112         * @return <tt>true</tt> if matched, <tt>false</tt> otherwise.
113         */
114        protected boolean matchesWhen(Exchange exchange) {
115            if (onWhen == null) {
116                // if no predicate then it's always a match
117                return true;
118            }
119            return onWhen.matches(exchange);
120        }
121    }