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.errorhandler;
018
019import java.util.HashMap;
020import java.util.Map;
021import java.util.function.BiFunction;
022
023import org.apache.camel.CamelContext;
024import org.apache.camel.ErrorHandlerFactory;
025import org.apache.camel.ExtendedCamelContext;
026import org.apache.camel.LoggingLevel;
027import org.apache.camel.NamedNode;
028import org.apache.camel.Predicate;
029import org.apache.camel.Processor;
030import org.apache.camel.RuntimeCamelException;
031import org.apache.camel.builder.DeadLetterChannelBuilder;
032import org.apache.camel.builder.DefaultErrorHandlerBuilder;
033import org.apache.camel.builder.ErrorHandlerBuilder;
034import org.apache.camel.builder.ErrorHandlerBuilderRef;
035import org.apache.camel.builder.ErrorHandlerBuilderSupport;
036import org.apache.camel.builder.NoErrorHandlerBuilder;
037import org.apache.camel.model.OnExceptionDefinition;
038import org.apache.camel.model.RedeliveryPolicyDefinition;
039import org.apache.camel.model.RouteDefinition;
040import org.apache.camel.processor.ErrorHandler;
041import org.apache.camel.processor.errorhandler.ErrorHandlerSupport;
042import org.apache.camel.processor.errorhandler.ExceptionPolicy;
043import org.apache.camel.processor.errorhandler.ExceptionPolicy.RedeliveryOption;
044import org.apache.camel.processor.errorhandler.RedeliveryErrorHandler;
045import org.apache.camel.processor.errorhandler.RedeliveryPolicy;
046import org.apache.camel.reifier.AbstractReifier;
047import org.apache.camel.reifier.language.ExpressionReifier;
048import org.apache.camel.spi.RouteContext;
049import org.apache.camel.support.CamelContextHelper;
050import org.apache.camel.util.ObjectHelper;
051
052public abstract class ErrorHandlerReifier<T extends ErrorHandlerBuilderSupport> extends AbstractReifier {
053
054    public static final String DEFAULT_ERROR_HANDLER_BUILDER = "CamelDefaultErrorHandlerBuilder";
055    private static final Map<Class<?>, BiFunction<RouteContext, ErrorHandlerFactory, ErrorHandlerReifier<? extends ErrorHandlerFactory>>> ERROR_HANDLERS;
056    static {
057        Map<Class<?>, BiFunction<RouteContext, ErrorHandlerFactory, ErrorHandlerReifier<? extends ErrorHandlerFactory>>> map = new HashMap<>();
058        map.put(DeadLetterChannelBuilder.class, DeadLetterChannelReifier::new);
059        map.put(DefaultErrorHandlerBuilder.class, DefaultErrorHandlerReifier::new);
060        map.put(ErrorHandlerBuilderRef.class, ErrorHandlerRefReifier::new);
061        map.put(NoErrorHandlerBuilder.class, NoErrorHandlerReifier::new);
062        ERROR_HANDLERS = map;
063    }
064
065    protected T definition;
066
067    /**
068     * Utility classes should not have a public constructor.
069     */
070    ErrorHandlerReifier(RouteContext routeContext, T definition) {
071        super(routeContext);
072        this.definition = definition;
073    }
074
075    public static void registerReifier(Class<?> errorHandlerClass, BiFunction<RouteContext, ErrorHandlerFactory, ErrorHandlerReifier<? extends ErrorHandlerFactory>> creator) {
076        ERROR_HANDLERS.put(errorHandlerClass, creator);
077    }
078
079    public static ErrorHandlerReifier<? extends ErrorHandlerFactory> reifier(RouteContext routeContext, ErrorHandlerFactory definition) {
080        BiFunction<RouteContext, ErrorHandlerFactory, ErrorHandlerReifier<? extends ErrorHandlerFactory>> reifier = ERROR_HANDLERS.get(definition.getClass());
081        if (reifier != null) {
082            return reifier.apply(routeContext, definition);
083        } else if (definition instanceof ErrorHandlerBuilderSupport) {
084            return new ErrorHandlerReifier<ErrorHandlerBuilderSupport>(routeContext, (ErrorHandlerBuilderSupport)definition) {
085                @Override
086                public Processor createErrorHandler(Processor processor) throws Exception {
087                    return definition.createErrorHandler(routeContext, processor);
088                }
089            };
090        } else {
091            throw new IllegalStateException("Unsupported definition: " + definition);
092        }
093    }
094
095    public static ExceptionPolicy createExceptionPolicy(OnExceptionDefinition def, CamelContext camelContext) {
096        Predicate handled = def.getHandledPolicy();
097        if (handled == null && def.getHandled() != null) {
098            handled = ExpressionReifier.reifier(camelContext, def.getHandled()).createPredicate();
099        }
100        Predicate continued = def.getContinuedPolicy();
101        if (continued == null && def.getContinued() != null) {
102            continued = ExpressionReifier.reifier(camelContext, def.getContinued()).createPredicate();
103        }
104        Predicate retryWhile = def.getRetryWhilePolicy();
105        if (retryWhile == null && def.getRetryWhile() != null) {
106            retryWhile = ExpressionReifier.reifier(camelContext, def.getRetryWhile()).createPredicate();
107        }
108        Processor onRedelivery = def.getOnRedelivery();
109        if (onRedelivery == null && def.getOnRedeliveryRef() != null) {
110            onRedelivery = CamelContextHelper.mandatoryLookup(camelContext,
111                    CamelContextHelper.parseText(camelContext, def.getOnRedeliveryRef()), Processor.class);
112        }
113        Processor onExceptionOccurred = def.getOnExceptionOccurred();
114        if (onExceptionOccurred == null && def.getOnExceptionOccurredRef() != null) {
115            onExceptionOccurred = CamelContextHelper.mandatoryLookup(camelContext,
116                    CamelContextHelper.parseText(camelContext, def.getOnExceptionOccurredRef()), Processor.class);
117        }
118        return new ExceptionPolicy(def.getId(), CamelContextHelper.getRouteId(def),
119                                   def.getUseOriginalMessage() != null && CamelContextHelper.parseBoolean(camelContext, def.getUseOriginalMessage()),
120                                   def.getUseOriginalBody() != null && CamelContextHelper.parseBoolean(camelContext, def.getUseOriginalBody()),
121                                   ObjectHelper.isNotEmpty(def.getOutputs()), handled,
122                                   continued, retryWhile, onRedelivery,
123                                   onExceptionOccurred, def.getRedeliveryPolicyRef(),
124                                   getRedeliveryPolicy(def.getRedeliveryPolicyType()), def.getExceptions());
125    }
126
127    private static Map<RedeliveryOption, String> getRedeliveryPolicy(RedeliveryPolicyDefinition definition) {
128        if (definition == null) {
129            return null;
130        }
131        Map<RedeliveryOption, String> policy = new HashMap<>();
132        setOption(policy, RedeliveryOption.maximumRedeliveries, definition.getMaximumRedeliveries());
133        setOption(policy, RedeliveryOption.redeliveryDelay, definition.getRedeliveryDelay());
134        setOption(policy, RedeliveryOption.asyncDelayedRedelivery, definition.getAsyncDelayedRedelivery());
135        setOption(policy, RedeliveryOption.backOffMultiplier, definition.getBackOffMultiplier());
136        setOption(policy, RedeliveryOption.useExponentialBackOff, definition.getUseExponentialBackOff());
137        setOption(policy, RedeliveryOption.collisionAvoidanceFactor, definition.getCollisionAvoidanceFactor());
138        setOption(policy, RedeliveryOption.useCollisionAvoidance, definition.getUseCollisionAvoidance());
139        setOption(policy, RedeliveryOption.maximumRedeliveryDelay, definition.getMaximumRedeliveryDelay());
140        setOption(policy, RedeliveryOption.retriesExhaustedLogLevel, definition.getRetriesExhaustedLogLevel());
141        setOption(policy, RedeliveryOption.retryAttemptedLogLevel, definition.getRetryAttemptedLogLevel());
142        setOption(policy, RedeliveryOption.retryAttemptedLogInterval, definition.getRetryAttemptedLogInterval());
143        setOption(policy, RedeliveryOption.logRetryAttempted, definition.getLogRetryAttempted());
144        setOption(policy, RedeliveryOption.logStackTrace, definition.getLogStackTrace());
145        setOption(policy, RedeliveryOption.logRetryStackTrace, definition.getLogRetryStackTrace());
146        setOption(policy, RedeliveryOption.logHandled, definition.getLogHandled());
147        setOption(policy, RedeliveryOption.logNewException, definition.getLogNewException());
148        setOption(policy, RedeliveryOption.logContinued, definition.getLogContinued());
149        setOption(policy, RedeliveryOption.logExhausted, definition.getLogExhausted());
150        setOption(policy, RedeliveryOption.logExhaustedMessageHistory, definition.getLogExhaustedMessageHistory());
151        setOption(policy, RedeliveryOption.logExhaustedMessageBody, definition.getLogExhaustedMessageBody());
152        setOption(policy, RedeliveryOption.disableRedelivery, definition.getDisableRedelivery());
153        setOption(policy, RedeliveryOption.delayPattern, definition.getDelayPattern());
154        setOption(policy, RedeliveryOption.allowRedeliveryWhileStopping, definition.getAllowRedeliveryWhileStopping());
155        setOption(policy, RedeliveryOption.exchangeFormatterRef, definition.getExchangeFormatterRef());
156        return policy;
157    }
158
159    private static void setOption(Map<RedeliveryOption, String> policy, RedeliveryOption option, Object value) {
160        if (value != null) {
161            policy.put(option, value.toString());
162        }
163    }
164
165    /**
166     * Lookup the error handler by the given ref
167     *
168     * @param routeContext the route context
169     * @param ref reference id for the error handler
170     * @return the error handler
171     */
172    public static ErrorHandlerFactory lookupErrorHandlerFactory(RouteContext routeContext, String ref) {
173        return lookupErrorHandlerFactory(routeContext, ref, true);
174    }
175
176    /**
177     * Lookup the error handler by the given ref
178     *
179     * @param routeContext the route context
180     * @param ref reference id for the error handler
181     * @param mandatory whether the error handler must exists, if not a
182     *            {@link org.apache.camel.NoSuchBeanException} is thrown
183     * @return the error handler
184     */
185    public static ErrorHandlerFactory lookupErrorHandlerFactory(RouteContext routeContext, String ref, boolean mandatory) {
186        ErrorHandlerFactory answer;
187
188        // if the ref is the default then we do not have any explicit error
189        // handler configured
190        // if that is the case then use error handlers configured on the route,
191        // as for instance
192        // the transacted error handler could have been configured on the route
193        // so we should use that one
194        if (!isErrorHandlerFactoryConfigured(ref)) {
195            // see if there has been configured a error handler builder on the route
196            // TODO: Avoid using RouteDefinition - tests should pass: https://issues.apache.org/jira/browse/CAMEL-13984
197            RouteDefinition route = (RouteDefinition)routeContext.getRoute();
198            answer = route.getErrorHandlerFactory();
199            // check if its also a ref with no error handler configuration like me
200            if (answer instanceof ErrorHandlerBuilderRef) {
201                ErrorHandlerBuilderRef other = (ErrorHandlerBuilderRef)answer;
202                String otherRef = other.getRef();
203                if (!isErrorHandlerFactoryConfigured(otherRef)) {
204                    // the other has also no explicit error handler configured
205                    // then fallback to the handler
206                    // configured on the parent camel context
207                    answer = lookupErrorHandlerFactory(routeContext.getCamelContext());
208                }
209                if (answer == null) {
210                    // the other has also no explicit error handler configured
211                    // then fallback to the default error handler
212                    // otherwise we could recursive loop forever (triggered by
213                    // createErrorHandler method)
214                    answer = new DefaultErrorHandlerBuilder();
215                }
216                // inherit the error handlers from the other as they are to be
217                // shared
218                // this is needed by camel-spring when none error handler has
219                // been explicit configured
220                routeContext.addErrorHandlerFactoryReference(other, answer);
221            }
222        } else {
223            // use specific configured error handler
224            if (mandatory) {
225                answer = routeContext.mandatoryLookup(ref, ErrorHandlerBuilder.class);
226            } else {
227                answer = routeContext.lookup(ref, ErrorHandlerBuilder.class);
228            }
229        }
230
231        return answer;
232    }
233
234    protected static ErrorHandlerFactory lookupErrorHandlerFactory(CamelContext camelContext) {
235        ErrorHandlerFactory answer = camelContext.adapt(ExtendedCamelContext.class).getErrorHandlerFactory();
236        if (answer instanceof ErrorHandlerBuilderRef) {
237            ErrorHandlerBuilderRef other = (ErrorHandlerBuilderRef)answer;
238            String otherRef = other.getRef();
239            if (isErrorHandlerFactoryConfigured(otherRef)) {
240                answer = camelContext.getRegistry().lookupByNameAndType(otherRef, ErrorHandlerBuilder.class);
241                if (answer == null) {
242                    throw new IllegalArgumentException("ErrorHandlerBuilder with id " + otherRef + " not found in registry.");
243                }
244            }
245        }
246
247        return answer;
248    }
249
250    /**
251     * Returns whether a specific error handler builder has been configured or
252     * not.
253     * <p/>
254     * Can be used to test if none has been configured and then install a custom
255     * error handler builder replacing the default error handler (that would
256     * have been used as fallback otherwise). <br/>
257     * This is for instance used by the transacted policy to setup a
258     * TransactedErrorHandlerBuilder in camel-spring.
259     */
260    public static boolean isErrorHandlerFactoryConfigured(String ref) {
261        return !DEFAULT_ERROR_HANDLER_BUILDER.equals(ref);
262    }
263
264    /**
265     * Creates the error handler
266     *
267     * @param processor the outer processor
268     * @return the error handler
269     * @throws Exception is thrown if the error handler could not be created
270     */
271    public abstract Processor createErrorHandler(Processor processor) throws Exception;
272
273    public void configure(RouteContext routeContext, ErrorHandler handler) {
274        if (handler instanceof ErrorHandlerSupport) {
275            ErrorHandlerSupport handlerSupport = (ErrorHandlerSupport)handler;
276
277            for (NamedNode exception : routeContext.getErrorHandlers(definition)) {
278                ErrorHandlerBuilderSupport.addExceptionPolicy(handlerSupport, routeContext, (OnExceptionDefinition)exception);
279            }
280        }
281        if (handler instanceof RedeliveryErrorHandler) {
282            boolean original = ((RedeliveryErrorHandler)handler).isUseOriginalMessagePolicy() || ((RedeliveryErrorHandler)handler).isUseOriginalMessagePolicy();
283            if (original) {
284                // ensure allow original is turned on
285                routeContext.setAllowUseOriginalMessage(true);
286            }
287        }
288    }
289
290    /**
291     * Note: Not for end users - this method is used internally by
292     * camel-blueprint
293     */
294    public static RedeliveryPolicy createRedeliveryPolicy(RedeliveryPolicyDefinition definition, CamelContext context, RedeliveryPolicy parentPolicy) {
295        RedeliveryPolicy answer;
296        if (parentPolicy != null) {
297            answer = parentPolicy.copy();
298        } else {
299            answer = new RedeliveryPolicy();
300        }
301
302        try {
303
304            // copy across the properties - if they are set
305            if (definition.getMaximumRedeliveries() != null) {
306                answer.setMaximumRedeliveries(CamelContextHelper.parseInteger(context, definition.getMaximumRedeliveries()));
307            }
308            if (definition.getRedeliveryDelay() != null) {
309                answer.setRedeliveryDelay(CamelContextHelper.parseLong(context, definition.getRedeliveryDelay()));
310            }
311            if (definition.getAsyncDelayedRedelivery() != null) {
312                answer.setAsyncDelayedRedelivery(CamelContextHelper.parseBoolean(context, definition.getAsyncDelayedRedelivery()));
313            }
314            if (definition.getRetriesExhaustedLogLevel() != null) {
315                answer.setRetriesExhaustedLogLevel(LoggingLevel.valueOf(definition.getRetriesExhaustedLogLevel()));
316            }
317            if (definition.getRetryAttemptedLogLevel() != null) {
318                answer.setRetryAttemptedLogLevel(LoggingLevel.valueOf(definition.getRetryAttemptedLogLevel()));
319            }
320            if (definition.getRetryAttemptedLogInterval() != null) {
321                answer.setRetryAttemptedLogInterval(CamelContextHelper.parseInteger(context, definition.getRetryAttemptedLogInterval()));
322            }
323            if (definition.getBackOffMultiplier() != null) {
324                answer.setBackOffMultiplier(CamelContextHelper.parseDouble(context, definition.getBackOffMultiplier()));
325            }
326            if (definition.getUseExponentialBackOff() != null) {
327                answer.setUseExponentialBackOff(CamelContextHelper.parseBoolean(context, definition.getUseExponentialBackOff()));
328            }
329            if (definition.getCollisionAvoidanceFactor() != null) {
330                answer.setCollisionAvoidanceFactor(CamelContextHelper.parseDouble(context, definition.getCollisionAvoidanceFactor()));
331            }
332            if (definition.getUseCollisionAvoidance() != null) {
333                answer.setUseCollisionAvoidance(CamelContextHelper.parseBoolean(context, definition.getUseCollisionAvoidance()));
334            }
335            if (definition.getMaximumRedeliveryDelay() != null) {
336                answer.setMaximumRedeliveryDelay(CamelContextHelper.parseLong(context, definition.getMaximumRedeliveryDelay()));
337            }
338            if (definition.getLogStackTrace() != null) {
339                answer.setLogStackTrace(CamelContextHelper.parseBoolean(context, definition.getLogStackTrace()));
340            }
341            if (definition.getLogRetryStackTrace() != null) {
342                answer.setLogRetryStackTrace(CamelContextHelper.parseBoolean(context, definition.getLogRetryStackTrace()));
343            }
344            if (definition.getLogHandled() != null) {
345                answer.setLogHandled(CamelContextHelper.parseBoolean(context, definition.getLogHandled()));
346            }
347            if (definition.getLogNewException() != null) {
348                answer.setLogNewException(CamelContextHelper.parseBoolean(context, definition.getLogNewException()));
349            }
350            if (definition.getLogContinued() != null) {
351                answer.setLogContinued(CamelContextHelper.parseBoolean(context, definition.getLogContinued()));
352            }
353            if (definition.getLogRetryAttempted() != null) {
354                answer.setLogRetryAttempted(CamelContextHelper.parseBoolean(context, definition.getLogRetryAttempted()));
355            }
356            if (definition.getLogExhausted() != null) {
357                answer.setLogExhausted(CamelContextHelper.parseBoolean(context, definition.getLogExhausted()));
358            }
359            if (definition.getLogExhaustedMessageHistory() != null) {
360                answer.setLogExhaustedMessageHistory(CamelContextHelper.parseBoolean(context, definition.getLogExhaustedMessageHistory()));
361            }
362            if (definition.getLogExhaustedMessageBody() != null) {
363                answer.setLogExhaustedMessageBody(CamelContextHelper.parseBoolean(context, definition.getLogExhaustedMessageBody()));
364            }
365            if (definition.getDisableRedelivery() != null) {
366                if (CamelContextHelper.parseBoolean(context, definition.getDisableRedelivery())) {
367                    answer.setMaximumRedeliveries(0);
368                }
369            }
370            if (definition.getDelayPattern() != null) {
371                answer.setDelayPattern(CamelContextHelper.parseText(context, definition.getDelayPattern()));
372            }
373            if (definition.getAllowRedeliveryWhileStopping() != null) {
374                answer.setAllowRedeliveryWhileStopping(CamelContextHelper.parseBoolean(context, definition.getAllowRedeliveryWhileStopping()));
375            }
376            if (definition.getExchangeFormatterRef() != null) {
377                answer.setExchangeFormatterRef(CamelContextHelper.parseText(context, definition.getExchangeFormatterRef()));
378            }
379        } catch (Exception e) {
380            throw RuntimeCamelException.wrapRuntimeCamelException(e);
381        }
382
383        return answer;
384    }
385
386}