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 */ 017 package org.apache.camel.processor; 018 019 import java.util.concurrent.RejectedExecutionException; 020 021 import org.apache.camel.Exchange; 022 import org.apache.camel.LoggingLevel; 023 import org.apache.camel.Message; 024 import org.apache.camel.Predicate; 025 import org.apache.camel.Processor; 026 import org.apache.camel.model.OnExceptionDefinition; 027 import org.apache.camel.util.ExchangeHelper; 028 import org.apache.camel.util.MessageHelper; 029 import org.apache.camel.util.ServiceHelper; 030 031 /** 032 * Base redeliverable error handler that also suppors a final dead letter queue in case 033 * all redelivery attempts fail. 034 * <p/> 035 * This implementation should contain all the error handling logic and the sub classes 036 * should only configure it according to what they support. 037 * 038 * @version $Revision: 792966 $ 039 */ 040 public abstract class RedeliveryErrorHandler extends ErrorHandlerSupport implements Processor { 041 042 protected final Processor deadLetter; 043 protected final String deadLetterUri; 044 protected final Processor output; 045 protected final Processor redeliveryProcessor; 046 protected final RedeliveryPolicy redeliveryPolicy; 047 protected final Predicate handledPolicy; 048 protected final Logger logger; 049 protected final boolean useOriginalMessagePolicy; 050 051 protected class RedeliveryData { 052 int redeliveryCounter; 053 long redeliveryDelay; 054 Predicate retryUntilPredicate; 055 056 // default behavior which can be overloaded on a per exception basis 057 RedeliveryPolicy currentRedeliveryPolicy = redeliveryPolicy; 058 Processor deadLetterProcessor = deadLetter; 059 Processor failureProcessor; 060 Processor onRedeliveryProcessor = redeliveryProcessor; 061 Predicate handledPredicate = handledPolicy; 062 boolean useOriginalInMessage = useOriginalMessagePolicy; 063 } 064 065 public RedeliveryErrorHandler(Processor output, Logger logger, Processor redeliveryProcessor, 066 RedeliveryPolicy redeliveryPolicy, Predicate handledPolicy, Processor deadLetter, 067 String deadLetterUri, boolean useOriginalMessagePolicy) { 068 this.redeliveryProcessor = redeliveryProcessor; 069 this.deadLetter = deadLetter; 070 this.output = output; 071 this.redeliveryPolicy = redeliveryPolicy; 072 this.logger = logger; 073 this.deadLetterUri = deadLetterUri; 074 this.handledPolicy = handledPolicy; 075 this.useOriginalMessagePolicy = useOriginalMessagePolicy; 076 } 077 078 public boolean supportTransacted() { 079 return false; 080 } 081 082 public void process(Exchange exchange) throws Exception { 083 if (output == null) { 084 // no output then just return 085 return; 086 } 087 088 processErrorHandler(exchange, new RedeliveryData()); 089 } 090 091 /** 092 * Processes the exchange decorated with this dead letter channel. 093 */ 094 protected void processErrorHandler(final Exchange exchange, final RedeliveryData data) throws Exception { 095 while (true) { 096 // we can't keep retrying if the route is being shutdown. 097 if (!isRunAllowed()) { 098 if (log.isDebugEnabled()) { 099 log.debug("Rejected execution as we are not started for exchange: " + exchange); 100 } 101 if (exchange.getException() == null) { 102 exchange.setException(new RejectedExecutionException()); 103 return; 104 } 105 } 106 107 // do not handle transacted exchanges that failed as this error handler does not support it 108 if (exchange.isTransacted() && !supportTransacted() && exchange.getException() != null) { 109 if (log.isTraceEnabled()) { 110 log.trace("This error handler does not support transacted exchanges." 111 + " Bypassing this error handler: " + this + " for exchangeId: " + exchange.getExchangeId()); 112 } 113 return; 114 } 115 116 // did previous processing cause an exception? 117 boolean handle = shouldHandleException(exchange); 118 if (handle) { 119 handleException(exchange, data); 120 } 121 122 // compute if we should redeliver or not 123 boolean shouldRedeliver = shouldRedeliver(exchange, data); 124 if (!shouldRedeliver) { 125 // no we should not redeliver to the same output so either try an onException (if any given) 126 // or the dead letter queue 127 Processor target = data.failureProcessor != null ? data.failureProcessor : data.deadLetterProcessor; 128 // deliver to the failure processor (either an on exception or dead letter queue 129 deliverToFailureProcessor(target, exchange, data); 130 // prepare the exchange for failure before returning 131 prepareExchangeAfterFailure(exchange, data); 132 // and then return 133 return; 134 } 135 136 // if we are redelivering then sleep before trying again 137 if (shouldRedeliver && data.redeliveryCounter > 0) { 138 prepareExchangeForRedelivery(exchange); 139 140 // wait until we should redeliver 141 try { 142 data.redeliveryDelay = data.currentRedeliveryPolicy.sleep(data.redeliveryDelay, data.redeliveryCounter); 143 } catch (InterruptedException e) { 144 log.debug("Sleep interrupted, are we stopping? " + (isStopping() || isStopped())); 145 // continue from top 146 continue; 147 } 148 149 // letting onRedeliver be executed 150 deliverToRedeliveryProcessor(exchange, data); 151 } 152 153 // process the exchange (also redelivery) 154 try { 155 processExchange(exchange); 156 } catch (Exception e) { 157 exchange.setException(e); 158 } 159 160 boolean done = isDone(exchange); 161 if (done) { 162 return; 163 } 164 // error occurred so loop back around..... 165 } 166 167 } 168 169 /** 170 * Strategy whether the exchange has an exception that we should try to handle. 171 * <p/> 172 * Standard implementations should just look for an exception. 173 */ 174 protected boolean shouldHandleException(Exchange exchange) { 175 return exchange.getException() != null; 176 } 177 178 /** 179 * Strategy to process the given exchange to the destinated output. 180 * <p/> 181 * This happens when the exchange is processed the first time and also for redeliveries 182 * to the same destination. 183 */ 184 protected void processExchange(Exchange exchange) throws Exception { 185 // process the exchange (also redelivery) 186 output.process(exchange); 187 } 188 189 /** 190 * Strategy to determine if the exchange is done so we can continue 191 */ 192 protected boolean isDone(Exchange exchange) throws Exception { 193 // only done if the exchange hasn't failed 194 // and it has not been handled by the failure processor 195 return exchange.getException() == null || ExchangeHelper.isFailureHandled(exchange); 196 } 197 198 /** 199 * Returns the output processor 200 */ 201 public Processor getOutput() { 202 return output; 203 } 204 205 /** 206 * Returns the dead letter that message exchanges will be sent to if the 207 * redelivery attempts fail 208 */ 209 public Processor getDeadLetter() { 210 return deadLetter; 211 } 212 213 public RedeliveryPolicy getRedeliveryPolicy() { 214 return redeliveryPolicy; 215 } 216 217 public Logger getLogger() { 218 return logger; 219 } 220 221 protected void prepareExchangeForRedelivery(Exchange exchange) { 222 // okay we will give it another go so clear the exception so we can try again 223 if (exchange.getException() != null) { 224 exchange.setException(null); 225 } 226 227 // clear rollback flags 228 exchange.setProperty(Exchange.ROLLBACK_ONLY, null); 229 230 // reset cached streams so they can be read again 231 MessageHelper.resetStreamCache(exchange.getIn()); 232 } 233 234 protected void handleException(Exchange exchange, RedeliveryData data) { 235 Throwable e = exchange.getException(); 236 237 // store the original caused exception in a property, so we can restore it later 238 exchange.setProperty(Exchange.EXCEPTION_CAUGHT, e); 239 240 // find the error handler to use (if any) 241 OnExceptionDefinition exceptionPolicy = getExceptionPolicy(exchange, e); 242 if (exceptionPolicy != null) { 243 data.currentRedeliveryPolicy = exceptionPolicy.createRedeliveryPolicy(exchange.getContext(), data.currentRedeliveryPolicy); 244 data.handledPredicate = exceptionPolicy.getHandledPolicy(); 245 data.retryUntilPredicate = exceptionPolicy.getRetryUntilPolicy(); 246 data.useOriginalInMessage = exceptionPolicy.getUseOriginalMessagePolicy(); 247 248 // route specific failure handler? 249 Processor processor = exceptionPolicy.getErrorHandler(); 250 if (processor != null) { 251 data.failureProcessor = processor; 252 } 253 // route specific on redelivey? 254 processor = exceptionPolicy.getOnRedelivery(); 255 if (processor != null) { 256 data.onRedeliveryProcessor = processor; 257 } 258 } 259 260 String msg = "Failed delivery for exchangeId: " + exchange.getExchangeId() 261 + ". On delivery attempt: " + data.redeliveryCounter + " caught: " + e; 262 logFailedDelivery(true, exchange, msg, data, e); 263 264 data.redeliveryCounter = incrementRedeliveryCounter(exchange, e); 265 } 266 267 /** 268 * Gives an optional configure redelivery processor a chance to process before the Exchange 269 * will be redelivered. This can be used to alter the Exchange. 270 */ 271 protected void deliverToRedeliveryProcessor(final Exchange exchange, final RedeliveryData data) { 272 if (data.onRedeliveryProcessor == null) { 273 return; 274 } 275 276 if (log.isTraceEnabled()) { 277 log.trace("Redelivery processor " + data.onRedeliveryProcessor + " is processing Exchange: " + exchange 278 + " before its redelivered"); 279 } 280 281 try { 282 data.onRedeliveryProcessor.process(exchange); 283 } catch (Exception e) { 284 exchange.setException(e); 285 } 286 log.trace("Redelivery processor done"); 287 } 288 289 /** 290 * All redelivery attempts failed so move the exchange to the dead letter queue 291 */ 292 protected void deliverToFailureProcessor(final Processor processor, final Exchange exchange, 293 final RedeliveryData data) { 294 // we did not success with the redelivery so now we let the failure processor handle it 295 // clear exception as we let the failure processor handle it 296 exchange.setException(null); 297 298 if (data.handledPredicate != null && data.handledPredicate.matches(exchange)) { 299 // its handled then remove traces of redelivery attempted 300 exchange.getIn().removeHeader(Exchange.REDELIVERED); 301 exchange.getIn().removeHeader(Exchange.REDELIVERY_COUNTER); 302 } else { 303 // must decrement the redelivery counter as we didn't process the redelivery but is 304 // handling by the failure handler. So we must -1 to not let the counter be out-of-sync 305 decrementRedeliveryCounter(exchange); 306 } 307 308 // reset cached streams so they can be read again 309 MessageHelper.resetStreamCache(exchange.getIn()); 310 311 if (processor != null) { 312 // prepare original IN body if it should be moved instead of current body 313 if (data.useOriginalInMessage) { 314 if (log.isTraceEnabled()) { 315 log.trace("Using the original IN message instead of current"); 316 } 317 318 Message original = exchange.getUnitOfWork().getOriginalInMessage(); 319 exchange.setIn(original); 320 } 321 322 if (log.isTraceEnabled()) { 323 log.trace("Failure processor " + processor + " is processing Exchange: " + exchange); 324 } 325 try { 326 processor.process(exchange); 327 } catch (Exception e) { 328 exchange.setException(e); 329 } 330 log.trace("Failure processor done"); 331 332 String msg = "Failed delivery for exchangeId: " + exchange.getExchangeId() 333 + ". Processed by failure processor: " + processor; 334 logFailedDelivery(false, exchange, msg, data, null); 335 } 336 } 337 338 protected void prepareExchangeAfterFailure(final Exchange exchange, final RedeliveryData data) { 339 // we could not process the exchange so we let the failure processor handled it 340 ExchangeHelper.setFailureHandled(exchange); 341 342 // honor if already set a handling 343 boolean alreadySet = exchange.getProperty(Exchange.ERRORHANDLER_HANDLED) != null; 344 if (alreadySet) { 345 boolean handled = exchange.getProperty(Exchange.ERRORHANDLER_HANDLED, Boolean.class); 346 if (log.isDebugEnabled()) { 347 log.debug("This exchange has already been marked for handling: " + handled); 348 } 349 if (handled) { 350 exchange.setException(null); 351 } else { 352 // exception not handled, put exception back in the exchange 353 exchange.setException(exchange.getProperty(Exchange.EXCEPTION_CAUGHT, Exception.class)); 354 } 355 return; 356 } 357 358 Predicate handledPredicate = data.handledPredicate; 359 if (handledPredicate == null || !handledPredicate.matches(exchange)) { 360 if (log.isDebugEnabled()) { 361 log.debug("This exchange is not handled so its marked as failed: " + exchange); 362 } 363 // exception not handled, put exception back in the exchange 364 exchange.setProperty(Exchange.ERRORHANDLER_HANDLED, Boolean.FALSE); 365 exchange.setException(exchange.getProperty(Exchange.EXCEPTION_CAUGHT, Exception.class)); 366 } else { 367 if (log.isDebugEnabled()) { 368 log.debug("This exchange is handled so its marked as not failed: " + exchange); 369 } 370 exchange.setProperty(Exchange.ERRORHANDLER_HANDLED, Boolean.TRUE); 371 } 372 } 373 374 private void logFailedDelivery(boolean shouldRedeliver, Exchange exchange, String message, RedeliveryData data, Throwable e) { 375 if (logger == null) { 376 return; 377 } 378 379 LoggingLevel newLogLevel; 380 if (shouldRedeliver) { 381 newLogLevel = data.currentRedeliveryPolicy.getRetryAttemptedLogLevel(); 382 } else { 383 newLogLevel = data.currentRedeliveryPolicy.getRetriesExhaustedLogLevel(); 384 } 385 if (exchange.isRollbackOnly()) { 386 String msg = "Rollback exchange"; 387 if (exchange.getException() != null) { 388 msg = msg + " due: " + exchange.getException().getMessage(); 389 } 390 if (newLogLevel == LoggingLevel.ERROR || newLogLevel == LoggingLevel.FATAL) { 391 // log intented rollback on maximum WARN level (no ERROR or FATAL) 392 logger.log(msg, LoggingLevel.WARN); 393 } else { 394 // otherwise use the desired logging level 395 logger.log(msg, newLogLevel); 396 } 397 } else if (data.currentRedeliveryPolicy.isLogStackTrace() && e != null) { 398 logger.log(message, e, newLogLevel); 399 } else { 400 logger.log(message, newLogLevel); 401 } 402 } 403 404 private boolean shouldRedeliver(Exchange exchange, RedeliveryData data) { 405 // if marked as rollback only then do not redeliver 406 Boolean rollback = exchange.getProperty(Exchange.ROLLBACK_ONLY, Boolean.class); 407 if (rollback != null && rollback) { 408 if (log.isTraceEnabled()) { 409 log.trace("This exchange is marked as rollback only, should not be redelivered: " + exchange); 410 } 411 return false; 412 } 413 return data.currentRedeliveryPolicy.shouldRedeliver(exchange, data.redeliveryCounter, data.retryUntilPredicate); 414 } 415 416 /** 417 * Increments the redelivery counter and adds the redelivered flag if the 418 * message has been redelivered 419 */ 420 private int incrementRedeliveryCounter(Exchange exchange, Throwable e) { 421 Message in = exchange.getIn(); 422 Integer counter = in.getHeader(Exchange.REDELIVERY_COUNTER, Integer.class); 423 int next = 1; 424 if (counter != null) { 425 next = counter + 1; 426 } 427 in.setHeader(Exchange.REDELIVERY_COUNTER, next); 428 in.setHeader(Exchange.REDELIVERED, Boolean.TRUE); 429 return next; 430 } 431 432 /** 433 * Prepares the redelivery counter and boolean flag for the failure handle processor 434 */ 435 private void decrementRedeliveryCounter(Exchange exchange) { 436 Message in = exchange.getIn(); 437 Integer counter = in.getHeader(Exchange.REDELIVERY_COUNTER, Integer.class); 438 if (counter != null) { 439 int prev = counter - 1; 440 in.setHeader(Exchange.REDELIVERY_COUNTER, prev); 441 // set boolean flag according to counter 442 in.setHeader(Exchange.REDELIVERED, prev > 0 ? Boolean.TRUE : Boolean.FALSE); 443 } else { 444 // not redelivered 445 in.setHeader(Exchange.REDELIVERY_COUNTER, 0); 446 in.setHeader(Exchange.REDELIVERED, Boolean.FALSE); 447 } 448 } 449 450 @Override 451 protected void doStart() throws Exception { 452 ServiceHelper.startServices(output, deadLetter); 453 } 454 455 @Override 456 protected void doStop() throws Exception { 457 ServiceHelper.stopServices(deadLetter, output); 458 } 459 460 }