diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/AbstractStepParser.java b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/AbstractStepParser.java index a431d4027..e71024087 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/AbstractStepParser.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/AbstractStepParser.java @@ -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") diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/CoreNamespaceBeanDefinitionUtils.java b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/CoreNamespaceBeanDefinitionUtils.java index 693ddd779..53849077c 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/CoreNamespaceBeanDefinitionUtils.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/CoreNamespaceBeanDefinitionUtils.java @@ -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); + } } diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/CoreNamespacePostProcessor.java b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/CoreNamespacePostProcessor.java index 1f4bf7187..630442fbf 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/CoreNamespacePostProcessor.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/CoreNamespacePostProcessor.java @@ -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. * * - * @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(); diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/StepParserTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/StepParserTests.java index bfe414ac4..091973650 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/StepParserTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/StepParserTests.java @@ -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); + } } diff --git a/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/StepParserParentAttributeTests-context.xml b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/StepParserParentAttributeTests-context.xml index 3fa9a82a1..c3b8268b0 100644 --- a/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/StepParserParentAttributeTests-context.xml +++ b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/StepParserParentAttributeTests-context.xml @@ -39,8 +39,10 @@ + + - + @@ -90,6 +92,17 @@ + + + + + + + + + +