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