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 */
017package org.apache.camel.spring.spi;
018
019import java.util.Map;
020
021import org.apache.camel.LoggingLevel;
022import org.apache.camel.Processor;
023import org.apache.camel.builder.DefaultErrorHandlerBuilder;
024import org.apache.camel.spi.RouteContext;
025import org.apache.camel.spi.TransactedPolicy;
026import org.apache.camel.util.CamelLogger;
027import org.apache.camel.util.ObjectHelper;
028import org.slf4j.Logger;
029import org.slf4j.LoggerFactory;
030import org.springframework.transaction.PlatformTransactionManager;
031import org.springframework.transaction.support.TransactionTemplate;
032
033/**
034 * A transactional error handler that supports leveraging Spring TransactionManager.
035 *
036 * @version 
037 */
038public class TransactionErrorHandlerBuilder extends DefaultErrorHandlerBuilder {
039
040    private static final Logger LOG = LoggerFactory.getLogger(TransactionErrorHandlerBuilder.class);
041    private static final String PROPAGATION_REQUIRED = "PROPAGATION_REQUIRED";
042    private TransactionTemplate transactionTemplate;
043    private LoggingLevel rollbackLoggingLevel = LoggingLevel.WARN;
044
045    public TransactionErrorHandlerBuilder() {
046        // no-arg constructor used by Spring DSL
047    }
048
049    public TransactionTemplate getTransactionTemplate() {
050        return transactionTemplate;
051    }
052
053    public boolean supportTransacted() {
054        return true;
055    }
056
057    public Processor createErrorHandler(RouteContext routeContext, Processor processor) throws Exception {
058        if (transactionTemplate == null) {
059            // lookup in context if no transaction template has been configured
060            LOG.debug("No TransactionTemplate configured on TransactionErrorHandlerBuilder. Will try find it in the registry.");
061
062            Map<String, TransactedPolicy> mapPolicy = routeContext.lookupByType(TransactedPolicy.class);
063            if (mapPolicy != null && mapPolicy.size() == 1) {
064                TransactedPolicy policy = mapPolicy.values().iterator().next();
065                if (policy instanceof SpringTransactionPolicy) {
066                    transactionTemplate = ((SpringTransactionPolicy) policy).getTransactionTemplate();
067                }
068            }
069
070            if (transactionTemplate == null) {
071                TransactedPolicy policy = routeContext.lookup(PROPAGATION_REQUIRED, TransactedPolicy.class);
072                if (policy instanceof SpringTransactionPolicy) {
073                    transactionTemplate = ((SpringTransactionPolicy) policy).getTransactionTemplate();
074                }
075            }
076
077            if (transactionTemplate == null) {
078                Map<String, TransactionTemplate> mapTemplate = routeContext.lookupByType(TransactionTemplate.class);
079                if (mapTemplate == null || mapTemplate.isEmpty()) {
080                    LOG.trace("No TransactionTemplate found in registry.");
081                } else if (mapTemplate.size() == 1) {
082                    transactionTemplate = mapTemplate.values().iterator().next();
083                } else {
084                    LOG.debug("Found {} TransactionTemplate in registry. Cannot determine which one to use. "
085                              + "Please configure a TransactionTemplate on the TransactionErrorHandlerBuilder", mapTemplate.size());
086                }
087            }
088
089            if (transactionTemplate == null) {
090                Map<String, PlatformTransactionManager> mapManager = routeContext.lookupByType(PlatformTransactionManager.class);
091                if (mapManager == null || mapManager.isEmpty()) {
092                    LOG.trace("No PlatformTransactionManager found in registry.");
093                } else if (mapManager.size() == 1) {
094                    transactionTemplate = new TransactionTemplate(mapManager.values().iterator().next());
095                } else {
096                    LOG.debug("Found {} PlatformTransactionManager in registry. Cannot determine which one to use for TransactionTemplate. "
097                              + "Please configure a TransactionTemplate on the TransactionErrorHandlerBuilder", mapManager.size());
098                }
099            }
100
101            if (transactionTemplate != null) {
102                LOG.debug("Found TransactionTemplate in registry to use: " + transactionTemplate);
103            }
104        }
105
106        ObjectHelper.notNull(transactionTemplate, "transactionTemplate", this);
107
108        TransactionErrorHandler answer = new TransactionErrorHandler(routeContext.getCamelContext(), processor,
109            getLogger(), getOnRedelivery(), getRedeliveryPolicy(), getExceptionPolicyStrategy(), transactionTemplate, 
110            getRetryWhilePolicy(routeContext.getCamelContext()), getExecutorService(routeContext.getCamelContext()), getRollbackLoggingLevel(), getOnExceptionOccurred());
111        // configure error handler before we can use it
112        configure(routeContext, answer);
113        return answer;
114    }
115
116    public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
117        this.transactionTemplate = transactionTemplate;
118    }
119
120    public void setSpringTransactionPolicy(SpringTransactionPolicy policy) {
121        this.transactionTemplate = policy.getTransactionTemplate();
122    }
123
124    public void setTransactionManager(PlatformTransactionManager transactionManager) {
125        this.transactionTemplate = new TransactionTemplate(transactionManager);
126    }
127
128    public LoggingLevel getRollbackLoggingLevel() {
129        return rollbackLoggingLevel;
130    }
131
132    /**
133     * Sets the logging level to use for logging transactional rollback.
134     * <p/>
135     * This option is default WARN.
136     *
137     * @param rollbackLoggingLevel the logging level
138     */
139    public void setRollbackLoggingLevel(LoggingLevel rollbackLoggingLevel) {
140        this.rollbackLoggingLevel = rollbackLoggingLevel;
141    }
142
143    // Builder methods
144    // -------------------------------------------------------------------------
145
146    /**
147     * Sets the logging level to use for logging transactional rollback.
148     * <p/>
149     * This option is default WARN.
150     *
151     * @param rollbackLoggingLevel the logging level
152     */
153    public TransactionErrorHandlerBuilder rollbackLoggingLevel(LoggingLevel rollbackLoggingLevel) {
154        setRollbackLoggingLevel(rollbackLoggingLevel);
155        return this;
156    }
157
158    // Implementation
159    // -------------------------------------------------------------------------
160
161    protected CamelLogger createLogger() {
162        return new CamelLogger(LoggerFactory.getLogger(TransactionErrorHandler.class), LoggingLevel.ERROR);
163    }
164
165    @Override
166    public String toString() {
167        return "TransactionErrorHandlerBuilder";
168    }
169
170}