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