View Javadoc

1   /*
2    * Copyright 2006-2013 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.springframework.batch.core.configuration.xml;
17  
18  import org.springframework.batch.core.repository.JobRepository;
19  import org.springframework.batch.core.step.AbstractStep;
20  import org.springframework.beans.BeansException;
21  import org.springframework.beans.MutablePropertyValues;
22  import org.springframework.beans.PropertyValue;
23  import org.springframework.beans.factory.config.BeanDefinition;
24  import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
25  import org.springframework.beans.factory.config.BeanPostProcessor;
26  import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
27  import org.springframework.beans.factory.config.RuntimeBeanReference;
28  import org.springframework.beans.factory.support.AbstractBeanDefinition;
29  import org.springframework.context.ApplicationContext;
30  import org.springframework.context.ApplicationContextAware;
31  import org.springframework.transaction.PlatformTransactionManager;
32  
33  /**
34   * Post-process jobs and steps defined using the batch namespace to inject
35   * dependencies.
36   *
37   * @author Dan Garrette
38   * @since 2.0.1
39   */
40  public class CoreNamespacePostProcessor implements BeanPostProcessor, BeanFactoryPostProcessor, ApplicationContextAware {
41  
42  	private static final String DEFAULT_JOB_REPOSITORY_NAME = "jobRepository";
43  
44  	private static final String DEFAULT_TRANSACTION_MANAGER_NAME = "transactionManager";
45  
46  	private static final String JOB_FACTORY_PROPERTY_NAME = "jobParserJobFactoryBeanRef";
47  
48  	private static final String JOB_REPOSITORY_PROPERTY_NAME = "jobRepository";
49  
50  	private ApplicationContext applicationContext;
51  
52  	@Override
53  	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
54  		for (String beanName : beanFactory.getBeanDefinitionNames()) {
55  			injectJobRepositoryIntoSteps(beanName, beanFactory);
56  			overrideStepClass(beanName, beanFactory);
57  		}
58  	}
59  
60  	/**
61  	 * Automatically inject job-repository from a job into its steps. Only
62  	 * inject if the step is an AbstractStep or StepParserStepFactoryBean.
63  	 *
64  	 * @param beanName
65  	 * @param beanFactory
66  	 */
67  	private void injectJobRepositoryIntoSteps(String beanName, ConfigurableListableBeanFactory beanFactory) {
68  		BeanDefinition bd = beanFactory.getBeanDefinition(beanName);
69  		if (bd.hasAttribute(JOB_FACTORY_PROPERTY_NAME)) {
70  			MutablePropertyValues pvs = bd.getPropertyValues();
71  			if (beanFactory.isTypeMatch(beanName, AbstractStep.class)) {
72  				String jobName = (String) bd.getAttribute(JOB_FACTORY_PROPERTY_NAME);
73  				PropertyValue jobRepository = BeanDefinitionUtils.getPropertyValue(jobName,
74  						JOB_REPOSITORY_PROPERTY_NAME, beanFactory);
75  				if (jobRepository != null) {
76  					// Set the job's JobRepository onto the step
77  					pvs.addPropertyValue(jobRepository);
78  				}
79  				else {
80  					// No JobRepository found, so inject the default
81  					RuntimeBeanReference jobRepositoryBeanRef = new RuntimeBeanReference(DEFAULT_JOB_REPOSITORY_NAME);
82  					pvs.addPropertyValue(JOB_REPOSITORY_PROPERTY_NAME, jobRepositoryBeanRef);
83  				}
84  			}
85  		}
86  	}
87  
88  	/**
89  	 * If any of the beans in the parent hierarchy is a <step/> with a
90  	 * <tasklet/>, then the bean class must be
91  	 * {@link StepParserStepFactoryBean}.
92  	 *
93  	 * @param beanName
94  	 * @param beanFactory
95  	 */
96  	private void overrideStepClass(String beanName, ConfigurableListableBeanFactory beanFactory) {
97  		BeanDefinition bd = beanFactory.getBeanDefinition(beanName);
98  		Object isNamespaceStep = BeanDefinitionUtils
99  				.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 		}
130 		else if (bean instanceof StepParserStepFactoryBean) {
131 			StepParserStepFactoryBean<?, ?> fb = (StepParserStepFactoryBean<?, ?>) bean;
132 			JobRepository jobRepository = fb.getJobRepository();
133 			if (jobRepository == null) {
134 				fb.setJobRepository((JobRepository) applicationContext.getBean(DEFAULT_JOB_REPOSITORY_NAME));
135 			}
136 			PlatformTransactionManager transactionManager = fb.getTransactionManager();
137 			if (transactionManager == null && fb.requiresTransactionManager()) {
138 				fb.setTransactionManager((PlatformTransactionManager) applicationContext
139 						.getBean(DEFAULT_TRANSACTION_MANAGER_NAME));
140 			}
141 		}
142 		return bean;
143 	}
144 
145 	@Override
146 	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
147 		return bean;
148 	}
149 
150 	@Override
151 	public void setApplicationContext(ApplicationContext applicationContext) {
152 		this.applicationContext = applicationContext;
153 	}
154 }