BATCH-1213:
*Code cleanup of CoreNamespacePostProcessor and CoreNamespaceBeanDefinitionUtils. *Ensure that steps defined with the batch namespace always use StepParseStepFactoryBean *Additional unit tests for the behavior of mixed hierarchies of batch and beans namespace defined steps
This commit is contained in:
@@ -111,6 +111,7 @@ public abstract class AbstractStepParser {
|
||||
boolean stepUnderspecified) {
|
||||
|
||||
bd.setBeanClass(StepParserStepFactoryBean.class);
|
||||
bd.setAttribute("isNamespaceStep", true);
|
||||
|
||||
String taskletRef = taskletElement.getAttribute(TASKLET_REF_ATTR);
|
||||
@SuppressWarnings("unchecked")
|
||||
|
||||
@@ -15,10 +15,8 @@
|
||||
*/
|
||||
package org.springframework.batch.core.configuration.xml;
|
||||
|
||||
import org.springframework.batch.core.repository.JobRepository;
|
||||
import org.springframework.batch.core.step.AbstractStep;
|
||||
import org.springframework.beans.PropertyValue;
|
||||
import org.springframework.beans.PropertyValues;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
|
||||
@@ -29,29 +27,38 @@ import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
public class CoreNamespaceBeanDefinitionUtils {
|
||||
|
||||
/**
|
||||
* @param name the name of a bean definition in the bean factory
|
||||
* @param beanName the name of a bean definition in the bean factory
|
||||
* @param beanFactory a {@link BeanFactory}
|
||||
* @return TRUE if the bean represents an {@link AbstractStep} (or
|
||||
* {@link StepParserStepFactoryBean}).
|
||||
* {@link StepParserStepFactoryBean}).
|
||||
*/
|
||||
public static boolean isAbstractStep(String name, ConfigurableListableBeanFactory beanFactory) {
|
||||
if (beanFactory.isFactoryBean(name)) {
|
||||
return beanFactory.isTypeMatch(BeanFactory.FACTORY_BEAN_PREFIX + name, StepParserStepFactoryBean.class);
|
||||
public static boolean isAbstractStep(String beanName, ConfigurableListableBeanFactory beanFactory) {
|
||||
if (beanFactory.isFactoryBean(beanName)) {
|
||||
return beanFactory.isTypeMatch(BeanFactory.FACTORY_BEAN_PREFIX + beanName, StepParserStepFactoryBean.class);
|
||||
}
|
||||
return beanFactory.isTypeMatch(name, AbstractStep.class);
|
||||
return beanFactory.isTypeMatch(beanName, AbstractStep.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param name a bean definition name
|
||||
* @param beanName a bean definition name
|
||||
* @param propertyName the name of the property
|
||||
* @param beanFactory a {@link BeanFactory}
|
||||
* @return The {@link PropertyValue} for the {@link JobRepository} of the
|
||||
* bean. Search parent hierarchy if necessary. Return null if none is found.
|
||||
* @return The {@link PropertyValue} for the property of the bean. Search
|
||||
* parent hierarchy if necessary. Return null if none is found.
|
||||
*/
|
||||
public static PropertyValue getPropertyValue(String name, String propertyName,
|
||||
public static PropertyValue getPropertyValue(String beanName, String propertyName,
|
||||
ConfigurableListableBeanFactory beanFactory) {
|
||||
PropertyValues jobDefPvs = beanFactory.getMergedBeanDefinition(name).getPropertyValues();
|
||||
return jobDefPvs.getPropertyValue(propertyName);
|
||||
return beanFactory.getMergedBeanDefinition(beanName).getPropertyValues().getPropertyValue(propertyName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param beanName a bean definition name
|
||||
* @param attributeName the name of the property
|
||||
* @param beanFactory a {@link BeanFactory}
|
||||
* @return The value for the attribute of the bean. Search parent hierarchy
|
||||
* if necessary. Return null if none is found.
|
||||
*/
|
||||
public static Object getAttribute(String beanName, String attributeName, ConfigurableListableBeanFactory beanFactory) {
|
||||
return beanFactory.getMergedBeanDefinition(beanName).getAttribute(attributeName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
@@ -47,44 +48,60 @@ public class CoreNamespacePostProcessor implements BeanPostProcessor, BeanFactor
|
||||
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
|
||||
for (String beanName : beanFactory.getBeanDefinitionNames()) {
|
||||
injectJobRepositoryIntoSteps(beanName, beanFactory);
|
||||
overrideStepClass(beanName, beanFactory);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Automatically inject job-repository from a job into its steps. Only
|
||||
* inject if the step is an AbstractStep or StepParserStepFactoryBean.
|
||||
*
|
||||
* @see org.springframework.beans.factory.config.BeanFactoryPostProcessor#postProcessBeanFactory(org.springframework.beans.factory.config.ConfigurableListableBeanFactory)
|
||||
* @param beanName
|
||||
* @param beanFactory
|
||||
*/
|
||||
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
|
||||
for (String beanName : beanFactory.getBeanDefinitionNames()) {
|
||||
BeanDefinition bd = beanFactory.getBeanDefinition(beanName);
|
||||
if (bd.hasAttribute(JOB_FACTORY_PROPERTY_NAME)) {
|
||||
MutablePropertyValues pvs = (MutablePropertyValues) bd.getPropertyValues();
|
||||
if (CoreNamespaceBeanDefinitionUtils.isAbstractStep(beanName, beanFactory)) {
|
||||
String jobName = (String) bd.getAttribute(JOB_FACTORY_PROPERTY_NAME);
|
||||
PropertyValue jobRepository = getJobRepository(jobName, beanFactory);
|
||||
if (jobRepository != null) {
|
||||
// Set the job's JobRepository onto the step
|
||||
pvs.addPropertyValue(jobRepository);
|
||||
}
|
||||
else {
|
||||
// No JobRepository found, so inject the default
|
||||
RuntimeBeanReference jobRepositoryBeanRef = new RuntimeBeanReference(
|
||||
DEFAULT_JOB_REPOSITORY_NAME);
|
||||
pvs.addPropertyValue(JOB_REPOSITORY_PROPERTY_NAME, jobRepositoryBeanRef);
|
||||
}
|
||||
private void injectJobRepositoryIntoSteps(String beanName, ConfigurableListableBeanFactory beanFactory) {
|
||||
BeanDefinition bd = beanFactory.getBeanDefinition(beanName);
|
||||
if (bd.hasAttribute(JOB_FACTORY_PROPERTY_NAME)) {
|
||||
MutablePropertyValues pvs = (MutablePropertyValues) bd.getPropertyValues();
|
||||
if (CoreNamespaceBeanDefinitionUtils.isAbstractStep(beanName, beanFactory)) {
|
||||
String jobName = (String) bd.getAttribute(JOB_FACTORY_PROPERTY_NAME);
|
||||
PropertyValue jobRepository = CoreNamespaceBeanDefinitionUtils.getPropertyValue(jobName,
|
||||
JOB_REPOSITORY_PROPERTY_NAME, beanFactory);
|
||||
if (jobRepository != null) {
|
||||
// Set the job's JobRepository onto the step
|
||||
pvs.addPropertyValue(jobRepository);
|
||||
}
|
||||
else {
|
||||
// No JobRepository found, so inject the default
|
||||
RuntimeBeanReference jobRepositoryBeanRef = new RuntimeBeanReference(DEFAULT_JOB_REPOSITORY_NAME);
|
||||
pvs.addPropertyValue(JOB_REPOSITORY_PROPERTY_NAME, jobRepositoryBeanRef);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param jobName
|
||||
* If any of the beans in the parent hierarchy is a <step/> with a
|
||||
* <tasklet/>, then the bean class must be
|
||||
* {@link StepParserStepFactoryBean}.
|
||||
*
|
||||
* @param beanName
|
||||
* @param beanFactory
|
||||
* @return The {@link PropertyValue} for the {@link JobRepository} of the
|
||||
* bean. Search parent hierarchy if necessary. Return null if none
|
||||
* is found.
|
||||
*/
|
||||
private PropertyValue getJobRepository(String jobName, ConfigurableListableBeanFactory beanFactory) {
|
||||
return CoreNamespaceBeanDefinitionUtils.getPropertyValue(jobName, JOB_REPOSITORY_PROPERTY_NAME, beanFactory);
|
||||
private void overrideStepClass(String beanName, ConfigurableListableBeanFactory beanFactory) {
|
||||
BeanDefinition bd = beanFactory.getBeanDefinition(beanName);
|
||||
Object isNamespaceStep = CoreNamespaceBeanDefinitionUtils
|
||||
.getAttribute(beanName, "isNamespaceStep", beanFactory);
|
||||
if (isNamespaceStep != null && (Boolean) isNamespaceStep == true) {
|
||||
((AbstractBeanDefinition) bd).setBeanClass(StepParserStepFactoryBean.class);
|
||||
}
|
||||
}
|
||||
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
|
||||
return injectDefaults(bean);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -96,10 +113,10 @@ public class CoreNamespacePostProcessor implements BeanPostProcessor, BeanFactor
|
||||
* {@link StepParserStepFactoryBean} without a transactionManager.
|
||||
* </ul>
|
||||
*
|
||||
* @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessBeforeInitialization(java.lang.Object,
|
||||
* java.lang.String)
|
||||
* @param bean
|
||||
* @return
|
||||
*/
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
|
||||
private Object injectDefaults(Object bean) {
|
||||
if (bean instanceof JobParserJobFactoryBean) {
|
||||
JobParserJobFactoryBean fb = (JobParserJobFactoryBean) bean;
|
||||
JobRepository jobRepository = fb.getJobRepository();
|
||||
|
||||
@@ -317,15 +317,73 @@ public class StepParserTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTaskletElementOverridesParentBeanClass() {
|
||||
public void testInlineTaskletElementOverridesParentBeanClass() {
|
||||
ApplicationContext ctx = stepParserParentAttributeTestsCtx;
|
||||
|
||||
assertTrue(ctx.containsBean("&s12"));
|
||||
Object factoryBean = ctx.getBean("&s12");
|
||||
assertTrue(factoryBean instanceof StepParserStepFactoryBean);
|
||||
|
||||
assertTrue(ctx.containsBean("dummyStep"));
|
||||
Object dummyStep = ctx.getBean("dummyStep");
|
||||
assertTrue(dummyStep instanceof DummyStep);
|
||||
|
||||
assertTrue(ctx.containsBean("s12"));
|
||||
Object bean = ctx.getBean("s12");
|
||||
assertTrue(bean instanceof TaskletStep);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTaskletElementOverridesChildBeanClass() {
|
||||
ApplicationContext ctx = stepParserParentAttributeTestsCtx;
|
||||
|
||||
assertTrue(ctx.containsBean("&s13"));
|
||||
Object factoryBean = ctx.getBean("&s13");
|
||||
assertTrue(factoryBean instanceof StepParserStepFactoryBean);
|
||||
|
||||
assertTrue(ctx.containsBean("s13"));
|
||||
Object bean = ctx.getBean("s13");
|
||||
assertTrue(bean instanceof TaskletStep);
|
||||
|
||||
assertTrue(ctx.containsBean("&dummyStepWithTaskletOnParent"));
|
||||
Object dummyStepFb = ctx.getBean("&dummyStepWithTaskletOnParent");
|
||||
assertTrue(dummyStepFb instanceof StepParserStepFactoryBean);
|
||||
|
||||
assertTrue(ctx.containsBean("dummyStepWithTaskletOnParent"));
|
||||
Object dummyStep = ctx.getBean("dummyStepWithTaskletOnParent");
|
||||
assertTrue(dummyStep instanceof TaskletStep);
|
||||
|
||||
assertTrue(ctx.containsBean("&standaloneStepWithTasklet"));
|
||||
Object standaloneStepFb = ctx.getBean("&standaloneStepWithTasklet");
|
||||
assertTrue(standaloneStepFb instanceof StepParserStepFactoryBean);
|
||||
|
||||
assertTrue(ctx.containsBean("standaloneStepWithTasklet"));
|
||||
Object standaloneStep = ctx.getBean("standaloneStepWithTasklet");
|
||||
assertTrue(standaloneStep instanceof TaskletStep);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTaskletElementOverridesParentBeanClass() {
|
||||
ApplicationContext ctx = stepParserParentAttributeTestsCtx;
|
||||
|
||||
assertTrue(ctx.containsBean("&s14"));
|
||||
Object factoryBean = ctx.getBean("&s14");
|
||||
assertTrue(factoryBean instanceof StepParserStepFactoryBean);
|
||||
|
||||
assertTrue(ctx.containsBean("s12"));
|
||||
Object bean = ctx.getBean("s12");
|
||||
assertTrue(bean instanceof TaskletStep);
|
||||
|
||||
assertTrue(ctx.containsBean("&standaloneStepWithTaskletAndDummyParent"));
|
||||
Object standaloneWithTaskletFb = ctx.getBean("&standaloneStepWithTaskletAndDummyParent");
|
||||
assertTrue(standaloneWithTaskletFb instanceof StepParserStepFactoryBean);
|
||||
|
||||
assertTrue(ctx.containsBean("standaloneStepWithTaskletAndDummyParent"));
|
||||
Object standaloneWithTasklet = ctx.getBean("standaloneStepWithTaskletAndDummyParent");
|
||||
assertTrue(standaloneWithTasklet instanceof TaskletStep);
|
||||
|
||||
assertTrue(ctx.containsBean("dummyStep"));
|
||||
Object dummyStep = ctx.getBean("dummyStep");
|
||||
assertTrue(dummyStep instanceof DummyStep);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,8 +39,10 @@
|
||||
<step id="s12" parent="dummyStep">
|
||||
<tasklet ref="dummyTasklet"/>
|
||||
</step>
|
||||
<step id="s13" parent="dummyStepWithTaskletOnParent"/>
|
||||
<step id="s14" parent="standaloneStepWithTaskletAndDummyParent"/>
|
||||
</job>
|
||||
|
||||
|
||||
<step id="standalone2" parent="baseStep">
|
||||
<tasklet>
|
||||
<chunk reader="reader" writer="writer" commit-interval="5" />
|
||||
@@ -90,6 +92,17 @@
|
||||
|
||||
<beans:bean id="dummyStep" class="org.springframework.batch.core.configuration.xml.DummyStep"/>
|
||||
|
||||
<beans:bean id="dummyStepWithTaskletOnParent" parent="standaloneStepWithTasklet"
|
||||
class="org.springframework.batch.core.configuration.xml.DummyTasklet"/>
|
||||
|
||||
<step id="standaloneStepWithTasklet">
|
||||
<tasklet ref="dummyTasklet"/>
|
||||
</step>
|
||||
|
||||
<step id="standaloneStepWithTaskletAndDummyParent" parent="dummyStep">
|
||||
<tasklet ref="dummyTasklet"/>
|
||||
</step>
|
||||
|
||||
<job id="jobWithoutRepo">
|
||||
<step id="defaultRepoStep"><tasklet ref="dummyTasklet"/></step>
|
||||
<step id="defaultRepoStepWithParent" parent="defaultRepoStandaloneStep"><tasklet ref="dummyTasklet"/></step>
|
||||
|
||||
Reference in New Issue
Block a user