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 }