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