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.ExchangeProperty;
021 import org.apache.camel.Processor;
022 import org.apache.camel.RuntimeCamelException;
023 import org.apache.camel.processor.DelayPolicy;
024 import org.apache.camel.processor.DelegateProcessor;
025 import org.apache.camel.processor.RedeliveryPolicy;
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 import static org.apache.camel.util.ObjectHelper.wrapRuntimeCamelException;
036
037 /**
038 * The <a href="http://activemq.apache.org/camel/transactional-client.html">Transactional Client</a>
039 * EIP pattern.
040 *
041 * @version $Revision: 695721 $
042 */
043 public class TransactionInterceptor extends DelegateProcessor {
044 public static final ExchangeProperty<Boolean> TRANSACTED =
045 new ExchangeProperty<Boolean>("transacted", "org.apache.camel.transacted", Boolean.class);
046 private static final transient Log LOG = LogFactory.getLog(TransactionInterceptor.class);
047 private final TransactionTemplate transactionTemplate;
048 private RedeliveryPolicy redeliveryPolicy;
049 private DelayPolicy delayPolicy;
050
051 public TransactionInterceptor(TransactionTemplate transactionTemplate) {
052 this.transactionTemplate = transactionTemplate;
053 }
054
055 public TransactionInterceptor(Processor processor, TransactionTemplate transactionTemplate) {
056 super(processor);
057 this.transactionTemplate = transactionTemplate;
058 }
059
060 /**
061 * @deprecated use DelayPolicy. Will be removed in Camel 2.0
062 */
063 public TransactionInterceptor(Processor processor, TransactionTemplate transactionTemplate, RedeliveryPolicy redeliveryPolicy) {
064 this(processor, transactionTemplate);
065 this.redeliveryPolicy = redeliveryPolicy;
066 this.delayPolicy = redeliveryPolicy;
067 }
068
069 public TransactionInterceptor(Processor processor, TransactionTemplate transactionTemplate, DelayPolicy delayPolicy) {
070 this(processor, transactionTemplate);
071 this.delayPolicy = delayPolicy;
072 }
073
074 @Override
075 public String toString() {
076 return "TransactionInterceptor:"
077 + propagationBehaviorToString(transactionTemplate.getPropagationBehavior())
078 + "[" + getProcessor() + "]";
079 }
080
081 public void process(final Exchange exchange) {
082 transactionTemplate.execute(new TransactionCallbackWithoutResult() {
083 protected void doInTransactionWithoutResult(TransactionStatus status) {
084
085 // wrapper exception to throw if the exchange failed
086 // IMPORTANT: Must be a runtime exception to let Spring regard it as to do "rollback"
087 RuntimeCamelException rce = null;
088
089 boolean activeTx = false;
090 try {
091 // find out if there is an actual transaction alive, and thus we are in transacted mode
092 activeTx = TransactionSynchronizationManager.isActualTransactionActive();
093 if (!activeTx) {
094 activeTx = status.isNewTransaction() && !status.isCompleted();
095 if (!activeTx) {
096 if (DefaultTransactionStatus.class.isAssignableFrom(status.getClass())) {
097 DefaultTransactionStatus defStatus =
098 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 TRANSACTED.set(exchange, Boolean.TRUE);
111 }
112
113 // process the exchange
114 processNext(exchange);
115
116 // wrap if the exchange failed with an exception
117 if (exchange.getException() != null) {
118 rce = wrapRuntimeCamelException(exchange.getException());
119 }
120 } catch (Exception e) {
121 rce = wrapRuntimeCamelException(e);
122 }
123
124 // rethrow exception if the exchange failed
125 if (rce != null) {
126 // an exception occured so please sleep before we rethrow the exception
127 delayBeforeRedelivery();
128 if (activeTx) {
129 status.setRollbackOnly();
130 LOG.debug("Setting transaction to rollbackOnly due to exception being thrown: " + rce.getMessage());
131 }
132 throw rce;
133 }
134 }
135 });
136 }
137
138 /**
139 * Sleeps before the transaction is set as rollback and the caused exception is rethrown to let the
140 * Spring TransactionManager handle the rollback.
141 */
142 protected void delayBeforeRedelivery() {
143 long delay = 0;
144 if (redeliveryPolicy != null) {
145 delay = redeliveryPolicy.getDelay();
146 } else if (delayPolicy != null) {
147 delay = delayPolicy.getDelay();
148 }
149
150 if (delay > 0) {
151 try {
152 if (LOG.isDebugEnabled()) {
153 LOG.debug("Sleeping for: " + delay + " millis until attempting redelivery");
154 }
155 Thread.sleep(delay);
156 } catch (InterruptedException e) {
157 if (LOG.isDebugEnabled()) {
158 LOG.debug("Thread interrupted: " + e, e);
159 }
160 }
161 }
162 }
163
164 /**
165 * @deprecated use DelayPolicy. Will be removed in Camel 2.0
166 */
167 public RedeliveryPolicy getRedeliveryPolicy() {
168 return redeliveryPolicy;
169 }
170
171 /**
172 * @deprecated use DelayPolicy. Will be removed in Camel 2.0
173 */
174 public void setRedeliveryPolicy(RedeliveryPolicy redeliveryPolicy) {
175 this.redeliveryPolicy = redeliveryPolicy;
176 }
177
178 public DelayPolicy getDelayPolicy() {
179 return delayPolicy;
180 }
181
182 public void setDelayPolicy(DelayPolicy delayPolicy) {
183 this.delayPolicy = delayPolicy;
184 }
185
186 protected String propagationBehaviorToString(int propagationBehavior) {
187 String rc;
188 switch (propagationBehavior) {
189 case TransactionDefinition.PROPAGATION_MANDATORY:
190 rc = "PROPAGATION_MANDATORY";
191 break;
192 case TransactionDefinition.PROPAGATION_NESTED:
193 rc = "PROPAGATION_NESTED";
194 break;
195 case TransactionDefinition.PROPAGATION_NEVER:
196 rc = "PROPAGATION_NEVER";
197 break;
198 case TransactionDefinition.PROPAGATION_NOT_SUPPORTED:
199 rc = "PROPAGATION_NOT_SUPPORTED";
200 break;
201 case TransactionDefinition.PROPAGATION_REQUIRED:
202 rc = "PROPAGATION_REQUIRED";
203 break;
204 case TransactionDefinition.PROPAGATION_REQUIRES_NEW:
205 rc = "PROPAGATION_REQUIRES_NEW";
206 break;
207 case TransactionDefinition.PROPAGATION_SUPPORTS:
208 rc = "PROPAGATION_SUPPORTS";
209 break;
210 default:
211 rc = "UNKNOWN";
212 }
213 return rc;
214 }
215
216 }