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    }