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: 829746 $
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 don't 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 = null;
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 // wrap exception in transacted exception
122 if (exchange.getException() != null) {
123 rce = wrapTransactedRuntimeException(exchange.getException());
124 }
125
126 if (activeTx && !status.isRollbackOnly()) {
127 status.setRollbackOnly();
128 if (LOG.isDebugEnabled()) {
129 if (rce != null) {
130 LOG.debug("Setting transaction to rollbackOnly due to exception being thrown: " + rce.getMessage());
131 } else {
132 LOG.debug("Setting transaction to rollbackOnly as Exchange was marked as rollback only");
133 }
134 }
135 }
136
137 // rethrow if an exception occurred
138 if (rce != null) {
139 throw rce;
140 }
141 }
142 }
143 });
144
145 log.trace("Transaction error handler done");
146 }
147
148 @Override
149 protected boolean shouldHandleException(Exchange exchange) {
150 boolean answer = false;
151 if (exchange.getException() != null) {
152 answer = true;
153 // handle onException
154 // but test beforehand if we have already handled it, if so we should not do it again
155 if (exchange.getException() instanceof TransactedRuntimeCamelException) {
156 TransactedRuntimeCamelException trce = exchange.getException(TransactedRuntimeCamelException.class);
157 answer = !trce.isHandled();
158 }
159 }
160 return answer;
161 }
162
163 protected TransactedRuntimeCamelException wrapTransactedRuntimeException(Exception exception) {
164 if (exception instanceof TransactedRuntimeCamelException) {
165 return (TransactedRuntimeCamelException) exception;
166 } else {
167 // Mark as handled so we don't want to handle the same exception twice or more in other
168 // wrapped transaction error handlers in this route.
169 // We need to mark this information in the exception as we need to propagate
170 // the exception back by rehtrowing it. We cannot mark it on the exchange as Camel
171 // uses copies of exchanges in its pipeline and the data isn't copied back in case
172 // when an exception occurred
173 return new TransactedRuntimeCamelException(exception, true);
174 }
175 }
176
177 protected String propagationBehaviorToString(int propagationBehavior) {
178 String rc;
179 switch (propagationBehavior) {
180 case TransactionDefinition.PROPAGATION_MANDATORY:
181 rc = "PROPAGATION_MANDATORY";
182 break;
183 case TransactionDefinition.PROPAGATION_NESTED:
184 rc = "PROPAGATION_NESTED";
185 break;
186 case TransactionDefinition.PROPAGATION_NEVER:
187 rc = "PROPAGATION_NEVER";
188 break;
189 case TransactionDefinition.PROPAGATION_NOT_SUPPORTED:
190 rc = "PROPAGATION_NOT_SUPPORTED";
191 break;
192 case TransactionDefinition.PROPAGATION_REQUIRED:
193 rc = "PROPAGATION_REQUIRED";
194 break;
195 case TransactionDefinition.PROPAGATION_REQUIRES_NEW:
196 rc = "PROPAGATION_REQUIRES_NEW";
197 break;
198 case TransactionDefinition.PROPAGATION_SUPPORTS:
199 rc = "PROPAGATION_SUPPORTS";
200 break;
201 default:
202 rc = "UNKNOWN";
203 }
204 return rc;
205 }
206
207 }