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:
dhgarrette
2009-05-23 15:07:11 +00:00
parent dfbdd5c8a8
commit da58dfc835
5 changed files with 140 additions and 44 deletions

View File

@@ -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")

View File

@@ -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);
}
}

View File

@@ -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();

View File

@@ -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);
}
}

View File

@@ -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>