001/*
002 * Copyright 2006-2013 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 *      https://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 */
016package org.springframework.batch.core.configuration.xml;
017
018import org.springframework.batch.core.repository.JobRepository;
019import org.springframework.batch.core.step.AbstractStep;
020import org.springframework.beans.BeansException;
021import org.springframework.beans.MutablePropertyValues;
022import org.springframework.beans.PropertyValue;
023import org.springframework.beans.factory.config.BeanDefinition;
024import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
025import org.springframework.beans.factory.config.BeanPostProcessor;
026import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
027import org.springframework.beans.factory.config.RuntimeBeanReference;
028import org.springframework.beans.factory.support.AbstractBeanDefinition;
029import org.springframework.context.ApplicationContext;
030import org.springframework.context.ApplicationContextAware;
031import org.springframework.transaction.PlatformTransactionManager;
032
033/**
034 * Post-process jobs and steps defined using the batch namespace to inject
035 * dependencies.
036 *
037 * @author Dan Garrette
038 * @since 2.0.1
039 */
040public class CoreNamespacePostProcessor implements BeanPostProcessor, BeanFactoryPostProcessor, ApplicationContextAware {
041
042        private static final String DEFAULT_JOB_REPOSITORY_NAME = "jobRepository";
043
044        private static final String DEFAULT_TRANSACTION_MANAGER_NAME = "transactionManager";
045
046        private static final String JOB_FACTORY_PROPERTY_NAME = "jobParserJobFactoryBeanRef";
047
048        private static final String JOB_REPOSITORY_PROPERTY_NAME = "jobRepository";
049
050        private ApplicationContext applicationContext;
051
052        @Override
053        public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
054                for (String beanName : beanFactory.getBeanDefinitionNames()) {
055                        injectJobRepositoryIntoSteps(beanName, beanFactory);
056                        overrideStepClass(beanName, beanFactory);
057                }
058        }
059
060        /**
061         * Automatically inject job-repository from a job into its steps. Only
062         * inject if the step is an AbstractStep or StepParserStepFactoryBean.
063         *
064         * @param beanName
065         * @param beanFactory
066         */
067        private void injectJobRepositoryIntoSteps(String beanName, ConfigurableListableBeanFactory beanFactory) {
068                BeanDefinition bd = beanFactory.getBeanDefinition(beanName);
069                if (bd.hasAttribute(JOB_FACTORY_PROPERTY_NAME)) {
070                        MutablePropertyValues pvs = bd.getPropertyValues();
071                        if (beanFactory.isTypeMatch(beanName, AbstractStep.class)) {
072                                String jobName = (String) bd.getAttribute(JOB_FACTORY_PROPERTY_NAME);
073                                PropertyValue jobRepository = BeanDefinitionUtils.getPropertyValue(jobName,
074                                                JOB_REPOSITORY_PROPERTY_NAME, beanFactory);
075                                if (jobRepository != null) {
076                                        // Set the job's JobRepository onto the step
077                                        pvs.addPropertyValue(jobRepository);
078                                }
079                                else {
080                                        // No JobRepository found, so inject the default
081                                        RuntimeBeanReference jobRepositoryBeanRef = new RuntimeBeanReference(DEFAULT_JOB_REPOSITORY_NAME);
082                                        pvs.addPropertyValue(JOB_REPOSITORY_PROPERTY_NAME, jobRepositoryBeanRef);
083                                }
084                        }
085                }
086        }
087
088        /**
089         * If any of the beans in the parent hierarchy is a <step/> with a
090         * <tasklet/>, then the bean class must be
091         * {@link StepParserStepFactoryBean}.
092         *
093         * @param beanName
094         * @param beanFactory
095         */
096        private void overrideStepClass(String beanName, ConfigurableListableBeanFactory beanFactory) {
097                BeanDefinition bd = beanFactory.getBeanDefinition(beanName);
098                Object isNamespaceStep = BeanDefinitionUtils
099                                .getAttribute(beanName, "isNamespaceStep", beanFactory);
100                if (isNamespaceStep != null && (Boolean) isNamespaceStep == true) {
101                        ((AbstractBeanDefinition) bd).setBeanClass(StepParserStepFactoryBean.class);
102                }
103        }
104
105        @Override
106        public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
107                return injectDefaults(bean);
108        }
109
110        /**
111         * Inject defaults into factory beans.
112         * <ul>
113         * <li>Inject "jobRepository" into any {@link JobParserJobFactoryBean}
114         * without a jobRepository.
115         * <li>Inject "transactionManager" into any
116         * {@link StepParserStepFactoryBean} without a transactionManager.
117         * </ul>
118         *
119         * @param bean
120         * @return
121         */
122        private Object injectDefaults(Object bean) {
123                if (bean instanceof JobParserJobFactoryBean) {
124                        JobParserJobFactoryBean fb = (JobParserJobFactoryBean) bean;
125                        JobRepository jobRepository = fb.getJobRepository();
126                        if (jobRepository == null) {
127                                fb.setJobRepository((JobRepository) applicationContext.getBean(DEFAULT_JOB_REPOSITORY_NAME));
128                        }
129                } else if (bean instanceof StepParserStepFactoryBean) {
130                        StepParserStepFactoryBean<?, ?> fb = (StepParserStepFactoryBean<?, ?>) bean;
131                        JobRepository jobRepository = fb.getJobRepository();
132                        if (jobRepository == null) {
133                                fb.setJobRepository((JobRepository) applicationContext.getBean(DEFAULT_JOB_REPOSITORY_NAME));
134                        }
135                        PlatformTransactionManager transactionManager = fb.getTransactionManager();
136                        if (transactionManager == null && fb.requiresTransactionManager()) {
137                                fb.setTransactionManager((PlatformTransactionManager) applicationContext
138                                                .getBean(DEFAULT_TRANSACTION_MANAGER_NAME));
139                        }
140                }
141                return bean;
142        }
143
144        @Override
145        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
146                return bean;
147        }
148
149        @Override
150        public void setApplicationContext(ApplicationContext applicationContext) {
151                this.applicationContext = applicationContext;
152        }
153}