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.reifier;
018
019import java.util.ArrayList;
020import java.util.List;
021
022import org.apache.camel.Predicate;
023import org.apache.camel.Processor;
024import org.apache.camel.builder.ErrorHandlerBuilder;
025import org.apache.camel.model.OnExceptionDefinition;
026import org.apache.camel.model.ProcessorDefinition;
027import org.apache.camel.processor.CatchProcessor;
028import org.apache.camel.processor.FatalFallbackErrorHandler;
029import org.apache.camel.spi.ClassResolver;
030import org.apache.camel.spi.RouteContext;
031import org.apache.camel.support.CamelContextHelper;
032import org.apache.camel.util.ObjectHelper;
033
034public class OnExceptionReifier extends ProcessorReifier<OnExceptionDefinition> {
035
036    public OnExceptionReifier(ProcessorDefinition<?> definition) {
037        super((OnExceptionDefinition)definition);
038    }
039
040    @Override
041    public void addRoutes(RouteContext routeContext) throws Exception {
042        // assign whether this was a route scoped onException or not
043        // we need to know this later when setting the parent, as only route
044        // scoped should have parent
045        // Note: this logic can possible be removed when the Camel routing
046        // engine decides at runtime
047        // to apply onException in a more dynamic fashion than current code base
048        // and therefore is in a better position to decide among context/route
049        // scoped OnException at runtime
050        if (definition.getRouteScoped() == null) {
051            definition.setRouteScoped(definition.getParent() != null);
052        }
053
054        setHandledFromExpressionType(routeContext);
055        setContinuedFromExpressionType(routeContext);
056        setRetryWhileFromExpressionType(routeContext);
057        setOnRedeliveryFromRedeliveryRef(routeContext);
058        setOnExceptionOccurredFromOnExceptionOccurredRef(routeContext);
059
060        // must validate configuration before creating processor
061        definition.validateConfiguration();
062
063        if (definition.getUseOriginalMessagePolicy() != null && definition.getUseOriginalMessagePolicy()) {
064            // ensure allow original is turned on
065            routeContext.setAllowUseOriginalMessage(true);
066        }
067
068        // lets attach this on exception to the route error handler
069        Processor child = createOutputsProcessor(routeContext);
070        if (child != null) {
071            // wrap in our special safe fallback error handler if OnException
072            // have child output
073            Processor errorHandler = new FatalFallbackErrorHandler(child);
074            String id = getId(definition, routeContext);
075            routeContext.setOnException(id, errorHandler);
076        }
077        // lookup the error handler builder
078        ErrorHandlerBuilder builder = (ErrorHandlerBuilder)routeContext.getErrorHandlerFactory();
079        // and add this as error handlers
080        routeContext.addErrorHandler(builder, definition);
081    }
082
083    @Override
084    public CatchProcessor createProcessor(RouteContext routeContext) throws Exception {
085        // load exception classes
086        List<Class<? extends Throwable>> classes = null;
087        if (definition.getExceptions() != null && !definition.getExceptions().isEmpty()) {
088            classes = createExceptionClasses(routeContext.getCamelContext().getClassResolver());
089        }
090
091        if (definition.getUseOriginalMessagePolicy() != null && definition.getUseOriginalMessagePolicy()) {
092            // ensure allow original is turned on
093            routeContext.setAllowUseOriginalMessage(true);
094        }
095
096        // must validate configuration before creating processor
097        definition.validateConfiguration();
098
099        Processor childProcessor = this.createChildProcessor(routeContext, false);
100
101        Predicate when = null;
102        if (definition.getOnWhen() != null) {
103            when = definition.getOnWhen().getExpression().createPredicate(routeContext);
104        }
105
106        Predicate handle = null;
107        if (definition.getHandled() != null) {
108            handle = definition.getHandled().createPredicate(routeContext);
109        }
110
111        return new CatchProcessor(classes, childProcessor, when, handle);
112    }
113
114    protected List<Class<? extends Throwable>> createExceptionClasses(ClassResolver resolver) throws ClassNotFoundException {
115        List<String> list = definition.getExceptions();
116        List<Class<? extends Throwable>> answer = new ArrayList<>(list.size());
117        for (String name : list) {
118            Class<? extends Throwable> type = resolver.resolveMandatoryClass(name, Throwable.class);
119            answer.add(type);
120        }
121        return answer;
122    }
123
124    private void setHandledFromExpressionType(RouteContext routeContext) {
125        if (definition.getHandled() != null && definition.getHandledPolicy() == null && routeContext != null) {
126            definition.handled(definition.getHandled().createPredicate(routeContext));
127        }
128    }
129
130    private void setContinuedFromExpressionType(RouteContext routeContext) {
131        if (definition.getContinued() != null && definition.getContinuedPolicy() == null && routeContext != null) {
132            definition.continued(definition.getContinued().createPredicate(routeContext));
133        }
134    }
135
136    private void setRetryWhileFromExpressionType(RouteContext routeContext) {
137        if (definition.getRetryWhile() != null && definition.getRetryWhilePolicy() == null && routeContext != null) {
138            definition.retryWhile(definition.getRetryWhile().createPredicate(routeContext));
139        }
140    }
141
142    private void setOnRedeliveryFromRedeliveryRef(RouteContext routeContext) {
143        // lookup onRedelivery if ref is provided
144        if (ObjectHelper.isNotEmpty(definition.getOnRedeliveryRef())) {
145            // if ref is provided then use mandatory lookup to fail if not found
146            Processor onRedelivery = CamelContextHelper.mandatoryLookup(routeContext.getCamelContext(), definition.getOnRedeliveryRef(), Processor.class);
147            definition.setOnRedelivery(onRedelivery);
148        }
149    }
150
151    private void setOnExceptionOccurredFromOnExceptionOccurredRef(RouteContext routeContext) {
152        // lookup onRedelivery if ref is provided
153        if (ObjectHelper.isNotEmpty(definition.getOnExceptionOccurredRef())) {
154            // if ref is provided then use mandatory lookup to fail if not found
155            Processor onExceptionOccurred = CamelContextHelper.mandatoryLookup(routeContext.getCamelContext(), definition.getOnExceptionOccurredRef(), Processor.class);
156            definition.setOnExceptionOccurred(onExceptionOccurred);
157        }
158    }
159
160}