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.processor; 018 019import java.util.ArrayList; 020import java.util.List; 021import java.util.concurrent.Callable; 022import java.util.concurrent.CountDownLatch; 023import java.util.concurrent.RejectedExecutionException; 024import java.util.concurrent.ScheduledExecutorService; 025import java.util.concurrent.ThreadPoolExecutor; 026import java.util.concurrent.TimeUnit; 027import java.util.concurrent.atomic.AtomicInteger; 028 029import org.apache.camel.AsyncCallback; 030import org.apache.camel.AsyncProcessor; 031import org.apache.camel.CamelContext; 032import org.apache.camel.Exchange; 033import org.apache.camel.LoggingLevel; 034import org.apache.camel.Message; 035import org.apache.camel.Navigate; 036import org.apache.camel.Predicate; 037import org.apache.camel.Processor; 038import org.apache.camel.model.OnExceptionDefinition; 039import org.apache.camel.spi.AsyncProcessorAwaitManager; 040import org.apache.camel.spi.ExchangeFormatter; 041import org.apache.camel.spi.ShutdownPrepared; 042import org.apache.camel.spi.SubUnitOfWorkCallback; 043import org.apache.camel.spi.UnitOfWork; 044import org.apache.camel.util.AsyncProcessorConverterHelper; 045import org.apache.camel.util.CamelContextHelper; 046import org.apache.camel.util.CamelLogger; 047import org.apache.camel.util.EventHelper; 048import org.apache.camel.util.ExchangeHelper; 049import org.apache.camel.util.MessageHelper; 050import org.apache.camel.util.ObjectHelper; 051import org.apache.camel.util.ServiceHelper; 052import org.apache.camel.util.StopWatch; 053import org.apache.camel.util.URISupport; 054 055/** 056 * Base redeliverable error handler that also supports a final dead letter queue in case 057 * all redelivery attempts fail. 058 * <p/> 059 * This implementation should contain all the error handling logic and the sub classes 060 * should only configure it according to what they support. 061 * 062 * @version 063 */ 064public abstract class RedeliveryErrorHandler extends ErrorHandlerSupport implements AsyncProcessor, ShutdownPrepared, Navigate<Processor> { 065 066 protected final AtomicInteger redeliverySleepCounter = new AtomicInteger(); 067 protected ScheduledExecutorService executorService; 068 protected final CamelContext camelContext; 069 protected final AsyncProcessorAwaitManager awaitManager; 070 protected final Processor deadLetter; 071 protected final String deadLetterUri; 072 protected final boolean deadLetterHandleNewException; 073 protected Processor output; 074 protected AsyncProcessor outputAsync; 075 protected final Processor redeliveryProcessor; 076 protected final RedeliveryPolicy redeliveryPolicy; 077 protected final Predicate retryWhilePolicy; 078 protected final CamelLogger logger; 079 protected final boolean useOriginalMessagePolicy; 080 protected boolean redeliveryEnabled; 081 protected volatile boolean preparingShutdown; 082 protected final ExchangeFormatter exchangeFormatter; 083 protected final boolean customExchangeFormatter; 084 protected final Processor onPrepareProcessor; 085 protected final Processor onExceptionProcessor; 086 087 /** 088 * Contains the current redelivery data 089 */ 090 protected class RedeliveryData { 091 // redelivery state 092 Exchange original; 093 boolean sync = true; 094 int redeliveryCounter; 095 long redeliveryDelay; 096 Predicate retryWhilePredicate; 097 boolean redeliverFromSync; 098 099 // default behavior which can be overloaded on a per exception basis 100 RedeliveryPolicy currentRedeliveryPolicy; 101 Processor failureProcessor; 102 Processor onRedeliveryProcessor; 103 Processor onExceptionProcessor; 104 Predicate handledPredicate; 105 Predicate continuedPredicate; 106 boolean useOriginalInMessage; 107 108 public RedeliveryData() { 109 // init with values from the error handler 110 this.retryWhilePredicate = retryWhilePolicy; 111 this.currentRedeliveryPolicy = redeliveryPolicy; 112 this.onRedeliveryProcessor = redeliveryProcessor; 113 this.onExceptionProcessor = RedeliveryErrorHandler.this.onExceptionProcessor; 114 this.handledPredicate = getDefaultHandledPredicate(); 115 this.useOriginalInMessage = useOriginalMessagePolicy; 116 } 117 } 118 119 /** 120 * Task for sleeping during redelivery attempts. 121 * <p/> 122 * This task is for the synchronous blocking. If using async delayed then a scheduled thread pool 123 * is used for sleeping and trigger redeliveries. 124 */ 125 private final class RedeliverSleepTask { 126 127 private final RedeliveryPolicy policy; 128 private final long delay; 129 130 RedeliverSleepTask(RedeliveryPolicy policy, long delay) { 131 this.policy = policy; 132 this.delay = delay; 133 } 134 135 public boolean sleep() throws InterruptedException { 136 // for small delays then just sleep 137 if (delay < 1000) { 138 policy.sleep(delay); 139 return true; 140 } 141 142 StopWatch watch = new StopWatch(); 143 144 log.debug("Sleeping for: {} millis until attempting redelivery", delay); 145 while (watch.taken() < delay) { 146 // sleep using 1 sec interval 147 148 long delta = delay - watch.taken(); 149 long max = Math.min(1000, delta); 150 if (max > 0) { 151 log.trace("Sleeping for: {} millis until waking up for re-check", max); 152 Thread.sleep(max); 153 } 154 155 // are we preparing for shutdown then only do redelivery if allowed 156 if (preparingShutdown && !policy.isAllowRedeliveryWhileStopping()) { 157 log.debug("Rejected redelivery while stopping"); 158 return false; 159 } 160 } 161 162 return true; 163 } 164 } 165 166 /** 167 * Tasks which performs asynchronous redelivery attempts, and being triggered by a 168 * {@link java.util.concurrent.ScheduledExecutorService} to avoid having any threads blocking if a task 169 * has to be delayed before a redelivery attempt is performed. 170 */ 171 private final class AsyncRedeliveryTask implements Callable<Boolean> { 172 173 private final Exchange exchange; 174 private final AsyncCallback callback; 175 private final RedeliveryData data; 176 177 AsyncRedeliveryTask(Exchange exchange, AsyncCallback callback, RedeliveryData data) { 178 this.exchange = exchange; 179 this.callback = callback; 180 this.data = data; 181 } 182 183 public Boolean call() throws Exception { 184 // prepare for redelivery 185 prepareExchangeForRedelivery(exchange, data); 186 187 // letting onRedeliver be executed at first 188 deliverToOnRedeliveryProcessor(exchange, data); 189 190 if (log.isTraceEnabled()) { 191 log.trace("Redelivering exchangeId: {} -> {} for Exchange: {}", new Object[]{exchange.getExchangeId(), outputAsync, exchange}); 192 } 193 194 // emmit event we are doing redelivery 195 EventHelper.notifyExchangeRedelivery(exchange.getContext(), exchange, data.redeliveryCounter); 196 197 // process the exchange (also redelivery) 198 boolean sync; 199 if (data.redeliverFromSync) { 200 // this redelivery task was scheduled from synchronous, which we forced to be asynchronous from 201 // this error handler, which means we have to invoke the callback with false, to have the callback 202 // be notified when we are done 203 sync = outputAsync.process(exchange, new AsyncCallback() { 204 public void done(boolean doneSync) { 205 log.trace("Redelivering exchangeId: {} done sync: {}", exchange.getExchangeId(), doneSync); 206 207 // mark we are in sync mode now 208 data.sync = false; 209 210 // only process if the exchange hasn't failed 211 // and it has not been handled by the error processor 212 if (isDone(exchange)) { 213 callback.done(false); 214 return; 215 } 216 217 // error occurred so loop back around which we do by invoking the processAsyncErrorHandler 218 processAsyncErrorHandler(exchange, callback, data); 219 } 220 }); 221 } else { 222 // this redelivery task was scheduled from asynchronous, which means we should only 223 // handle when the asynchronous task was done 224 sync = outputAsync.process(exchange, new AsyncCallback() { 225 public void done(boolean doneSync) { 226 log.trace("Redelivering exchangeId: {} done sync: {}", exchange.getExchangeId(), doneSync); 227 228 // this callback should only handle the async case 229 if (doneSync) { 230 return; 231 } 232 233 // mark we are in async mode now 234 data.sync = false; 235 236 // only process if the exchange hasn't failed 237 // and it has not been handled by the error processor 238 if (isDone(exchange)) { 239 callback.done(doneSync); 240 return; 241 } 242 // error occurred so loop back around which we do by invoking the processAsyncErrorHandler 243 processAsyncErrorHandler(exchange, callback, data); 244 } 245 }); 246 } 247 248 return sync; 249 } 250 } 251 252 public RedeliveryErrorHandler(CamelContext camelContext, Processor output, CamelLogger logger, 253 Processor redeliveryProcessor, RedeliveryPolicy redeliveryPolicy, Processor deadLetter, 254 String deadLetterUri, boolean deadLetterHandleNewException, boolean useOriginalMessagePolicy, 255 Predicate retryWhile, ScheduledExecutorService executorService, Processor onPrepareProcessor, Processor onExceptionProcessor) { 256 257 ObjectHelper.notNull(camelContext, "CamelContext", this); 258 ObjectHelper.notNull(redeliveryPolicy, "RedeliveryPolicy", this); 259 260 this.camelContext = camelContext; 261 this.awaitManager = camelContext.getAsyncProcessorAwaitManager(); 262 this.redeliveryProcessor = redeliveryProcessor; 263 this.deadLetter = deadLetter; 264 this.output = output; 265 this.outputAsync = AsyncProcessorConverterHelper.convert(output); 266 this.redeliveryPolicy = redeliveryPolicy; 267 this.logger = logger; 268 this.deadLetterUri = deadLetterUri; 269 this.deadLetterHandleNewException = deadLetterHandleNewException; 270 this.useOriginalMessagePolicy = useOriginalMessagePolicy; 271 this.retryWhilePolicy = retryWhile; 272 this.executorService = executorService; 273 this.onPrepareProcessor = onPrepareProcessor; 274 this.onExceptionProcessor = onExceptionProcessor; 275 276 if (ObjectHelper.isNotEmpty(redeliveryPolicy.getExchangeFormatterRef())) { 277 ExchangeFormatter formatter = camelContext.getRegistry().lookupByNameAndType(redeliveryPolicy.getExchangeFormatterRef(), ExchangeFormatter.class); 278 if (formatter != null) { 279 this.exchangeFormatter = formatter; 280 this.customExchangeFormatter = true; 281 } else { 282 throw new IllegalArgumentException("Cannot find the exchangeFormatter by using reference id " + redeliveryPolicy.getExchangeFormatterRef()); 283 } 284 } else { 285 this.customExchangeFormatter = false; 286 // setup exchange formatter to be used for message history dump 287 DefaultExchangeFormatter formatter = new DefaultExchangeFormatter(); 288 formatter.setShowExchangeId(true); 289 formatter.setMultiline(true); 290 formatter.setShowHeaders(true); 291 formatter.setStyle(DefaultExchangeFormatter.OutputStyle.Fixed); 292 try { 293 Integer maxChars = CamelContextHelper.parseInteger(camelContext, camelContext.getGlobalOption(Exchange.LOG_DEBUG_BODY_MAX_CHARS)); 294 if (maxChars != null) { 295 formatter.setMaxChars(maxChars); 296 } 297 } catch (Exception e) { 298 throw ObjectHelper.wrapRuntimeCamelException(e); 299 } 300 this.exchangeFormatter = formatter; 301 } 302 } 303 304 /** 305 * Allows to change the output of the error handler which are used when optimising the 306 * JMX instrumentation to use either an advice or wrapped processor when calling a processor. 307 * The former is faster and therefore preferred, however if the error handler supports 308 * redelivery we need fine grained instrumentation which then must be wrapped and therefore 309 * need to change the output on the error handler. 310 */ 311 public void changeOutput(Processor output) { 312 this.output = output; 313 this.outputAsync = AsyncProcessorConverterHelper.convert(output); 314 } 315 316 public boolean supportTransacted() { 317 return false; 318 } 319 320 @Override 321 public boolean hasNext() { 322 return output != null; 323 } 324 325 @Override 326 public List<Processor> next() { 327 if (!hasNext()) { 328 return null; 329 } 330 List<Processor> answer = new ArrayList<Processor>(1); 331 answer.add(output); 332 return answer; 333 } 334 335 protected boolean isRunAllowed(RedeliveryData data) { 336 // if camel context is forcing a shutdown then do not allow running 337 boolean forceShutdown = camelContext.getShutdownStrategy().forceShutdown(this); 338 if (forceShutdown) { 339 log.trace("isRunAllowed() -> false (Run not allowed as ShutdownStrategy is forcing shutting down)"); 340 return false; 341 } 342 343 // redelivery policy can control if redelivery is allowed during stopping/shutdown 344 // but this only applies during a redelivery (counter must > 0) 345 if (data.redeliveryCounter > 0) { 346 if (data.currentRedeliveryPolicy.allowRedeliveryWhileStopping) { 347 log.trace("isRunAllowed() -> true (Run allowed as RedeliverWhileStopping is enabled)"); 348 return true; 349 } else if (preparingShutdown) { 350 // we are preparing for shutdown, now determine if we can still run 351 boolean answer = isRunAllowedOnPreparingShutdown(); 352 log.trace("isRunAllowed() -> {} (Run not allowed as we are preparing for shutdown)", answer); 353 return answer; 354 } 355 } 356 357 // we cannot run if we are stopping/stopped 358 boolean answer = !isStoppingOrStopped(); 359 log.trace("isRunAllowed() -> {} (Run allowed if we are not stopped/stopping)", answer); 360 return answer; 361 } 362 363 protected boolean isRunAllowedOnPreparingShutdown() { 364 return false; 365 } 366 367 protected boolean isRedeliveryAllowed(RedeliveryData data) { 368 // redelivery policy can control if redelivery is allowed during stopping/shutdown 369 // but this only applies during a redelivery (counter must > 0) 370 if (data.redeliveryCounter > 0) { 371 boolean stopping = isStoppingOrStopped(); 372 if (!preparingShutdown && !stopping) { 373 log.trace("isRedeliveryAllowed() -> true (we are not stopping/stopped)"); 374 return true; 375 } else { 376 // we are stopping or preparing to shutdown 377 if (data.currentRedeliveryPolicy.allowRedeliveryWhileStopping) { 378 log.trace("isRedeliveryAllowed() -> true (Redelivery allowed as RedeliverWhileStopping is enabled)"); 379 return true; 380 } else { 381 log.trace("isRedeliveryAllowed() -> false (Redelivery not allowed as RedeliverWhileStopping is disabled)"); 382 return false; 383 } 384 } 385 } 386 387 return true; 388 } 389 390 @Override 391 public void prepareShutdown(boolean suspendOnly, boolean forced) { 392 // prepare for shutdown, eg do not allow redelivery if configured 393 log.trace("Prepare shutdown on error handler {}", this); 394 preparingShutdown = true; 395 } 396 397 public void process(Exchange exchange) throws Exception { 398 if (output == null) { 399 // no output then just return 400 return; 401 } 402 403 // inline org.apache.camel.util.AsyncProcessorHelper.process(org.apache.camel.AsyncProcessor, org.apache.camel.Exchange) 404 // to optimize and reduce stacktrace lengths 405 final CountDownLatch latch = new CountDownLatch(1); 406 boolean sync = process(exchange, new AsyncCallback() { 407 public void done(boolean doneSync) { 408 if (!doneSync) { 409 awaitManager.countDown(exchange, latch); 410 } 411 } 412 }); 413 if (!sync) { 414 awaitManager.await(exchange, latch); 415 } 416 } 417 418 /** 419 * Process the exchange using redelivery error handling. 420 */ 421 public boolean process(final Exchange exchange, final AsyncCallback callback) { 422 final RedeliveryData data = new RedeliveryData(); 423 424 // do a defensive copy of the original Exchange, which is needed for redelivery so we can ensure the 425 // original Exchange is being redelivered, and not a mutated Exchange 426 data.original = defensiveCopyExchangeIfNeeded(exchange); 427 428 // use looping to have redelivery attempts 429 while (true) { 430 431 // can we still run 432 if (!isRunAllowed(data)) { 433 log.trace("Run not allowed, will reject executing exchange: {}", exchange); 434 if (exchange.getException() == null) { 435 exchange.setException(new RejectedExecutionException()); 436 } 437 // we cannot process so invoke callback 438 callback.done(data.sync); 439 return data.sync; 440 } 441 442 // did previous processing cause an exception? 443 boolean handle = shouldHandleException(exchange); 444 if (handle) { 445 handleException(exchange, data, isDeadLetterChannel()); 446 onExceptionOccurred(exchange, data); 447 } 448 449 // compute if we are exhausted, and whether redelivery is allowed 450 boolean exhausted = isExhausted(exchange, data); 451 boolean redeliverAllowed = isRedeliveryAllowed(data); 452 453 // if we are exhausted or redelivery is not allowed, then deliver to failure processor (eg such as DLC) 454 if (!redeliverAllowed || exhausted) { 455 Processor target = null; 456 boolean deliver = true; 457 458 // the unit of work may have an optional callback associated we need to leverage 459 SubUnitOfWorkCallback uowCallback = exchange.getUnitOfWork().getSubUnitOfWorkCallback(); 460 if (uowCallback != null) { 461 // signal to the callback we are exhausted 462 uowCallback.onExhausted(exchange); 463 // do not deliver to the failure processor as its been handled by the callback instead 464 deliver = false; 465 } 466 467 if (deliver) { 468 // should deliver to failure processor (either from onException or the dead letter channel) 469 target = data.failureProcessor != null ? data.failureProcessor : deadLetter; 470 } 471 // we should always invoke the deliverToFailureProcessor as it prepares, logs and does a fair 472 // bit of work for exhausted exchanges (its only the target processor which may be null if handled by a savepoint) 473 boolean isDeadLetterChannel = isDeadLetterChannel() && (target == null || target == deadLetter); 474 boolean sync = deliverToFailureProcessor(target, isDeadLetterChannel, exchange, data, callback); 475 // we are breaking out 476 return sync; 477 } 478 479 if (data.redeliveryCounter > 0) { 480 // calculate delay 481 data.redeliveryDelay = determineRedeliveryDelay(exchange, data.currentRedeliveryPolicy, data.redeliveryDelay, data.redeliveryCounter); 482 483 if (data.redeliveryDelay > 0) { 484 // okay there is a delay so create a scheduled task to have it executed in the future 485 486 if (data.currentRedeliveryPolicy.isAsyncDelayedRedelivery() && !exchange.isTransacted()) { 487 488 // we are doing a redelivery then a thread pool must be configured (see the doStart method) 489 ObjectHelper.notNull(executorService, "Redelivery is enabled but ExecutorService has not been configured.", this); 490 491 // let the RedeliverTask be the logic which tries to redeliver the Exchange which we can used a scheduler to 492 // have it being executed in the future, or immediately 493 // we are continuing asynchronously 494 495 // mark we are routing async from now and that this redelivery task came from a synchronous routing 496 data.sync = false; 497 data.redeliverFromSync = true; 498 AsyncRedeliveryTask task = new AsyncRedeliveryTask(exchange, callback, data); 499 500 // schedule the redelivery task 501 if (log.isTraceEnabled()) { 502 log.trace("Scheduling redelivery task to run in {} millis for exchangeId: {}", data.redeliveryDelay, exchange.getExchangeId()); 503 } 504 executorService.schedule(task, data.redeliveryDelay, TimeUnit.MILLISECONDS); 505 506 return false; 507 } else { 508 // async delayed redelivery was disabled or we are transacted so we must be synchronous 509 // as the transaction manager requires to execute in the same thread context 510 try { 511 // we are doing synchronous redelivery and use thread sleep, so we keep track using a counter how many are sleeping 512 redeliverySleepCounter.incrementAndGet(); 513 RedeliverSleepTask task = new RedeliverSleepTask(data.currentRedeliveryPolicy, data.redeliveryDelay); 514 boolean complete = task.sleep(); 515 redeliverySleepCounter.decrementAndGet(); 516 if (!complete) { 517 // the task was rejected 518 exchange.setException(new RejectedExecutionException("Redelivery not allowed while stopping")); 519 // mark the exchange as redelivery exhausted so the failure processor / dead letter channel can process the exchange 520 exchange.setProperty(Exchange.REDELIVERY_EXHAUSTED, Boolean.TRUE); 521 // jump to start of loop which then detects that we are failed and exhausted 522 continue; 523 } 524 } catch (InterruptedException e) { 525 redeliverySleepCounter.decrementAndGet(); 526 // we was interrupted so break out 527 exchange.setException(e); 528 // mark the exchange to stop continue routing when interrupted 529 // as we do not want to continue routing (for example a task has been cancelled) 530 exchange.setProperty(Exchange.ROUTE_STOP, Boolean.TRUE); 531 callback.done(data.sync); 532 return data.sync; 533 } 534 } 535 } 536 537 // prepare for redelivery 538 prepareExchangeForRedelivery(exchange, data); 539 540 // letting onRedeliver be executed 541 deliverToOnRedeliveryProcessor(exchange, data); 542 543 // emmit event we are doing redelivery 544 EventHelper.notifyExchangeRedelivery(exchange.getContext(), exchange, data.redeliveryCounter); 545 } 546 547 // process the exchange (also redelivery) 548 boolean sync = outputAsync.process(exchange, new AsyncCallback() { 549 public void done(boolean sync) { 550 // this callback should only handle the async case 551 if (sync) { 552 return; 553 } 554 555 // mark we are in async mode now 556 data.sync = false; 557 558 // if we are done then notify callback and exit 559 if (isDone(exchange)) { 560 callback.done(sync); 561 return; 562 } 563 564 // error occurred so loop back around which we do by invoking the processAsyncErrorHandler 565 // method which takes care of this in a asynchronous manner 566 processAsyncErrorHandler(exchange, callback, data); 567 } 568 }); 569 570 if (!sync) { 571 // the remainder of the Exchange is being processed asynchronously so we should return 572 return false; 573 } 574 // we continue to route synchronously 575 576 // if we are done then notify callback and exit 577 boolean done = isDone(exchange); 578 if (done) { 579 callback.done(true); 580 return true; 581 } 582 583 // error occurred so loop back around..... 584 } 585 } 586 587 /** 588 * <p>Determines the redelivery delay time by first inspecting the Message header {@link Exchange#REDELIVERY_DELAY} 589 * and if not present, defaulting to {@link RedeliveryPolicy#calculateRedeliveryDelay(long, int)}</p> 590 * 591 * <p>In order to prevent manipulation of the RedeliveryData state, the values of {@link RedeliveryData#redeliveryDelay} 592 * and {@link RedeliveryData#redeliveryCounter} are copied in.</p> 593 * 594 * @param exchange The current exchange in question. 595 * @param redeliveryPolicy The RedeliveryPolicy to use in the calculation. 596 * @param redeliveryDelay The default redelivery delay from RedeliveryData 597 * @param redeliveryCounter The redeliveryCounter 598 * @return The time to wait before the next redelivery. 599 */ 600 protected long determineRedeliveryDelay(Exchange exchange, RedeliveryPolicy redeliveryPolicy, long redeliveryDelay, int redeliveryCounter) { 601 Message message = exchange.getIn(); 602 Long delay = message.getHeader(Exchange.REDELIVERY_DELAY, Long.class); 603 if (delay == null) { 604 delay = redeliveryPolicy.calculateRedeliveryDelay(redeliveryDelay, redeliveryCounter); 605 log.debug("Redelivery delay calculated as {}", delay); 606 } else { 607 log.debug("Redelivery delay is {} from Message Header [{}]", delay, Exchange.REDELIVERY_DELAY); 608 } 609 return delay; 610 } 611 612 /** 613 * This logic is only executed if we have to retry redelivery asynchronously, which have to be done from the callback. 614 * <p/> 615 * And therefore the logic is a bit different than the synchronous <tt>processErrorHandler</tt> method which can use 616 * a loop based redelivery technique. However this means that these two methods in overall have to be in <b>sync</b> 617 * in terms of logic. 618 */ 619 protected void processAsyncErrorHandler(final Exchange exchange, final AsyncCallback callback, final RedeliveryData data) { 620 // can we still run 621 if (!isRunAllowed(data)) { 622 log.trace("Run not allowed, will reject executing exchange: {}", exchange); 623 if (exchange.getException() == null) { 624 exchange.setException(new RejectedExecutionException()); 625 } 626 callback.done(data.sync); 627 return; 628 } 629 630 // did previous processing cause an exception? 631 boolean handle = shouldHandleException(exchange); 632 if (handle) { 633 handleException(exchange, data, isDeadLetterChannel()); 634 onExceptionOccurred(exchange, data); 635 } 636 637 // compute if we are exhausted or not 638 boolean exhausted = isExhausted(exchange, data); 639 if (exhausted) { 640 Processor target = null; 641 boolean deliver = true; 642 643 // the unit of work may have an optional callback associated we need to leverage 644 UnitOfWork uow = exchange.getUnitOfWork(); 645 if (uow != null) { 646 SubUnitOfWorkCallback uowCallback = uow.getSubUnitOfWorkCallback(); 647 if (uowCallback != null) { 648 // signal to the callback we are exhausted 649 uowCallback.onExhausted(exchange); 650 // do not deliver to the failure processor as its been handled by the callback instead 651 deliver = false; 652 } 653 } 654 655 if (deliver) { 656 // should deliver to failure processor (either from onException or the dead letter channel) 657 target = data.failureProcessor != null ? data.failureProcessor : deadLetter; 658 } 659 // we should always invoke the deliverToFailureProcessor as it prepares, logs and does a fair 660 // bit of work for exhausted exchanges (its only the target processor which may be null if handled by a savepoint) 661 boolean isDeadLetterChannel = isDeadLetterChannel() && target == deadLetter; 662 deliverToFailureProcessor(target, isDeadLetterChannel, exchange, data, callback); 663 // we are breaking out 664 return; 665 } 666 667 if (data.redeliveryCounter > 0) { 668 // we are doing a redelivery then a thread pool must be configured (see the doStart method) 669 ObjectHelper.notNull(executorService, "Redelivery is enabled but ExecutorService has not been configured.", this); 670 671 // let the RedeliverTask be the logic which tries to redeliver the Exchange which we can used a scheduler to 672 // have it being executed in the future, or immediately 673 // Note: the data.redeliverFromSync should be kept as is, in case it was enabled previously 674 // to ensure the callback will continue routing from where we left 675 AsyncRedeliveryTask task = new AsyncRedeliveryTask(exchange, callback, data); 676 677 // calculate the redelivery delay 678 data.redeliveryDelay = determineRedeliveryDelay(exchange, data.currentRedeliveryPolicy, data.redeliveryDelay, data.redeliveryCounter); 679 680 if (data.redeliveryDelay > 0) { 681 // schedule the redelivery task 682 if (log.isTraceEnabled()) { 683 log.trace("Scheduling redelivery task to run in {} millis for exchangeId: {}", data.redeliveryDelay, exchange.getExchangeId()); 684 } 685 executorService.schedule(task, data.redeliveryDelay, TimeUnit.MILLISECONDS); 686 } else { 687 // execute the task immediately 688 executorService.submit(task); 689 } 690 } 691 } 692 693 /** 694 * Performs a defensive copy of the exchange if needed 695 * 696 * @param exchange the exchange 697 * @return the defensive copy, or <tt>null</tt> if not needed (redelivery is not enabled). 698 */ 699 protected Exchange defensiveCopyExchangeIfNeeded(Exchange exchange) { 700 // only do a defensive copy if redelivery is enabled 701 if (redeliveryEnabled) { 702 return ExchangeHelper.createCopy(exchange, true); 703 } else { 704 return null; 705 } 706 } 707 708 /** 709 * Strategy whether the exchange has an exception that we should try to handle. 710 * <p/> 711 * Standard implementations should just look for an exception. 712 */ 713 protected boolean shouldHandleException(Exchange exchange) { 714 return exchange.getException() != null; 715 } 716 717 /** 718 * Strategy to determine if the exchange is done so we can continue 719 */ 720 protected boolean isDone(Exchange exchange) { 721 boolean answer = isCancelledOrInterrupted(exchange); 722 723 // only done if the exchange hasn't failed 724 // and it has not been handled by the failure processor 725 // or we are exhausted 726 if (!answer) { 727 answer = exchange.getException() == null 728 || ExchangeHelper.isFailureHandled(exchange) 729 || ExchangeHelper.isRedeliveryExhausted(exchange); 730 } 731 732 log.trace("Is exchangeId: {} done? {}", exchange.getExchangeId(), answer); 733 return answer; 734 } 735 736 /** 737 * Strategy to determine if the exchange was cancelled or interrupted 738 */ 739 protected boolean isCancelledOrInterrupted(Exchange exchange) { 740 boolean answer = false; 741 742 if (ExchangeHelper.isInterrupted(exchange)) { 743 // mark the exchange to stop continue routing when interrupted 744 // as we do not want to continue routing (for example a task has been cancelled) 745 exchange.setProperty(Exchange.ROUTE_STOP, Boolean.TRUE); 746 answer = true; 747 } 748 749 log.trace("Is exchangeId: {} interrupted? {}", exchange.getExchangeId(), answer); 750 return answer; 751 } 752 753 /** 754 * Returns the output processor 755 */ 756 public Processor getOutput() { 757 return output; 758 } 759 760 /** 761 * Returns the dead letter that message exchanges will be sent to if the 762 * redelivery attempts fail 763 */ 764 public Processor getDeadLetter() { 765 return deadLetter; 766 } 767 768 public String getDeadLetterUri() { 769 return deadLetterUri; 770 } 771 772 public boolean isUseOriginalMessagePolicy() { 773 return useOriginalMessagePolicy; 774 } 775 776 public boolean isDeadLetterHandleNewException() { 777 return deadLetterHandleNewException; 778 } 779 780 public RedeliveryPolicy getRedeliveryPolicy() { 781 return redeliveryPolicy; 782 } 783 784 public CamelLogger getLogger() { 785 return logger; 786 } 787 788 protected Predicate getDefaultHandledPredicate() { 789 // Default is not not handle errors 790 return null; 791 } 792 793 protected void prepareExchangeForContinue(Exchange exchange, RedeliveryData data, boolean isDeadLetterChannel) { 794 Exception caught = exchange.getException(); 795 796 // we continue so clear any exceptions 797 exchange.setException(null); 798 // clear rollback flags 799 exchange.setProperty(Exchange.ROLLBACK_ONLY, null); 800 // reset cached streams so they can be read again 801 MessageHelper.resetStreamCache(exchange.getIn()); 802 803 // its continued then remove traces of redelivery attempted and caught exception 804 exchange.getIn().removeHeader(Exchange.REDELIVERED); 805 exchange.getIn().removeHeader(Exchange.REDELIVERY_COUNTER); 806 exchange.getIn().removeHeader(Exchange.REDELIVERY_MAX_COUNTER); 807 exchange.removeProperty(Exchange.FAILURE_HANDLED); 808 // keep the Exchange.EXCEPTION_CAUGHT as property so end user knows the caused exception 809 810 // create log message 811 String msg = "Failed delivery for " + ExchangeHelper.logIds(exchange); 812 msg = msg + ". Exhausted after delivery attempt: " + data.redeliveryCounter + " caught: " + caught; 813 msg = msg + ". Handled and continue routing."; 814 815 // log that we failed but want to continue 816 logFailedDelivery(false, false, false, true, isDeadLetterChannel, exchange, msg, data, null); 817 } 818 819 protected void prepareExchangeForRedelivery(Exchange exchange, RedeliveryData data) { 820 if (!redeliveryEnabled) { 821 throw new IllegalStateException("Redelivery is not enabled on " + this + ". Make sure you have configured the error handler properly."); 822 } 823 // there must be a defensive copy of the exchange 824 ObjectHelper.notNull(data.original, "Defensive copy of Exchange is null", this); 825 826 // okay we will give it another go so clear the exception so we can try again 827 exchange.setException(null); 828 829 // clear rollback flags 830 exchange.setProperty(Exchange.ROLLBACK_ONLY, null); 831 832 // TODO: We may want to store these as state on RedeliveryData so we keep them in case end user messes with Exchange 833 // and then put these on the exchange when doing a redelivery / fault processor 834 835 // preserve these headers 836 Integer redeliveryCounter = exchange.getIn().getHeader(Exchange.REDELIVERY_COUNTER, Integer.class); 837 Integer redeliveryMaxCounter = exchange.getIn().getHeader(Exchange.REDELIVERY_MAX_COUNTER, Integer.class); 838 Boolean redelivered = exchange.getIn().getHeader(Exchange.REDELIVERED, Boolean.class); 839 840 // we are redelivering so copy from original back to exchange 841 exchange.getIn().copyFrom(data.original.getIn()); 842 exchange.setOut(null); 843 // reset cached streams so they can be read again 844 MessageHelper.resetStreamCache(exchange.getIn()); 845 846 // put back headers 847 if (redeliveryCounter != null) { 848 exchange.getIn().setHeader(Exchange.REDELIVERY_COUNTER, redeliveryCounter); 849 } 850 if (redeliveryMaxCounter != null) { 851 exchange.getIn().setHeader(Exchange.REDELIVERY_MAX_COUNTER, redeliveryMaxCounter); 852 } 853 if (redelivered != null) { 854 exchange.getIn().setHeader(Exchange.REDELIVERED, redelivered); 855 } 856 } 857 858 protected void handleException(Exchange exchange, RedeliveryData data, boolean isDeadLetterChannel) { 859 Exception e = exchange.getException(); 860 // e is never null 861 862 Throwable previous = exchange.getProperty(Exchange.EXCEPTION_CAUGHT, Throwable.class); 863 if (previous != null && previous != e) { 864 // a 2nd exception was thrown while handling a previous exception 865 // so we need to add the previous as suppressed by the new exception 866 // see also FatalFallbackErrorHandler 867 Throwable[] suppressed = e.getSuppressed(); 868 boolean found = false; 869 for (Throwable t : suppressed) { 870 if (t == previous) { 871 found = true; 872 } 873 } 874 if (!found) { 875 e.addSuppressed(previous); 876 } 877 } 878 879 // store the original caused exception in a property, so we can restore it later 880 exchange.setProperty(Exchange.EXCEPTION_CAUGHT, e); 881 882 // find the error handler to use (if any) 883 OnExceptionDefinition exceptionPolicy = getExceptionPolicy(exchange, e); 884 if (exceptionPolicy != null) { 885 data.currentRedeliveryPolicy = exceptionPolicy.createRedeliveryPolicy(exchange.getContext(), data.currentRedeliveryPolicy); 886 data.handledPredicate = exceptionPolicy.getHandledPolicy(); 887 data.continuedPredicate = exceptionPolicy.getContinuedPolicy(); 888 data.retryWhilePredicate = exceptionPolicy.getRetryWhilePolicy(); 889 data.useOriginalInMessage = exceptionPolicy.getUseOriginalMessagePolicy() != null && exceptionPolicy.getUseOriginalMessagePolicy(); 890 891 // route specific failure handler? 892 Processor processor = null; 893 UnitOfWork uow = exchange.getUnitOfWork(); 894 if (uow != null && uow.getRouteContext() != null) { 895 String routeId = uow.getRouteContext().getRoute().getId(); 896 processor = exceptionPolicy.getErrorHandler(routeId); 897 } else if (!exceptionPolicy.getErrorHandlers().isEmpty()) { 898 // note this should really not happen, but we have this code as a fail safe 899 // to be backwards compatible with the old behavior 900 log.warn("Cannot determine current route from Exchange with id: {}, will fallback and use first error handler.", exchange.getExchangeId()); 901 processor = exceptionPolicy.getErrorHandlers().iterator().next(); 902 } 903 if (processor != null) { 904 data.failureProcessor = processor; 905 } 906 907 // route specific on redelivery? 908 processor = exceptionPolicy.getOnRedelivery(); 909 if (processor != null) { 910 data.onRedeliveryProcessor = processor; 911 } 912 // route specific on exception occurred? 913 processor = exceptionPolicy.getOnExceptionOccurred(); 914 if (processor != null) { 915 data.onExceptionProcessor = processor; 916 } 917 } 918 919 // only log if not failure handled or not an exhausted unit of work 920 if (!ExchangeHelper.isFailureHandled(exchange) && !ExchangeHelper.isUnitOfWorkExhausted(exchange)) { 921 String msg = "Failed delivery for " + ExchangeHelper.logIds(exchange) 922 + ". On delivery attempt: " + data.redeliveryCounter + " caught: " + e; 923 logFailedDelivery(true, false, false, false, isDeadLetterChannel, exchange, msg, data, e); 924 } 925 926 data.redeliveryCounter = incrementRedeliveryCounter(exchange, e, data); 927 } 928 929 /** 930 * Gives an optional configured OnExceptionOccurred processor a chance to process just after an exception 931 * was thrown while processing the Exchange. This allows to execute the processor at the same time the exception was thrown. 932 */ 933 protected void onExceptionOccurred(Exchange exchange, final RedeliveryData data) { 934 if (data.onExceptionProcessor == null) { 935 return; 936 } 937 938 // run this synchronously as its just a Processor 939 try { 940 if (log.isTraceEnabled()) { 941 log.trace("OnExceptionOccurred processor {} is processing Exchange: {} due exception occurred", data.onExceptionProcessor, exchange); 942 } 943 data.onExceptionProcessor.process(exchange); 944 } catch (Throwable e) { 945 // we dont not want new exception to override existing, so log it as a WARN 946 log.warn("Error during processing OnExceptionOccurred. This exception is ignored.", e); 947 } 948 log.trace("OnExceptionOccurred processor done"); 949 } 950 951 /** 952 * Gives an optional configured redelivery processor a chance to process before the Exchange 953 * will be redelivered. This can be used to alter the Exchange. 954 */ 955 protected void deliverToOnRedeliveryProcessor(final Exchange exchange, final RedeliveryData data) { 956 if (data.onRedeliveryProcessor == null) { 957 return; 958 } 959 960 if (log.isTraceEnabled()) { 961 log.trace("Redelivery processor {} is processing Exchange: {} before its redelivered", 962 data.onRedeliveryProcessor, exchange); 963 } 964 965 // run this synchronously as its just a Processor 966 try { 967 data.onRedeliveryProcessor.process(exchange); 968 } catch (Throwable e) { 969 exchange.setException(e); 970 } 971 log.trace("Redelivery processor done"); 972 } 973 974 /** 975 * All redelivery attempts failed so move the exchange to the dead letter queue 976 */ 977 protected boolean deliverToFailureProcessor(final Processor processor, final boolean isDeadLetterChannel, final Exchange exchange, 978 final RedeliveryData data, final AsyncCallback callback) { 979 boolean sync = true; 980 981 Exception caught = exchange.getException(); 982 983 // we did not success with the redelivery so now we let the failure processor handle it 984 // clear exception as we let the failure processor handle it 985 exchange.setException(null); 986 987 final boolean shouldHandle = shouldHandle(exchange, data); 988 final boolean shouldContinue = shouldContinue(exchange, data); 989 990 // regard both handled or continued as being handled 991 boolean handled = false; 992 993 // always handle if dead letter channel 994 boolean handleOrContinue = isDeadLetterChannel || shouldHandle || shouldContinue; 995 if (handleOrContinue) { 996 // its handled then remove traces of redelivery attempted 997 exchange.getIn().removeHeader(Exchange.REDELIVERED); 998 exchange.getIn().removeHeader(Exchange.REDELIVERY_COUNTER); 999 exchange.getIn().removeHeader(Exchange.REDELIVERY_MAX_COUNTER); 1000 exchange.removeProperty(Exchange.REDELIVERY_EXHAUSTED); 1001 1002 // and remove traces of rollback only and uow exhausted markers 1003 exchange.removeProperty(Exchange.ROLLBACK_ONLY); 1004 exchange.removeProperty(Exchange.UNIT_OF_WORK_EXHAUSTED); 1005 1006 handled = true; 1007 } else { 1008 // must decrement the redelivery counter as we didn't process the redelivery but is 1009 // handling by the failure handler. So we must -1 to not let the counter be out-of-sync 1010 decrementRedeliveryCounter(exchange); 1011 } 1012 1013 // we should allow using the failure processor if we should not continue 1014 // or in case of continue then the failure processor is NOT a dead letter channel 1015 // because you can continue and still let the failure processor do some routing 1016 // before continue in the main route. 1017 boolean allowFailureProcessor = !shouldContinue || !isDeadLetterChannel; 1018 1019 if (allowFailureProcessor && processor != null) { 1020 1021 // prepare original IN body if it should be moved instead of current body 1022 if (data.useOriginalInMessage) { 1023 log.trace("Using the original IN message instead of current"); 1024 Message original = ExchangeHelper.getOriginalInMessage(exchange); 1025 exchange.setIn(original); 1026 if (exchange.hasOut()) { 1027 log.trace("Removing the out message to avoid some uncertain behavior"); 1028 exchange.setOut(null); 1029 } 1030 } 1031 1032 // reset cached streams so they can be read again 1033 MessageHelper.resetStreamCache(exchange.getIn()); 1034 1035 // invoke custom on prepare 1036 if (onPrepareProcessor != null) { 1037 try { 1038 log.trace("OnPrepare processor {} is processing Exchange: {}", onPrepareProcessor, exchange); 1039 onPrepareProcessor.process(exchange); 1040 } catch (Exception e) { 1041 // a new exception was thrown during prepare 1042 exchange.setException(e); 1043 } 1044 } 1045 1046 log.trace("Failure processor {} is processing Exchange: {}", processor, exchange); 1047 1048 // store the last to endpoint as the failure endpoint 1049 exchange.setProperty(Exchange.FAILURE_ENDPOINT, exchange.getProperty(Exchange.TO_ENDPOINT)); 1050 // and store the route id so we know in which route we failed 1051 UnitOfWork uow = exchange.getUnitOfWork(); 1052 if (uow != null && uow.getRouteContext() != null) { 1053 exchange.setProperty(Exchange.FAILURE_ROUTE_ID, uow.getRouteContext().getRoute().getId()); 1054 } 1055 1056 // fire event as we had a failure processor to handle it, which there is a event for 1057 final boolean deadLetterChannel = processor == deadLetter; 1058 1059 EventHelper.notifyExchangeFailureHandling(exchange.getContext(), exchange, processor, deadLetterChannel, deadLetterUri); 1060 1061 // the failure processor could also be asynchronous 1062 AsyncProcessor afp = AsyncProcessorConverterHelper.convert(processor); 1063 sync = afp.process(exchange, new AsyncCallback() { 1064 public void done(boolean sync) { 1065 log.trace("Failure processor done: {} processing Exchange: {}", processor, exchange); 1066 try { 1067 prepareExchangeAfterFailure(exchange, data, isDeadLetterChannel, shouldHandle, shouldContinue); 1068 // fire event as we had a failure processor to handle it, which there is a event for 1069 EventHelper.notifyExchangeFailureHandled(exchange.getContext(), exchange, processor, deadLetterChannel, deadLetterUri); 1070 } finally { 1071 // if the fault was handled asynchronously, this should be reflected in the callback as well 1072 data.sync &= sync; 1073 callback.done(data.sync); 1074 } 1075 } 1076 }); 1077 } else { 1078 try { 1079 // invoke custom on prepare 1080 if (onPrepareProcessor != null) { 1081 try { 1082 log.trace("OnPrepare processor {} is processing Exchange: {}", onPrepareProcessor, exchange); 1083 onPrepareProcessor.process(exchange); 1084 } catch (Exception e) { 1085 // a new exception was thrown during prepare 1086 exchange.setException(e); 1087 } 1088 } 1089 // no processor but we need to prepare after failure as well 1090 prepareExchangeAfterFailure(exchange, data, isDeadLetterChannel, shouldHandle, shouldContinue); 1091 } finally { 1092 // callback we are done 1093 callback.done(data.sync); 1094 } 1095 } 1096 1097 // create log message 1098 String msg = "Failed delivery for " + ExchangeHelper.logIds(exchange); 1099 msg = msg + ". Exhausted after delivery attempt: " + data.redeliveryCounter + " caught: " + caught; 1100 if (processor != null) { 1101 if (isDeadLetterChannel && deadLetterUri != null) { 1102 msg = msg + ". Handled by DeadLetterChannel: [" + URISupport.sanitizeUri(deadLetterUri) + "]"; 1103 } else { 1104 msg = msg + ". Processed by failure processor: " + processor; 1105 } 1106 } 1107 1108 // log that we failed delivery as we are exhausted 1109 logFailedDelivery(false, false, handled, false, isDeadLetterChannel, exchange, msg, data, null); 1110 1111 return sync; 1112 } 1113 1114 protected void prepareExchangeAfterFailure(final Exchange exchange, final RedeliveryData data, final boolean isDeadLetterChannel, 1115 final boolean shouldHandle, final boolean shouldContinue) { 1116 1117 Exception newException = exchange.getException(); 1118 1119 // we could not process the exchange so we let the failure processor handled it 1120 ExchangeHelper.setFailureHandled(exchange); 1121 1122 // honor if already set a handling 1123 boolean alreadySet = exchange.getProperty(Exchange.ERRORHANDLER_HANDLED) != null; 1124 if (alreadySet) { 1125 boolean handled = exchange.getProperty(Exchange.ERRORHANDLER_HANDLED, Boolean.class); 1126 log.trace("This exchange has already been marked for handling: {}", handled); 1127 if (!handled) { 1128 // exception not handled, put exception back in the exchange 1129 exchange.setException(exchange.getProperty(Exchange.EXCEPTION_CAUGHT, Exception.class)); 1130 // and put failure endpoint back as well 1131 exchange.setProperty(Exchange.FAILURE_ENDPOINT, exchange.getProperty(Exchange.TO_ENDPOINT)); 1132 } 1133 return; 1134 } 1135 1136 // dead letter channel is special 1137 if (shouldContinue) { 1138 log.trace("This exchange is continued: {}", exchange); 1139 // okay we want to continue then prepare the exchange for that as well 1140 prepareExchangeForContinue(exchange, data, isDeadLetterChannel); 1141 } else if (shouldHandle) { 1142 log.trace("This exchange is handled so its marked as not failed: {}", exchange); 1143 exchange.setProperty(Exchange.ERRORHANDLER_HANDLED, Boolean.TRUE); 1144 } else { 1145 // okay the redelivery policy are not explicit set to true, so we should allow to check for some 1146 // special situations when using dead letter channel 1147 if (isDeadLetterChannel) { 1148 1149 // DLC is always handling the first thrown exception, 1150 // but if its a new exception then use the configured option 1151 boolean handled = newException == null || deadLetterHandleNewException; 1152 1153 // when using DLC then log new exception whether its being handled or not, as otherwise it may appear as 1154 // the DLC swallow new exceptions by default (which is by design to ensure the DLC always complete, 1155 // to avoid causing endless poison messages that fails forever) 1156 if (newException != null && data.currentRedeliveryPolicy.isLogNewException()) { 1157 String uri = URISupport.sanitizeUri(deadLetterUri); 1158 String msg = "New exception occurred during processing by the DeadLetterChannel[" + uri + "] due " + newException.getMessage(); 1159 if (handled) { 1160 msg += ". The new exception is being handled as deadLetterHandleNewException=true."; 1161 } else { 1162 msg += ". The new exception is not handled as deadLetterHandleNewException=false."; 1163 } 1164 logFailedDelivery(false, true, handled, false, true, exchange, msg, data, newException); 1165 } 1166 1167 if (handled) { 1168 log.trace("This exchange is handled so its marked as not failed: {}", exchange); 1169 exchange.setProperty(Exchange.ERRORHANDLER_HANDLED, Boolean.TRUE); 1170 return; 1171 } 1172 } 1173 1174 // not handled by default 1175 prepareExchangeAfterFailureNotHandled(exchange); 1176 } 1177 } 1178 1179 private void prepareExchangeAfterFailureNotHandled(Exchange exchange) { 1180 log.trace("This exchange is not handled or continued so its marked as failed: {}", exchange); 1181 // exception not handled, put exception back in the exchange 1182 exchange.setProperty(Exchange.ERRORHANDLER_HANDLED, Boolean.FALSE); 1183 exchange.setException(exchange.getProperty(Exchange.EXCEPTION_CAUGHT, Exception.class)); 1184 // and put failure endpoint back as well 1185 exchange.setProperty(Exchange.FAILURE_ENDPOINT, exchange.getProperty(Exchange.TO_ENDPOINT)); 1186 // and store the route id so we know in which route we failed 1187 UnitOfWork uow = exchange.getUnitOfWork(); 1188 if (uow != null && uow.getRouteContext() != null) { 1189 exchange.setProperty(Exchange.FAILURE_ROUTE_ID, uow.getRouteContext().getRoute().getId()); 1190 } 1191 } 1192 1193 private void logFailedDelivery(boolean shouldRedeliver, boolean newException, boolean handled, boolean continued, boolean isDeadLetterChannel, 1194 Exchange exchange, String message, RedeliveryData data, Throwable e) { 1195 if (logger == null) { 1196 return; 1197 } 1198 1199 if (!exchange.isRollbackOnly()) { 1200 if (newException && !data.currentRedeliveryPolicy.isLogNewException()) { 1201 // do not log new exception 1202 return; 1203 } 1204 1205 // if we should not rollback, then check whether logging is enabled 1206 1207 if (!newException && handled && !data.currentRedeliveryPolicy.isLogHandled()) { 1208 // do not log handled 1209 return; 1210 } 1211 1212 if (!newException && continued && !data.currentRedeliveryPolicy.isLogContinued()) { 1213 // do not log handled 1214 return; 1215 } 1216 1217 if (!newException && shouldRedeliver && !data.currentRedeliveryPolicy.isLogRetryAttempted()) { 1218 // do not log retry attempts 1219 return; 1220 } 1221 1222 if (!newException && !shouldRedeliver && !data.currentRedeliveryPolicy.isLogExhausted()) { 1223 // do not log exhausted 1224 return; 1225 } 1226 } 1227 1228 LoggingLevel newLogLevel; 1229 boolean logStackTrace; 1230 if (exchange.isRollbackOnly()) { 1231 newLogLevel = data.currentRedeliveryPolicy.getRetriesExhaustedLogLevel(); 1232 logStackTrace = data.currentRedeliveryPolicy.isLogStackTrace(); 1233 } else if (shouldRedeliver) { 1234 newLogLevel = data.currentRedeliveryPolicy.getRetryAttemptedLogLevel(); 1235 logStackTrace = data.currentRedeliveryPolicy.isLogRetryStackTrace(); 1236 } else { 1237 newLogLevel = data.currentRedeliveryPolicy.getRetriesExhaustedLogLevel(); 1238 logStackTrace = data.currentRedeliveryPolicy.isLogStackTrace(); 1239 } 1240 if (e == null) { 1241 e = exchange.getProperty(Exchange.EXCEPTION_CAUGHT, Exception.class); 1242 } 1243 1244 if (newException) { 1245 // log at most WARN level 1246 if (newLogLevel == LoggingLevel.ERROR) { 1247 newLogLevel = LoggingLevel.WARN; 1248 } 1249 String msg = message; 1250 if (msg == null) { 1251 msg = "New exception " + ExchangeHelper.logIds(exchange); 1252 // special for logging the new exception 1253 Throwable cause = e; 1254 if (cause != null) { 1255 msg = msg + " due: " + cause.getMessage(); 1256 } 1257 } 1258 1259 if (e != null && logStackTrace) { 1260 logger.log(msg, e, newLogLevel); 1261 } else { 1262 logger.log(msg, newLogLevel); 1263 } 1264 } else if (exchange.isRollbackOnly()) { 1265 String msg = "Rollback " + ExchangeHelper.logIds(exchange); 1266 Throwable cause = exchange.getException() != null ? exchange.getException() : exchange.getProperty(Exchange.EXCEPTION_CAUGHT, Throwable.class); 1267 if (cause != null) { 1268 msg = msg + " due: " + cause.getMessage(); 1269 } 1270 1271 // should we include message history 1272 if (!shouldRedeliver && data.currentRedeliveryPolicy.isLogExhaustedMessageHistory()) { 1273 // only use the exchange formatter if we should log exhausted message body (and if using a custom formatter then always use it) 1274 ExchangeFormatter formatter = customExchangeFormatter 1275 ? exchangeFormatter : (data.currentRedeliveryPolicy.isLogExhaustedMessageBody() || camelContext.isLogExhaustedMessageBody() ? exchangeFormatter : null); 1276 String routeStackTrace = MessageHelper.dumpMessageHistoryStacktrace(exchange, formatter, false); 1277 if (routeStackTrace != null) { 1278 msg = msg + "\n" + routeStackTrace; 1279 } 1280 } 1281 1282 if (newLogLevel == LoggingLevel.ERROR) { 1283 // log intended rollback on maximum WARN level (no ERROR) 1284 logger.log(msg, LoggingLevel.WARN); 1285 } else { 1286 // otherwise use the desired logging level 1287 logger.log(msg, newLogLevel); 1288 } 1289 } else { 1290 String msg = message; 1291 // should we include message history 1292 if (!shouldRedeliver && data.currentRedeliveryPolicy.isLogExhaustedMessageHistory()) { 1293 // only use the exchange formatter if we should log exhausted message body (and if using a custom formatter then always use it) 1294 ExchangeFormatter formatter = customExchangeFormatter 1295 ? exchangeFormatter : (data.currentRedeliveryPolicy.isLogExhaustedMessageBody() || camelContext.isLogExhaustedMessageBody() ? exchangeFormatter : null); 1296 String routeStackTrace = MessageHelper.dumpMessageHistoryStacktrace(exchange, formatter, e != null && logStackTrace); 1297 if (routeStackTrace != null) { 1298 msg = msg + "\n" + routeStackTrace; 1299 } 1300 } 1301 1302 if (e != null && logStackTrace) { 1303 logger.log(msg, e, newLogLevel); 1304 } else { 1305 logger.log(msg, newLogLevel); 1306 } 1307 } 1308 } 1309 1310 /** 1311 * Determines whether the exchange is exhausted (or anyway marked to not continue such as rollback). 1312 * <p/> 1313 * If the exchange is exhausted, then we will not continue processing, but let the 1314 * failure processor deal with the exchange. 1315 * 1316 * @param exchange the current exchange 1317 * @param data the redelivery data 1318 * @return <tt>false</tt> to continue/redeliver, or <tt>true</tt> to exhaust. 1319 */ 1320 private boolean isExhausted(Exchange exchange, RedeliveryData data) { 1321 // if marked as rollback only then do not continue/redeliver 1322 boolean exhausted = exchange.getProperty(Exchange.REDELIVERY_EXHAUSTED, false, Boolean.class); 1323 if (exhausted) { 1324 log.trace("This exchange is marked as redelivery exhausted: {}", exchange); 1325 return true; 1326 } 1327 1328 // if marked as rollback only then do not continue/redeliver 1329 boolean rollbackOnly = exchange.getProperty(Exchange.ROLLBACK_ONLY, false, Boolean.class); 1330 if (rollbackOnly) { 1331 log.trace("This exchange is marked as rollback only, so forcing it to be exhausted: {}", exchange); 1332 return true; 1333 } 1334 // its the first original call so continue 1335 if (data.redeliveryCounter == 0) { 1336 return false; 1337 } 1338 // its a potential redelivery so determine if we should redeliver or not 1339 boolean redeliver = data.currentRedeliveryPolicy.shouldRedeliver(exchange, data.redeliveryCounter, data.retryWhilePredicate); 1340 return !redeliver; 1341 } 1342 1343 /** 1344 * Determines whether or not to continue if we are exhausted. 1345 * 1346 * @param exchange the current exchange 1347 * @param data the redelivery data 1348 * @return <tt>true</tt> to continue, or <tt>false</tt> to exhaust. 1349 */ 1350 private boolean shouldContinue(Exchange exchange, RedeliveryData data) { 1351 if (data.continuedPredicate != null) { 1352 return data.continuedPredicate.matches(exchange); 1353 } 1354 // do not continue by default 1355 return false; 1356 } 1357 1358 /** 1359 * Determines whether or not to handle if we are exhausted. 1360 * 1361 * @param exchange the current exchange 1362 * @param data the redelivery data 1363 * @return <tt>true</tt> to handle, or <tt>false</tt> to exhaust. 1364 */ 1365 private boolean shouldHandle(Exchange exchange, RedeliveryData data) { 1366 if (data.handledPredicate != null) { 1367 return data.handledPredicate.matches(exchange); 1368 } 1369 // do not handle by default 1370 return false; 1371 } 1372 1373 /** 1374 * Increments the redelivery counter and adds the redelivered flag if the 1375 * message has been redelivered 1376 */ 1377 private int incrementRedeliveryCounter(Exchange exchange, Throwable e, RedeliveryData data) { 1378 Message in = exchange.getIn(); 1379 Integer counter = in.getHeader(Exchange.REDELIVERY_COUNTER, Integer.class); 1380 int next = 1; 1381 if (counter != null) { 1382 next = counter + 1; 1383 } 1384 in.setHeader(Exchange.REDELIVERY_COUNTER, next); 1385 in.setHeader(Exchange.REDELIVERED, Boolean.TRUE); 1386 // if maximum redeliveries is used, then provide that information as well 1387 if (data.currentRedeliveryPolicy.getMaximumRedeliveries() > 0) { 1388 in.setHeader(Exchange.REDELIVERY_MAX_COUNTER, data.currentRedeliveryPolicy.getMaximumRedeliveries()); 1389 } 1390 return next; 1391 } 1392 1393 /** 1394 * Prepares the redelivery counter and boolean flag for the failure handle processor 1395 */ 1396 private void decrementRedeliveryCounter(Exchange exchange) { 1397 Message in = exchange.getIn(); 1398 Integer counter = in.getHeader(Exchange.REDELIVERY_COUNTER, Integer.class); 1399 if (counter != null) { 1400 int prev = counter - 1; 1401 in.setHeader(Exchange.REDELIVERY_COUNTER, prev); 1402 // set boolean flag according to counter 1403 in.setHeader(Exchange.REDELIVERED, prev > 0 ? Boolean.TRUE : Boolean.FALSE); 1404 } else { 1405 // not redelivered 1406 in.setHeader(Exchange.REDELIVERY_COUNTER, 0); 1407 in.setHeader(Exchange.REDELIVERED, Boolean.FALSE); 1408 } 1409 } 1410 1411 /** 1412 * Determines if redelivery is enabled by checking if any of the redelivery policy 1413 * settings may allow redeliveries. 1414 * 1415 * @return <tt>true</tt> if redelivery is possible, <tt>false</tt> otherwise 1416 * @throws Exception can be thrown 1417 */ 1418 public boolean determineIfRedeliveryIsEnabled() throws Exception { 1419 // determine if redeliver is enabled either on error handler 1420 if (getRedeliveryPolicy().getMaximumRedeliveries() != 0) { 1421 // must check for != 0 as (-1 means redeliver forever) 1422 return true; 1423 } 1424 if (retryWhilePolicy != null) { 1425 return true; 1426 } 1427 1428 // or on the exception policies 1429 if (!exceptionPolicies.isEmpty()) { 1430 // walk them to see if any of them have a maximum redeliveries > 0 or retry until set 1431 for (OnExceptionDefinition def : exceptionPolicies.values()) { 1432 1433 String ref = def.getRedeliveryPolicyRef(); 1434 if (ref != null) { 1435 // lookup in registry if ref provided 1436 RedeliveryPolicy policy = CamelContextHelper.mandatoryLookup(camelContext, ref, RedeliveryPolicy.class); 1437 if (policy.getMaximumRedeliveries() != 0) { 1438 // must check for != 0 as (-1 means redeliver forever) 1439 return true; 1440 } 1441 } else if (def.getRedeliveryPolicy() != null) { 1442 Integer max = CamelContextHelper.parseInteger(camelContext, def.getRedeliveryPolicy().getMaximumRedeliveries()); 1443 if (max != null && max != 0) { 1444 // must check for != 0 as (-1 means redeliver forever) 1445 return true; 1446 } 1447 } 1448 1449 if (def.getRetryWhilePolicy() != null || def.getRetryWhile() != null) { 1450 return true; 1451 } 1452 } 1453 } 1454 1455 return false; 1456 } 1457 1458 /** 1459 * Gets the number of exchanges that are pending for redelivery 1460 */ 1461 public int getPendingRedeliveryCount() { 1462 int answer = redeliverySleepCounter.get(); 1463 if (executorService != null && executorService instanceof ThreadPoolExecutor) { 1464 answer += ((ThreadPoolExecutor) executorService).getQueue().size(); 1465 } 1466 1467 return answer; 1468 } 1469 1470 @Override 1471 protected void doStart() throws Exception { 1472 ServiceHelper.startServices(output, outputAsync, deadLetter); 1473 1474 // determine if redeliver is enabled or not 1475 redeliveryEnabled = determineIfRedeliveryIsEnabled(); 1476 if (log.isTraceEnabled()) { 1477 log.trace("Redelivery enabled: {} on error handler: {}", redeliveryEnabled, this); 1478 } 1479 1480 // we only need thread pool if redelivery is enabled 1481 if (redeliveryEnabled) { 1482 if (executorService == null) { 1483 // use default shared executor service 1484 executorService = camelContext.getErrorHandlerExecutorService(); 1485 } 1486 if (log.isDebugEnabled()) { 1487 log.debug("Using ExecutorService: {} for redeliveries on error handler: {}", executorService, this); 1488 } 1489 } 1490 1491 // reset flag when starting 1492 preparingShutdown = false; 1493 redeliverySleepCounter.set(0); 1494 } 1495 1496 @Override 1497 protected void doStop() throws Exception { 1498 // noop, do not stop any services which we only do when shutting down 1499 // as the error handler can be context scoped, and should not stop in case 1500 // a route stops 1501 } 1502 1503 @Override 1504 protected void doShutdown() throws Exception { 1505 ServiceHelper.stopAndShutdownServices(deadLetter, output, outputAsync); 1506 } 1507}