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 }