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 != null && 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 != null && 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.size() == 1) {
080                    transactionTemplate = mapTemplate.values().iterator().next();
081                }
082                if (mapTemplate == null || mapTemplate.isEmpty()) {
083                    LOG.trace("No TransactionTemplate found in registry.");
084                } else {
085                    LOG.debug("Found {} TransactionTemplate in registry. Cannot determine which one to use. "
086                              + "Please configure a TransactionTemplate on the TransactionErrorHandlerBuilder", mapTemplate.size());
087                }
088            }
089
090            if (transactionTemplate == null) {
091                Map<String, PlatformTransactionManager> mapManager = routeContext.lookupByType(PlatformTransactionManager.class);
092                if (mapManager != null && mapManager.size() == 1) {
093                    transactionTemplate = new TransactionTemplate(mapManager.values().iterator().next());
094                }
095                if (mapManager == null || mapManager.isEmpty()) {
096                    LOG.trace("No PlatformTransactionManager found in registry.");
097                } else {
098                    LOG.debug("Found {} PlatformTransactionManager in registry. Cannot determine which one to use for TransactionTemplate. "
099                              + "Please configure a TransactionTemplate on the TransactionErrorHandlerBuilder", mapManager.size());
100                }
101            }
102
103            if (transactionTemplate != null) {
104                LOG.debug("Found TransactionTemplate in registry to use: " + transactionTemplate);
105            }
106        }
107
108        ObjectHelper.notNull(transactionTemplate, "transactionTemplate", this);
109
110        TransactionErrorHandler answer = new TransactionErrorHandler(routeContext.getCamelContext(), processor,
111            getLogger(), getOnRedelivery(), getRedeliveryPolicy(), getExceptionPolicyStrategy(), transactionTemplate, 
112            getRetryWhilePolicy(routeContext.getCamelContext()), getExecutorService(routeContext.getCamelContext()), getRollbackLoggingLevel());
113        // configure error handler before we can use it
114        configure(routeContext, answer);
115        return answer;
116    }
117
118    public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
119        this.transactionTemplate = transactionTemplate;
120    }
121
122    public void setSpringTransactionPolicy(SpringTransactionPolicy policy) {
123        this.transactionTemplate = policy.getTransactionTemplate();
124    }
125
126    public void setTransactionManager(PlatformTransactionManager transactionManager) {
127        this.transactionTemplate = new TransactionTemplate(transactionManager);
128    }
129
130    public LoggingLevel getRollbackLoggingLevel() {
131        return rollbackLoggingLevel;
132    }
133
134    /**
135     * Sets the logging level to use for logging transactional rollback.
136     * <p/>
137     * This option is default WARN.
138     *
139     * @param rollbackLoggingLevel the logging level
140     */
141    public void setRollbackLoggingLevel(LoggingLevel rollbackLoggingLevel) {
142        this.rollbackLoggingLevel = rollbackLoggingLevel;
143    }
144
145    // Builder methods
146    // -------------------------------------------------------------------------
147
148    /**
149     * Sets the logging level to use for logging transactional rollback.
150     * <p/>
151     * This option is default WARN.
152     *
153     * @param rollbackLoggingLevel the logging level
154     */
155    public TransactionErrorHandlerBuilder rollbackLoggingLevel(LoggingLevel rollbackLoggingLevel) {
156        setRollbackLoggingLevel(rollbackLoggingLevel);
157        return this;
158    }
159
160    // Implementation
161    // -------------------------------------------------------------------------
162
163    protected CamelLogger createLogger() {
164        return new CamelLogger(LoggerFactory.getLogger(TransactionErrorHandler.class), LoggingLevel.ERROR);
165    }
166
167    @Override
168    public String toString() {
169        return "TransactionErrorHandlerBuilder";
170    }
171
172}