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