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.builder; 018 019import java.util.concurrent.ScheduledExecutorService; 020 021import org.apache.camel.CamelContext; 022import org.apache.camel.Endpoint; 023import org.apache.camel.Expression; 024import org.apache.camel.LoggingLevel; 025import org.apache.camel.Predicate; 026import org.apache.camel.Processor; 027import org.apache.camel.processor.DefaultErrorHandler; 028import org.apache.camel.processor.RedeliveryPolicy; 029import org.apache.camel.spi.ExecutorServiceManager; 030import org.apache.camel.spi.Language; 031import org.apache.camel.spi.RouteContext; 032import org.apache.camel.spi.ThreadPoolProfile; 033import org.apache.camel.util.CamelLogger; 034import org.apache.camel.util.ExpressionToPredicateAdapter; 035import org.slf4j.LoggerFactory; 036 037/** 038 * The default error handler builder. 039 * 040 * @version 041 */ 042public class DefaultErrorHandlerBuilder extends ErrorHandlerBuilderSupport { 043 044 protected CamelLogger logger; 045 protected RedeliveryPolicy redeliveryPolicy; 046 protected Processor onRedelivery; 047 protected Predicate retryWhile; 048 protected String retryWhileRef; 049 protected Processor failureProcessor; 050 protected Endpoint deadLetter; 051 protected String deadLetterUri; 052 protected boolean deadLetterHandleNewException = true; 053 protected boolean useOriginalMessage; 054 protected boolean asyncDelayedRedelivery; 055 protected String executorServiceRef; 056 protected ScheduledExecutorService executorService; 057 protected Processor onPrepareFailure; 058 protected Processor onExceptionOccurred; 059 060 public DefaultErrorHandlerBuilder() { 061 } 062 063 public Processor createErrorHandler(RouteContext routeContext, Processor processor) throws Exception { 064 DefaultErrorHandler answer = new DefaultErrorHandler(routeContext.getCamelContext(), processor, getLogger(), getOnRedelivery(), 065 getRedeliveryPolicy(), getExceptionPolicyStrategy(), getRetryWhilePolicy(routeContext.getCamelContext()), 066 getExecutorService(routeContext.getCamelContext()), getOnPrepareFailure(), getOnExceptionOccurred()); 067 // configure error handler before we can use it 068 configure(routeContext, answer); 069 return answer; 070 } 071 072 public boolean supportTransacted() { 073 return false; 074 } 075 076 @Override 077 public ErrorHandlerBuilder cloneBuilder() { 078 DefaultErrorHandlerBuilder answer = new DefaultErrorHandlerBuilder(); 079 cloneBuilder(answer); 080 return answer; 081 } 082 083 protected void cloneBuilder(DefaultErrorHandlerBuilder other) { 084 super.cloneBuilder(other); 085 086 if (logger != null) { 087 other.setLogger(logger); 088 } 089 if (redeliveryPolicy != null) { 090 other.setRedeliveryPolicy(redeliveryPolicy.copy()); 091 } 092 if (onRedelivery != null) { 093 other.setOnRedelivery(onRedelivery); 094 } 095 if (retryWhile != null) { 096 other.setRetryWhile(retryWhile); 097 } 098 if (retryWhileRef != null) { 099 other.setRetryWhileRef(retryWhileRef); 100 } 101 if (failureProcessor != null) { 102 other.setFailureProcessor(failureProcessor); 103 } 104 if (deadLetter != null) { 105 other.setDeadLetter(deadLetter); 106 } 107 if (deadLetterUri != null) { 108 other.setDeadLetterUri(deadLetterUri); 109 } 110 if (onPrepareFailure != null) { 111 other.setOnPrepareFailure(onPrepareFailure); 112 } 113 if (onExceptionOccurred != null) { 114 other.setOnExceptionOccurred(onExceptionOccurred); 115 } 116 other.setDeadLetterHandleNewException(deadLetterHandleNewException); 117 other.setUseOriginalMessage(useOriginalMessage); 118 other.setAsyncDelayedRedelivery(asyncDelayedRedelivery); 119 other.setExecutorServiceRef(executorServiceRef); 120 } 121 122 // Builder methods 123 // ------------------------------------------------------------------------- 124 public DefaultErrorHandlerBuilder backOffMultiplier(double backOffMultiplier) { 125 getRedeliveryPolicy().backOffMultiplier(backOffMultiplier); 126 return this; 127 } 128 129 public DefaultErrorHandlerBuilder collisionAvoidancePercent(double collisionAvoidancePercent) { 130 getRedeliveryPolicy().collisionAvoidancePercent(collisionAvoidancePercent); 131 return this; 132 } 133 134 /** 135 * @deprecated will be removed in the near future. Use {@link #redeliveryDelay(long)} instead 136 */ 137 @Deprecated 138 public DefaultErrorHandlerBuilder redeliverDelay(long delay) { 139 getRedeliveryPolicy().redeliveryDelay(delay); 140 return this; 141 } 142 143 public DefaultErrorHandlerBuilder redeliveryDelay(long delay) { 144 getRedeliveryPolicy().redeliveryDelay(delay); 145 return this; 146 } 147 148 public DefaultErrorHandlerBuilder delayPattern(String delayPattern) { 149 getRedeliveryPolicy().delayPattern(delayPattern); 150 return this; 151 } 152 153 public DefaultErrorHandlerBuilder maximumRedeliveries(int maximumRedeliveries) { 154 getRedeliveryPolicy().maximumRedeliveries(maximumRedeliveries); 155 return this; 156 } 157 158 public DefaultErrorHandlerBuilder disableRedelivery() { 159 getRedeliveryPolicy().maximumRedeliveries(0); 160 return this; 161 } 162 163 public DefaultErrorHandlerBuilder maximumRedeliveryDelay(long maximumRedeliveryDelay) { 164 getRedeliveryPolicy().maximumRedeliveryDelay(maximumRedeliveryDelay); 165 return this; 166 } 167 168 public DefaultErrorHandlerBuilder useCollisionAvoidance() { 169 getRedeliveryPolicy().useCollisionAvoidance(); 170 return this; 171 } 172 173 public DefaultErrorHandlerBuilder useExponentialBackOff() { 174 getRedeliveryPolicy().useExponentialBackOff(); 175 return this; 176 } 177 178 public DefaultErrorHandlerBuilder retriesExhaustedLogLevel(LoggingLevel retriesExhaustedLogLevel) { 179 getRedeliveryPolicy().setRetriesExhaustedLogLevel(retriesExhaustedLogLevel); 180 return this; 181 } 182 183 public DefaultErrorHandlerBuilder retryAttemptedLogLevel(LoggingLevel retryAttemptedLogLevel) { 184 getRedeliveryPolicy().setRetryAttemptedLogLevel(retryAttemptedLogLevel); 185 return this; 186 } 187 188 public DefaultErrorHandlerBuilder logStackTrace(boolean logStackTrace) { 189 getRedeliveryPolicy().setLogStackTrace(logStackTrace); 190 return this; 191 } 192 193 public DefaultErrorHandlerBuilder logRetryStackTrace(boolean logRetryStackTrace) { 194 getRedeliveryPolicy().setLogRetryStackTrace(logRetryStackTrace); 195 return this; 196 } 197 198 public DefaultErrorHandlerBuilder logHandled(boolean logHandled) { 199 getRedeliveryPolicy().setLogHandled(logHandled); 200 return this; 201 } 202 203 public DefaultErrorHandlerBuilder logNewException(boolean logNewException) { 204 getRedeliveryPolicy().setLogNewException(logNewException); 205 return this; 206 } 207 208 public DefaultErrorHandlerBuilder logExhausted(boolean logExhausted) { 209 getRedeliveryPolicy().setLogExhausted(logExhausted); 210 return this; 211 } 212 213 public DefaultErrorHandlerBuilder logExhaustedMessageHistory(boolean logExhaustedMessageHistory) { 214 getRedeliveryPolicy().setLogExhaustedMessageHistory(logExhaustedMessageHistory); 215 return this; 216 } 217 218 public DefaultErrorHandlerBuilder logExhaustedMessageBody(boolean logExhaustedMessageBody) { 219 getRedeliveryPolicy().setLogExhaustedMessageBody(logExhaustedMessageBody); 220 return this; 221 } 222 223 public DefaultErrorHandlerBuilder exchangeFormatterRef(String exchangeFormatterRef) { 224 getRedeliveryPolicy().setExchangeFormatterRef(exchangeFormatterRef); 225 return this; 226 } 227 228 /** 229 * Will allow asynchronous delayed redeliveries. The route, in particular the consumer's component, 230 * must support the Asynchronous Routing Engine (e.g. seda) 231 * 232 * @see org.apache.camel.processor.RedeliveryPolicy#setAsyncDelayedRedelivery(boolean) 233 * @return the builder 234 */ 235 public DefaultErrorHandlerBuilder asyncDelayedRedelivery() { 236 getRedeliveryPolicy().setAsyncDelayedRedelivery(true); 237 return this; 238 } 239 240 /** 241 * Controls whether to allow redelivery while stopping/shutting down a route that uses error handling. 242 * 243 * @param allowRedeliveryWhileStopping <tt>true</tt> to allow redelivery, <tt>false</tt> to reject redeliveries 244 * @return the builder 245 */ 246 public DefaultErrorHandlerBuilder allowRedeliveryWhileStopping(boolean allowRedeliveryWhileStopping) { 247 getRedeliveryPolicy().setAllowRedeliveryWhileStopping(allowRedeliveryWhileStopping); 248 return this; 249 } 250 251 /** 252 * Sets a reference to a thread pool to be used for redelivery. 253 * 254 * @param ref reference to a scheduled thread pool 255 * @return the builder. 256 */ 257 public DefaultErrorHandlerBuilder executorServiceRef(String ref) { 258 setExecutorServiceRef(ref); 259 return this; 260 } 261 262 /** 263 * Sets the logger used for caught exceptions 264 * 265 * @param logger the logger 266 * @return the builder 267 */ 268 public DefaultErrorHandlerBuilder logger(CamelLogger logger) { 269 setLogger(logger); 270 return this; 271 } 272 273 /** 274 * Sets the logging level of exceptions caught 275 * 276 * @param level the logging level 277 * @return the builder 278 */ 279 public DefaultErrorHandlerBuilder loggingLevel(LoggingLevel level) { 280 getLogger().setLevel(level); 281 return this; 282 } 283 284 /** 285 * Sets the log used for caught exceptions 286 * 287 * @param log the logger 288 * @return the builder 289 */ 290 public DefaultErrorHandlerBuilder log(org.slf4j.Logger log) { 291 getLogger().setLog(log); 292 return this; 293 } 294 295 /** 296 * Sets the log used for caught exceptions 297 * 298 * @param log the log name 299 * @return the builder 300 */ 301 public DefaultErrorHandlerBuilder log(String log) { 302 return log(LoggerFactory.getLogger(log)); 303 } 304 305 /** 306 * Sets the log used for caught exceptions 307 * 308 * @param log the log class 309 * @return the builder 310 */ 311 public DefaultErrorHandlerBuilder log(Class<?> log) { 312 return log(LoggerFactory.getLogger(log)); 313 } 314 315 /** 316 * Sets a processor that should be processed <b>before</b> a redelivery attempt. 317 * <p/> 318 * Can be used to change the {@link org.apache.camel.Exchange} <b>before</b> its being redelivered. 319 * 320 * @param processor the processor 321 * @return the builder 322 */ 323 public DefaultErrorHandlerBuilder onRedelivery(Processor processor) { 324 setOnRedelivery(processor); 325 return this; 326 } 327 328 /** 329 * Sets the retry while expression. 330 * <p/> 331 * Will continue retrying until expression evaluates to <tt>false</tt>. 332 * 333 * @param retryWhile expression that determines when to stop retrying 334 * @return the builder 335 */ 336 public DefaultErrorHandlerBuilder retryWhile(Expression retryWhile) { 337 setRetryWhile(ExpressionToPredicateAdapter.toPredicate(retryWhile)); 338 return this; 339 } 340 341 /** 342 * Will use the original input {@link org.apache.camel.Message} when an {@link org.apache.camel.Exchange} 343 * is moved to the dead letter queue. 344 * <p/> 345 * <b>Notice:</b> this only applies when all redeliveries attempt have failed and the {@link org.apache.camel.Exchange} 346 * is doomed for failure. 347 * <br/> 348 * Instead of using the current inprogress {@link org.apache.camel.Exchange} IN message we use the original 349 * IN message instead. This allows you to store the original input in the dead letter queue instead of the inprogress 350 * snapshot of the IN message. 351 * For instance if you route transform the IN body during routing and then failed. With the original exchange 352 * store in the dead letter queue it might be easier to manually re submit the {@link org.apache.camel.Exchange} 353 * again as the IN message is the same as when Camel received it. 354 * So you should be able to send the {@link org.apache.camel.Exchange} to the same input. 355 * <p/> 356 * By default this feature is off. 357 * 358 * @return the builder 359 */ 360 public DefaultErrorHandlerBuilder useOriginalMessage() { 361 setUseOriginalMessage(true); 362 return this; 363 } 364 365 /** 366 * Whether the dead letter channel should handle (and ignore) any new exception that may been thrown during sending the 367 * message to the dead letter endpoint. 368 * <p/> 369 * The default value is <tt>true</tt> which means any such kind of exception is handled and ignored. Set this to <tt>false</tt> 370 * to let the exception be propagated back on the {@link org.apache.camel.Exchange}. This can be used in situations 371 * where you use transactions, and want to use Camel's dead letter channel to deal with exceptions during routing, 372 * but if the dead letter channel itself fails because of a new exception being thrown, then by setting this to <tt>false</tt> 373 * the new exceptions is propagated back and set on the {@link org.apache.camel.Exchange}, which allows the transaction 374 * to detect the exception, and rollback. 375 * 376 * @param handleNewException <tt>true</tt> to handle (and ignore), <tt>false</tt> to catch and propagated the exception on the {@link org.apache.camel.Exchange} 377 * @return the builder 378 */ 379 public DefaultErrorHandlerBuilder deadLetterHandleNewException(boolean handleNewException) { 380 setDeadLetterHandleNewException(handleNewException); 381 return this; 382 } 383 384 /** 385 * @deprecated use {@link #deadLetterHandleNewException(boolean)}} with value <tt>false</tt> 386 */ 387 @Deprecated 388 public DefaultErrorHandlerBuilder checkException() { 389 setDeadLetterHandleNewException(false); 390 return this; 391 } 392 393 /** 394 * Sets a custom {@link org.apache.camel.Processor} to prepare the {@link org.apache.camel.Exchange} before 395 * handled by the failure processor / dead letter channel. This allows for example to enrich the message 396 * before sending to a dead letter queue. 397 * 398 * @param processor the processor 399 * @return the builder 400 */ 401 public DefaultErrorHandlerBuilder onPrepareFailure(Processor processor) { 402 setOnPrepareFailure(processor); 403 return this; 404 } 405 406 /** 407 * Sets a custom {@link org.apache.camel.Processor} to process the {@link org.apache.camel.Exchange} just after an exception was thrown. 408 * This allows to execute the processor at the same time the exception was thrown. 409 * <p/> 410 * Important: Any exception thrown from this processor will be ignored. 411 * 412 * @param processor the processor 413 * @return the builder 414 */ 415 public DefaultErrorHandlerBuilder onExceptionOccurred(Processor processor) { 416 setOnExceptionOccurred(processor); 417 return this; 418 } 419 420 // Properties 421 // ------------------------------------------------------------------------- 422 423 public Processor getFailureProcessor() { 424 return failureProcessor; 425 } 426 427 public void setFailureProcessor(Processor failureProcessor) { 428 this.failureProcessor = failureProcessor; 429 } 430 431 public RedeliveryPolicy getRedeliveryPolicy() { 432 if (redeliveryPolicy == null) { 433 redeliveryPolicy = createRedeliveryPolicy(); 434 } 435 return redeliveryPolicy; 436 } 437 438 /** 439 * Sets the redelivery policy 440 */ 441 public void setRedeliveryPolicy(RedeliveryPolicy redeliveryPolicy) { 442 this.redeliveryPolicy = redeliveryPolicy; 443 } 444 445 public CamelLogger getLogger() { 446 if (logger == null) { 447 logger = createLogger(); 448 } 449 return logger; 450 } 451 452 public void setLogger(CamelLogger logger) { 453 this.logger = logger; 454 } 455 456 public Processor getOnRedelivery() { 457 return onRedelivery; 458 } 459 460 public void setOnRedelivery(Processor onRedelivery) { 461 this.onRedelivery = onRedelivery; 462 } 463 464 public Predicate getRetryWhilePolicy(CamelContext context) { 465 Predicate answer = getRetryWhile(); 466 467 if (getRetryWhileRef() != null) { 468 // its a bean expression 469 Language bean = context.resolveLanguage("bean"); 470 answer = bean.createPredicate(getRetryWhileRef()); 471 } 472 473 return answer; 474 } 475 476 public Predicate getRetryWhile() { 477 return retryWhile; 478 } 479 480 public void setRetryWhile(Predicate retryWhile) { 481 this.retryWhile = retryWhile; 482 } 483 484 public String getRetryWhileRef() { 485 return retryWhileRef; 486 } 487 488 public void setRetryWhileRef(String retryWhileRef) { 489 this.retryWhileRef = retryWhileRef; 490 } 491 492 public String getDeadLetterUri() { 493 return deadLetterUri; 494 } 495 496 public void setDeadLetterUri(String deadLetterUri) { 497 this.deadLetter = null; 498 this.deadLetterUri = deadLetterUri; 499 } 500 501 public Endpoint getDeadLetter() { 502 return deadLetter; 503 } 504 505 public void setDeadLetter(Endpoint deadLetter) { 506 this.deadLetter = deadLetter; 507 this.deadLetterUri = deadLetter.getEndpointUri(); 508 } 509 510 public boolean isDeadLetterHandleNewException() { 511 return deadLetterHandleNewException; 512 } 513 514 public void setDeadLetterHandleNewException(boolean deadLetterHandleNewException) { 515 this.deadLetterHandleNewException = deadLetterHandleNewException; 516 } 517 518 public boolean isUseOriginalMessage() { 519 return useOriginalMessage; 520 } 521 522 public void setUseOriginalMessage(boolean useOriginalMessage) { 523 this.useOriginalMessage = useOriginalMessage; 524 } 525 526 public boolean isAsyncDelayedRedelivery() { 527 return asyncDelayedRedelivery; 528 } 529 530 public void setAsyncDelayedRedelivery(boolean asyncDelayedRedelivery) { 531 this.asyncDelayedRedelivery = asyncDelayedRedelivery; 532 } 533 534 public String getExecutorServiceRef() { 535 return executorServiceRef; 536 } 537 538 public void setExecutorServiceRef(String executorServiceRef) { 539 this.executorServiceRef = executorServiceRef; 540 } 541 542 public Processor getOnPrepareFailure() { 543 return onPrepareFailure; 544 } 545 546 public void setOnPrepareFailure(Processor onPrepareFailure) { 547 this.onPrepareFailure = onPrepareFailure; 548 } 549 550 public Processor getOnExceptionOccurred() { 551 return onExceptionOccurred; 552 } 553 554 public void setOnExceptionOccurred(Processor onExceptionOccurred) { 555 this.onExceptionOccurred = onExceptionOccurred; 556 } 557 558 protected RedeliveryPolicy createRedeliveryPolicy() { 559 RedeliveryPolicy policy = new RedeliveryPolicy(); 560 policy.disableRedelivery(); 561 return policy; 562 } 563 564 protected CamelLogger createLogger() { 565 return new CamelLogger(LoggerFactory.getLogger(DefaultErrorHandler.class), LoggingLevel.ERROR); 566 } 567 568 protected synchronized ScheduledExecutorService getExecutorService(CamelContext camelContext) { 569 if (executorService == null || executorService.isShutdown()) { 570 // camel context will shutdown the executor when it shutdown so no need to shut it down when stopping 571 if (executorServiceRef != null) { 572 executorService = camelContext.getRegistry().lookupByNameAndType(executorServiceRef, ScheduledExecutorService.class); 573 if (executorService == null) { 574 ExecutorServiceManager manager = camelContext.getExecutorServiceManager(); 575 ThreadPoolProfile profile = manager.getThreadPoolProfile(executorServiceRef); 576 executorService = manager.newScheduledThreadPool(this, executorServiceRef, profile); 577 } 578 if (executorService == null) { 579 throw new IllegalArgumentException("ExecutorServiceRef " + executorServiceRef + " not found in registry."); 580 } 581 } else { 582 // no explicit configured thread pool, so leave it up to the error handler to decide if it need 583 // a default thread pool from CamelContext#getErrorHandlerExecutorService 584 executorService = null; 585 } 586 } 587 return executorService; 588 } 589 590 @Override 591 public String toString() { 592 return "DefaultErrorHandlerBuilder"; 593 } 594 595}