Package org.apache.camel.processor.exceptionpolicy

Source Code of org.apache.camel.processor.exceptionpolicy.DefaultExceptionPolicyStrategy

/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements.  See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License.  You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.camel.processor.exceptionpolicy;

import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import org.apache.camel.Exchange;
import org.apache.camel.model.OnExceptionDefinition;
import org.apache.camel.util.ObjectHelper;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
* The default strategy used in Camel to resolve the {@link org.apache.camel.model.OnExceptionDefinition} that should
* handle the thrown exception.
* <p/>
* <b>Selection strategy:</b>
* <br/>This strategy applies the following rules:
* <ul>
* <li>Will walk the exception hieracy from bottom upwards till the thrown exception, meaning that the most outer caused
* by is selected first, ending with the thrown exception itself. The method {@link #createExceptionIterator(Throwable)}
* provides the Iterator used for the walking.</li>
* <li>The exception type must be configured with an Exception that is an instance of the thrown exception, this
* is tested using the {@link #filter(org.apache.camel.model.OnExceptionDefinition, Class, Throwable)} method.
* By default the filter uses <tt>instanceof</tt> test.</li>
* <li>If the exception type has <b>exactly</b> the thrown exception then its selected as its an exact match</li>
* <li>Otherwise the type that has an exception that is the closets super of the thrown exception is selected
* (recurring up the exception hierarchy)</li>
* </ul>
* <p/>
* <b>Fine grained matching:</b>
* <br/> If the {@link OnExceptionDefinition} has a when defined with an expression the type is also matches against
* the current exchange using the {@link #matchesWhen(org.apache.camel.model.OnExceptionDefinition, org.apache.camel.Exchange)}
* method. This can be used to for more fine grained matching, so you can e.g. define multiple sets of
* exception types with the same exception class(es) but have a predicate attached to select which to select at runtime.
*/
public class DefaultExceptionPolicyStrategy implements ExceptionPolicyStrategy {

    private static final transient Log LOG = LogFactory.getLog(DefaultExceptionPolicyStrategy.class);

    public OnExceptionDefinition getExceptionPolicy(Map<ExceptionPolicyKey, OnExceptionDefinition> exceptionPolicices,
                                                    Exchange exchange, Throwable exception) {

        // recursive up the tree using the iterator
        Iterator<Throwable> it = createExceptionIterator(exception);
        while (it.hasNext()) {
            OnExceptionDefinition type = findMatchedExceptionPolicy(exceptionPolicices, exchange, it.next());
            if (type != null) {
                return type;
            }
        }

        // no type found
        return null;
    }


    private OnExceptionDefinition findMatchedExceptionPolicy(Map<ExceptionPolicyKey, OnExceptionDefinition> exceptionPolicices,
                                                             Exchange exchange, Throwable exception) {
        if (LOG.isTraceEnabled()) {
            LOG.trace("Finding best suited exception policy for thrown exception " + exception.getClass().getName());
        }

        // the goal is to find the exception with the same/closet inheritance level as the target exception being thrown
        int targetLevel = getInheritanceLevel(exception.getClass());
        // candidate is the best candidate found so far to return
        OnExceptionDefinition candidate = null;
        // difference in inheritance level between the current candidate and the thrown exception (target level)
        int candidateDiff = Integer.MAX_VALUE;

        // loop through all the entries and find the best candidates to use
        Set<Map.Entry<ExceptionPolicyKey, OnExceptionDefinition>> entries = exceptionPolicices.entrySet();
        for (Map.Entry<ExceptionPolicyKey, OnExceptionDefinition> entry : entries) {
            Class clazz = entry.getKey().getExceptionClass();
            OnExceptionDefinition type = entry.getValue();

            if (filter(type, clazz, exception)) {

                // must match
                if (!matchesWhen(type, exchange)) {
                    if (LOG.isTraceEnabled()) {
                        LOG.trace("The type did not match when: " + type);
                    }
                    continue;
                }

                // exact match then break
                if (clazz.equals(exception.getClass())) {
                    candidate = type;
                    break;
                }

                // not an exact match so find the best candidate
                int level = getInheritanceLevel(clazz);
                int diff = targetLevel - level;

                if (diff < candidateDiff) {
                    // replace with a much better candidate
                    candidate = type;
                    candidateDiff = diff;
                }
            }
        }

        if (LOG.isTraceEnabled()) {
            if (candidate != null) {
                LOG.trace("Using " + candidate + " as the exception policy");
            } else {
                LOG.trace("No candidate found to be used as exception policy");
            }
        }

        return candidate;
    }

    /**
     * Strategy to filter the given type exception class with the thrown exception
     *
     * @param type           the exception type
     * @param exceptionClass the current exception class for testing
     * @param exception      the thrown exception
     * @return <tt>true</tt> if the to current exception class is a candidate, <tt>false</tt> to skip it.
     */
    protected boolean filter(OnExceptionDefinition type, Class exceptionClass, Throwable exception) {
        // must be instance of check to ensure that the exceptionClass is one type of the thrown exception
        return exceptionClass.isInstance(exception);
    }

    /**
     * Strategy method for matching the exception type with the current exchange.
     * <p/>
     * This default implementation will match as:
     * <ul>
     * <li>Always true if no when predicate on the exception type
     * <li>Otherwise the when predicate is matches against the current exchange
     * </ul>
     *
     * @param definition     the exception definition
     * @param exchange the current {@link Exchange}
     * @return <tt>true</tt> if matched, <tt>false</tt> otherwise.
     */
    protected boolean matchesWhen(OnExceptionDefinition definition, Exchange exchange) {
        if (definition.getOnWhen() == null || definition.getOnWhen().getExpression() == null) {
            // if no predicate then it's always a match
            return true;
        }
        return definition.getOnWhen().getExpression().matches(exchange);
    }

    /**
     * Strategy method creating the iterator to walk the exception in the order Camel should use
     * for find the {@link OnExceptionDefinition} should be used.
     * <p/>
     * The default iterator will walk from the bottom upwards
     * (the last caused by going upwards to the exception)
     *
     * @param exception  the exception
     * @return the iterator
     */
    protected Iterator<Throwable> createExceptionIterator(Throwable exception) {
        return ObjectHelper.createExceptionIterator(exception);
    }

    private static int getInheritanceLevel(Class clazz) {
        if (clazz == null || "java.lang.Object".equals(clazz.getName())) {
            return 0;
        }
        return 1 + getInheritanceLevel(clazz.getSuperclass());
    }

}
TOP

Related Classes of org.apache.camel.processor.exceptionpolicy.DefaultExceptionPolicyStrategy

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.