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.spring.spi; 018 019 import org.apache.camel.Exchange; 020 import org.apache.camel.Predicate; 021 import org.apache.camel.Processor; 022 import org.apache.camel.processor.Logger; 023 import org.apache.camel.processor.RedeliveryErrorHandler; 024 import org.apache.camel.processor.RedeliveryPolicy; 025 import org.apache.camel.processor.exceptionpolicy.ExceptionPolicyStrategy; 026 import org.apache.commons.logging.Log; 027 import org.apache.commons.logging.LogFactory; 028 import org.springframework.transaction.TransactionDefinition; 029 import org.springframework.transaction.TransactionStatus; 030 import org.springframework.transaction.support.DefaultTransactionStatus; 031 import org.springframework.transaction.support.TransactionCallbackWithoutResult; 032 import org.springframework.transaction.support.TransactionSynchronizationManager; 033 import org.springframework.transaction.support.TransactionTemplate; 034 035 /** 036 * The <a href="http://camel.apache.org/transactional-client.html">Transactional Client</a> 037 * EIP pattern. 038 * 039 * @version $Revision: 793373 $ 040 */ 041 public class TransactionErrorHandler extends RedeliveryErrorHandler { 042 043 private static final transient Log LOG = LogFactory.getLog(TransactionErrorHandler.class); 044 private final TransactionTemplate transactionTemplate; 045 046 /** 047 * Creates the transaction error handler. 048 * 049 * @param output outer processor that should use this default error handler 050 * @param logger logger to use for logging failures and redelivery attempts 051 * @param redeliveryProcessor an optional processor to run before redelivery attempt 052 * @param redeliveryPolicy policy for redelivery 053 * @param handledPolicy policy for handling failed exception that are moved to the dead letter queue 054 * @param exceptionPolicyStrategy strategy for onException handling 055 * @param transactionTemplate the transaction template 056 */ 057 public TransactionErrorHandler(Processor output, Logger logger, Processor redeliveryProcessor, 058 RedeliveryPolicy redeliveryPolicy, Predicate handledPolicy, 059 ExceptionPolicyStrategy exceptionPolicyStrategy, TransactionTemplate transactionTemplate) { 060 super(output, logger, redeliveryProcessor, redeliveryPolicy, handledPolicy, null, null, false); 061 setExceptionPolicy(exceptionPolicyStrategy); 062 this.transactionTemplate = transactionTemplate; 063 } 064 065 public boolean supportTransacted() { 066 return true; 067 } 068 069 @Override 070 public String toString() { 071 if (output == null) { 072 // if no output then dont do any description 073 return ""; 074 } 075 return "TransactionErrorHandler:" 076 + propagationBehaviorToString(transactionTemplate.getPropagationBehavior()) 077 + "[" + getOutput() + "]"; 078 } 079 080 public void process(final Exchange exchange) throws Exception { 081 if (log.isTraceEnabled()) { 082 log.trace("Transaction error handler is processing: " + exchange); 083 } 084 085 // just to let the stacktrace reveal that this is a transaction error handler 086 transactionTemplate.execute(new TransactionCallbackWithoutResult() { 087 protected void doInTransactionWithoutResult(TransactionStatus status) { 088 // wrapper exception to throw if the exchange failed 089 // IMPORTANT: Must be a runtime exception to let Spring regard it as to do "rollback" 090 TransactedRuntimeCamelException rce; 091 092 // find out if there is an actual transaction alive, and thus we are in transacted mode 093 boolean activeTx = TransactionSynchronizationManager.isActualTransactionActive(); 094 if (!activeTx) { 095 activeTx = status.isNewTransaction() && !status.isCompleted(); 096 if (!activeTx) { 097 if (DefaultTransactionStatus.class.isAssignableFrom(status.getClass())) { 098 DefaultTransactionStatus defStatus = DefaultTransactionStatus.class.cast(status); 099 activeTx = defStatus.hasTransaction() && !status.isCompleted(); 100 } 101 } 102 } 103 if (LOG.isTraceEnabled()) { 104 LOG.trace("Is actual transaction active: " + activeTx); 105 } 106 107 // okay mark the exchange as transacted, then the DeadLetterChannel or others know 108 // its a transacted exchange 109 if (activeTx) { 110 exchange.setProperty(Exchange.TRANSACTED, Boolean.TRUE); 111 } 112 113 try { 114 TransactionErrorHandler.super.process(exchange); 115 } catch (Exception e) { 116 exchange.setException(e); 117 } 118 119 // after handling and still an exception or marked as rollback only then rollback 120 if (exchange.getException() != null || exchange.isRollbackOnly()) { 121 rce = wrapTransactedRuntimeException(exchange.getException()); 122 123 if (activeTx && !status.isRollbackOnly()) { 124 status.setRollbackOnly(); 125 if (LOG.isDebugEnabled()) { 126 if (rce != null) { 127 LOG.debug("Setting transaction to rollbackOnly due to exception being thrown: " + rce.getMessage()); 128 } else { 129 LOG.debug("Setting transaction to rollbackOnly as Exchange was marked as rollback only"); 130 } 131 } 132 } 133 134 // rethrow if an exception occured 135 if (rce != null) { 136 throw rce; 137 } 138 } 139 } 140 }); 141 142 log.trace("Transaction error handler done"); 143 } 144 145 @Override 146 protected boolean shouldHandleException(Exchange exchange) { 147 boolean answer = false; 148 if (exchange.getException() != null) { 149 answer = true; 150 // handle onException 151 // but test beforehand if we have already handled it, if so we should not do it again 152 if (exchange.getException() instanceof TransactedRuntimeCamelException) { 153 TransactedRuntimeCamelException trce = exchange.getException(TransactedRuntimeCamelException.class); 154 answer = !trce.isHandled(); 155 } 156 } 157 return answer; 158 } 159 160 protected TransactedRuntimeCamelException wrapTransactedRuntimeException(Exception exception) { 161 if (exception instanceof TransactedRuntimeCamelException) { 162 return (TransactedRuntimeCamelException) exception; 163 } else { 164 // Mark as handled so we dont want to handle the same exception twice or more in other 165 // wrapped transaction error handlers in this route. 166 // We need to mark this information in the exception as we need to propagage 167 // the exception back by rehtrowing it. We cannot mark it on the exchange as Camel 168 // uses copies of exchanges in its pipeline and the data isnt copied back in case 169 // when an exception occured 170 // TODO: revist if/when we avoid doing the copying in the pipeline 171 return new TransactedRuntimeCamelException(exception, true); 172 } 173 } 174 175 protected String propagationBehaviorToString(int propagationBehavior) { 176 String rc; 177 switch (propagationBehavior) { 178 case TransactionDefinition.PROPAGATION_MANDATORY: 179 rc = "PROPAGATION_MANDATORY"; 180 break; 181 case TransactionDefinition.PROPAGATION_NESTED: 182 rc = "PROPAGATION_NESTED"; 183 break; 184 case TransactionDefinition.PROPAGATION_NEVER: 185 rc = "PROPAGATION_NEVER"; 186 break; 187 case TransactionDefinition.PROPAGATION_NOT_SUPPORTED: 188 rc = "PROPAGATION_NOT_SUPPORTED"; 189 break; 190 case TransactionDefinition.PROPAGATION_REQUIRED: 191 rc = "PROPAGATION_REQUIRED"; 192 break; 193 case TransactionDefinition.PROPAGATION_REQUIRES_NEW: 194 rc = "PROPAGATION_REQUIRES_NEW"; 195 break; 196 case TransactionDefinition.PROPAGATION_SUPPORTS: 197 rc = "PROPAGATION_SUPPORTS"; 198 break; 199 default: 200 rc = "UNKNOWN"; 201 } 202 return rc; 203 } 204 205 }