001/*
002 * Copyright 2012-2018 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.boot.jta.atomikos;
018
019import java.util.Arrays;
020import java.util.Collections;
021import java.util.LinkedHashSet;
022import java.util.List;
023import java.util.Set;
024
025import com.atomikos.icatch.jta.UserTransactionManager;
026
027import org.springframework.beans.BeansException;
028import org.springframework.beans.factory.config.BeanDefinition;
029import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
030import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
031import org.springframework.core.Ordered;
032import org.springframework.util.StringUtils;
033
034/**
035 * {@link BeanFactoryPostProcessor} to automatically setup the recommended
036 * {@link BeanDefinition#setDependsOn(String[]) dependsOn} settings for
037 * <a href="http://www.atomikos.com/Documentation/SpringIntegration">correct Atomikos
038 * ordering</a>.
039 *
040 * @author Phillip Webb
041 * @since 1.2.0
042 */
043public class AtomikosDependsOnBeanFactoryPostProcessor
044                implements BeanFactoryPostProcessor, Ordered {
045
046        private static final String[] NO_BEANS = {};
047
048        private int order = Ordered.LOWEST_PRECEDENCE;
049
050        @Override
051        public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
052                        throws BeansException {
053                String[] transactionManagers = beanFactory
054                                .getBeanNamesForType(UserTransactionManager.class, true, false);
055                for (String transactionManager : transactionManagers) {
056                        addTransactionManagerDependencies(beanFactory, transactionManager);
057                }
058                addMessageDrivenContainerDependencies(beanFactory, transactionManagers);
059        }
060
061        private void addTransactionManagerDependencies(
062                        ConfigurableListableBeanFactory beanFactory, String transactionManager) {
063                BeanDefinition bean = beanFactory.getBeanDefinition(transactionManager);
064                Set<String> dependsOn = new LinkedHashSet<>(asList(bean.getDependsOn()));
065                int initialSize = dependsOn.size();
066                addDependencies(beanFactory, "javax.jms.ConnectionFactory", dependsOn);
067                addDependencies(beanFactory, "javax.sql.DataSource", dependsOn);
068                if (dependsOn.size() != initialSize) {
069                        bean.setDependsOn(StringUtils.toStringArray(dependsOn));
070                }
071        }
072
073        private void addMessageDrivenContainerDependencies(
074                        ConfigurableListableBeanFactory beanFactory, String[] transactionManagers) {
075                String[] messageDrivenContainers = getBeanNamesForType(beanFactory,
076                                "com.atomikos.jms.extra.MessageDrivenContainer");
077                for (String messageDrivenContainer : messageDrivenContainers) {
078                        BeanDefinition bean = beanFactory.getBeanDefinition(messageDrivenContainer);
079                        Set<String> dependsOn = new LinkedHashSet<>(asList(bean.getDependsOn()));
080                        dependsOn.addAll(asList(transactionManagers));
081                        bean.setDependsOn(StringUtils.toStringArray(dependsOn));
082                }
083        }
084
085        private void addDependencies(ConfigurableListableBeanFactory beanFactory, String type,
086                        Set<String> dependsOn) {
087                dependsOn.addAll(asList(getBeanNamesForType(beanFactory, type)));
088        }
089
090        private String[] getBeanNamesForType(ConfigurableListableBeanFactory beanFactory,
091                        String type) {
092                try {
093                        return beanFactory.getBeanNamesForType(Class.forName(type), true, false);
094                }
095                catch (ClassNotFoundException | NoClassDefFoundError ex) {
096                        // Ignore
097                }
098                return NO_BEANS;
099        }
100
101        private List<String> asList(String[] array) {
102                return (array != null) ? Arrays.asList(array) : Collections.emptyList();
103        }
104
105        @Override
106        public int getOrder() {
107                return this.order;
108        }
109
110        public void setOrder(int order) {
111                this.order = order;
112        }
113
114}