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