REOPENED - issue BATCH-1338: Allow segregation of jobs by type or origin

Added parent configuration copying as a feature in ClassPathXmlApplicationContextFactory.
This commit is contained in:
dsyer
2009-09-09 10:45:13 +00:00
parent 6d20540c01
commit f98c5775ed
4 changed files with 178 additions and 3 deletions

View File

@@ -17,6 +17,8 @@
package org.springframework.batch.core.configuration.support;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
@@ -41,6 +43,10 @@ public class ClassPathXmlApplicationContextFactory implements ApplicationContext
private ResourceXmlApplicationContext context;
private boolean copyConfiguration = true;
private boolean copyBeanFactoryPostProcessors = true;
private final Object lock = new Object();
/**
@@ -54,6 +60,27 @@ public class ClassPathXmlApplicationContextFactory implements ApplicationContext
this.path = path;
}
/**
* Flag to indicate that configuration such as bean post processors and
* custom editors should be copied from the parent context. Defaults to
* true;
*
* @param copyConfiguration the flag value to set
*/
public void setCopyConfiguration(boolean copyConfiguration) {
this.copyConfiguration = copyConfiguration;
}
/**
* Flag to indicate that bean factory post processors (like property
* placeholders) should be copied from the parent context. Defaults to true;
*
* @param copyBeanFactoryPostProcessors the flag value to set
*/
public void setCopyBeanFactoryPostProcessors(boolean copyBeanFactoryPostProcessors) {
this.copyBeanFactoryPostProcessors = copyBeanFactoryPostProcessors;
}
/**
* Setter for the parent application context.
*
@@ -92,17 +119,81 @@ public class ClassPathXmlApplicationContextFactory implements ApplicationContext
*
*/
private final class ResourceXmlApplicationContext extends AbstractXmlApplicationContext {
private final DefaultListableBeanFactory parentBeanFactory;
/**
* @param parent
*/
private ResourceXmlApplicationContext(ApplicationContext parent) {
public ResourceXmlApplicationContext(ConfigurableApplicationContext parent) {
super(parent);
if (parent != null) {
Assert.isTrue(parent.getBeanFactory() instanceof DefaultListableBeanFactory,
"The parent application context must have a bean factory of type DefaultListableBeanFactory");
parentBeanFactory = (DefaultListableBeanFactory) parent.getBeanFactory();
refreshBeanFactory();
prepareContext(parent, this);
}
else {
parentBeanFactory = null;
}
refresh();
}
@Override
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
super.customizeBeanFactory(beanFactory);
if (parentBeanFactory != null) {
ClassPathXmlApplicationContextFactory.this.prepareBeanFactory(parentBeanFactory, beanFactory);
}
}
@Override
protected Resource[] getConfigResources() {
return new Resource[] { path };
}
}
/**
* Extension point for special subclasses that want to do more complex
* things with the context prior to refresh. The default implementation
* copies bean factory post processors according to the flag set. The bean
* factory for the context will be available if needed through
* {@link ConfigurableApplicationContext#getBeanFactory()
* context.getBeanFactory()}.
*
* @param parent the parent for the new application context
* @param context the new application context before it is refreshed, but
* after bean factory is initialized
*
* @see ClassPathXmlApplicationContextFactory#setCopyBeanFactoryPostProcessors(boolean)
*/
protected void prepareContext(ConfigurableApplicationContext parent, ConfigurableApplicationContext context) {
if (copyBeanFactoryPostProcessors) {
for (String name : parent.getBeanNamesForType(BeanFactoryPostProcessor.class)) {
context.addBeanFactoryPostProcessor((BeanFactoryPostProcessor) parent.getBean(name));
}
}
}
/**
* Extension point for special subclasses that want to do more complex
* things with the bean factory prior to refresh. The default implementation
* copies all configuration from the parent according to the flag set.
*
* @param parent the parent bean factory for the new context (will never be
* null)
* @param beanFactory the new bean factory before bean definitions are
* loaded
*
* @see ClassPathXmlApplicationContextFactory#setCopyConfiguration(boolean)
* @see DefaultListableBeanFactory#copyConfigurationFrom(ConfigurableBeanFactory)
*/
protected void prepareBeanFactory(DefaultListableBeanFactory parent, DefaultListableBeanFactory beanFactory) {
if (copyConfiguration && parent != null) {
beanFactory.copyConfigurationFrom(parent);
}
}
}

View File

@@ -15,9 +15,13 @@
*/
package org.springframework.batch.core.configuration.support;
import junit.framework.TestCase;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import org.junit.Test;
import org.springframework.batch.core.Job;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.ClassPathResource;
import org.springframework.util.ClassUtils;
@@ -25,18 +29,60 @@ import org.springframework.util.ClassUtils;
* @author Dave Syer
*
*/
public class ClassPathXmlApplicationContextFactoryTests extends TestCase {
public class ClassPathXmlApplicationContextFactoryTests {
private ClassPathXmlApplicationContextFactory factory = new ClassPathXmlApplicationContextFactory();
@Test
public void testCreateJob() {
factory.setPath(new ClassPathResource(ClassUtils.addResourcePathToPackagePath(getClass(), "trivial-context.xml")));
assertNotNull(factory.createApplicationContext());
}
@Test
public void testGetJobName() {
factory.setPath(new ClassPathResource(ClassUtils.addResourcePathToPackagePath(getClass(), "trivial-context.xml")));
assertEquals("test-job", factory.createApplicationContext().getBeanNamesForType(Job.class)[0]);
}
@Test
public void testParentConfigurationInherited() {
factory.setApplicationContext(new ClassPathXmlApplicationContext(ClassUtils.addResourcePathToPackagePath(getClass(), "parent-context.xml")));
factory.setPath(new ClassPathResource(ClassUtils.addResourcePathToPackagePath(getClass(), "child-context.xml")));
ConfigurableApplicationContext context = factory.createApplicationContext();
assertEquals("test-job", context.getBeanNamesForType(Job.class)[0]);
assertEquals("bar", ((Job) context.getBean("test-job", Job.class)).getName());
assertEquals(4, ((Foo) context.getBean("foo", Foo.class)).values[1], 0.01);
}
@Test
public void testBeanFactoryPostProcessorsNotCopied() {
factory.setApplicationContext(new ClassPathXmlApplicationContext(ClassUtils.addResourcePathToPackagePath(getClass(), "parent-context.xml")));
factory.setPath(new ClassPathResource(ClassUtils.addResourcePathToPackagePath(getClass(), "child-context.xml")));
factory.setCopyBeanFactoryPostProcessors(false);
ConfigurableApplicationContext context = factory.createApplicationContext();
assertEquals("test-job", context.getBeanNamesForType(Job.class)[0]);
assertEquals("${foo}", ((Job) context.getBean("test-job", Job.class)).getName());
assertEquals(4, ((Foo) context.getBean("foo", Foo.class)).values[1], 0.01);
}
@Test
public void testBeanFactoryConfigurationNotCopied() {
factory.setApplicationContext(new ClassPathXmlApplicationContext(ClassUtils.addResourcePathToPackagePath(getClass(), "parent-context.xml")));
factory.setPath(new ClassPathResource(ClassUtils.addResourcePathToPackagePath(getClass(), "child-context.xml")));
factory.setCopyConfiguration(false);
ConfigurableApplicationContext context = factory.createApplicationContext();
assertEquals("test-job", context.getBeanNamesForType(Job.class)[0]);
assertEquals("bar", ((Job) context.getBean("test-job", Job.class)).getName());
// The CustomEditorConfigurer is a BeanFactoryPostProcessor so the editor gets copied anyway!
assertEquals(4, ((Foo) context.getBean("foo", Foo.class)).values[1], 0.01);
}
public static class Foo {
private double[] values;
public void setValues(double[] values) {
this.values = values;
}
}
}

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:p="http://www.springframework.org/schema/p" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
<bean id="test-job" class="org.springframework.batch.core.job.JobSupport">
<property name="name" value="${foo}" />
</bean>
<bean id="foo" class="org.springframework.batch.core.configuration.support.ClassPathXmlApplicationContextFactoryTests$Foo">
<property name="values" value="1,4" />
</bean>
</beans>

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:p="http://www.springframework.org/schema/p" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="properties" value="foo=bar" />
</bean>
<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
<property name="customEditors">
<map>
<entry key="double[]" value="org.springframework.batch.support.IntArrayPropertyEditor"/>
</map>
</property>
</bean>
</beans>