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}