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