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