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://camel.apache.org/transactional-client.html">Transactional Client</a> 038 * EIP pattern. 039 * 040 * @version $Revision: 747759 $ 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 public TransactionInterceptor(Processor processor, TransactionTemplate transactionTemplate, DelayPolicy delayPolicy) { 058 this(processor, transactionTemplate); 059 this.delayPolicy = delayPolicy; 060 } 061 062 @Override 063 public String toString() { 064 return "TransactionInterceptor:" 065 + propagationBehaviorToString(transactionTemplate.getPropagationBehavior()) 066 + "[" + getProcessor() + "]"; 067 } 068 069 public void process(final Exchange exchange) { 070 transactionTemplate.execute(new TransactionCallbackWithoutResult() { 071 protected void doInTransactionWithoutResult(TransactionStatus status) { 072 073 // wrapper exception to throw if the exchange failed 074 // IMPORTANT: Must be a runtime exception to let Spring regard it as to do "rollback" 075 RuntimeCamelException rce = null; 076 077 boolean activeTx = false; 078 try { 079 // find out if there is an actual transaction alive, and thus we are in transacted mode 080 activeTx = TransactionSynchronizationManager.isActualTransactionActive(); 081 if (!activeTx) { 082 activeTx = status.isNewTransaction() && !status.isCompleted(); 083 if (!activeTx) { 084 if (DefaultTransactionStatus.class.isAssignableFrom(status.getClass())) { 085 DefaultTransactionStatus defStatus = DefaultTransactionStatus.class.cast(status); 086 activeTx = defStatus.hasTransaction() && !status.isCompleted(); 087 } 088 } 089 } 090 if (LOG.isTraceEnabled()) { 091 LOG.trace("Is actual transaction active: " + activeTx); 092 } 093 094 // okay mark the exchange as transacted, then the DeadLetterChannel or others know 095 // its a transacted exchange 096 if (activeTx) { 097 exchange.setProperty(Exchange.TRANSACTED, Boolean.TRUE); 098 } 099 100 // process the exchange 101 processNext(exchange); 102 103 // wrap if the exchange failed with an exception 104 if (exchange.getException() != null) { 105 rce = wrapRuntimeCamelException(exchange.getException()); 106 } 107 } catch (Exception e) { 108 rce = wrapRuntimeCamelException(e); 109 } 110 111 // rethrow exception if the exchange failed 112 if (rce != null) { 113 // an exception occured so please sleep before we rethrow the exception 114 delayBeforeRedelivery(); 115 if (activeTx) { 116 status.setRollbackOnly(); 117 LOG.debug("Setting transaction to rollbackOnly due to exception being thrown: " + rce.getMessage()); 118 } 119 throw rce; 120 } 121 } 122 }); 123 } 124 125 /** 126 * Sleeps before the transaction is set as rollback and the caused exception is rethrown to let the 127 * Spring TransactionManager handle the rollback. 128 */ 129 protected void delayBeforeRedelivery() { 130 long delay = 0; 131 if (redeliveryPolicy != null) { 132 delay = redeliveryPolicy.getDelay(); 133 } else if (delayPolicy != null) { 134 delay = delayPolicy.getDelay(); 135 } 136 137 if (delay > 0) { 138 try { 139 if (LOG.isDebugEnabled()) { 140 LOG.debug("Sleeping for: " + delay + " millis until attempting redelivery"); 141 } 142 Thread.sleep(delay); 143 } catch (InterruptedException e) { 144 Thread.currentThread().interrupt(); 145 } 146 } 147 } 148 149 public DelayPolicy getDelayPolicy() { 150 return delayPolicy; 151 } 152 153 public void setDelayPolicy(DelayPolicy delayPolicy) { 154 this.delayPolicy = delayPolicy; 155 } 156 157 protected String propagationBehaviorToString(int propagationBehavior) { 158 String rc; 159 switch (propagationBehavior) { 160 case TransactionDefinition.PROPAGATION_MANDATORY: 161 rc = "PROPAGATION_MANDATORY"; 162 break; 163 case TransactionDefinition.PROPAGATION_NESTED: 164 rc = "PROPAGATION_NESTED"; 165 break; 166 case TransactionDefinition.PROPAGATION_NEVER: 167 rc = "PROPAGATION_NEVER"; 168 break; 169 case TransactionDefinition.PROPAGATION_NOT_SUPPORTED: 170 rc = "PROPAGATION_NOT_SUPPORTED"; 171 break; 172 case TransactionDefinition.PROPAGATION_REQUIRED: 173 rc = "PROPAGATION_REQUIRED"; 174 break; 175 case TransactionDefinition.PROPAGATION_REQUIRES_NEW: 176 rc = "PROPAGATION_REQUIRES_NEW"; 177 break; 178 case TransactionDefinition.PROPAGATION_SUPPORTS: 179 rc = "PROPAGATION_SUPPORTS"; 180 break; 181 default: 182 rc = "UNKNOWN"; 183 } 184 return rc; 185 } 186 187 }