BATCH-2227: Allow multiple configs (xmls, java-files, packages) to be

loaded in ApplicationContextFactory
This commit is contained in:
Thomas Bosch
2014-04-24 15:13:15 +02:00
committed by Chris Schaefer
parent 6e4b0cb045
commit 90aca9c98b
5 changed files with 163 additions and 47 deletions

View File

@@ -17,6 +17,7 @@
package org.springframework.batch.core.configuration.support;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
@@ -49,7 +50,7 @@ public abstract class AbstractApplicationContextFactory implements ApplicationCo
private static final Log logger = LogFactory.getLog(AbstractApplicationContextFactory.class);
private Object resource;
private Object[] resources;
private ConfigurableApplicationContext parent;
@@ -63,9 +64,9 @@ public abstract class AbstractApplicationContextFactory implements ApplicationCo
* Create a factory instance with the resource specified. The resource is a Spring configuration file or java
* package containing configuration files.
*/
public AbstractApplicationContextFactory(Object resource) {
public AbstractApplicationContextFactory(Object[] resources) {
this.resource = resource;
this.resources = resources;
beanFactoryPostProcessorClasses = new ArrayList<Class<? extends BeanFactoryPostProcessor>>();
beanFactoryPostProcessorClasses.add(PropertyPlaceholderConfigurer.class);
beanFactoryPostProcessorClasses.add(PropertySourcesPlaceholderConfigurer.class);
@@ -162,16 +163,16 @@ public abstract class AbstractApplicationContextFactory implements ApplicationCo
@Override
public ConfigurableApplicationContext createApplicationContext() {
if (resource == null) {
if (resources == null || resources.length == 0) {
return parent;
}
return createApplicationContext(parent, resource);
return createApplicationContext(parent, resources);
}
protected abstract ConfigurableApplicationContext createApplicationContext(ConfigurableApplicationContext parent,
Object resource);
Object... resources);
/**
* Extension point for special subclasses that want to do more complex things with the context prior to refresh. The
@@ -215,7 +216,7 @@ public abstract class AbstractApplicationContextFactory implements ApplicationCo
@Override
public String toString() {
return "ApplicationContextFactory [resource=" + resource + "]";
return "ApplicationContextFactory [resources=" + Arrays.toString(resources) + "]";
}
@Override

View File

@@ -17,6 +17,9 @@
package org.springframework.batch.core.configuration.support;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
@@ -28,6 +31,7 @@ import org.springframework.context.support.GenericApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;
import org.springframework.core.io.Resource;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* {@link ApplicationContextFactory} implementation that takes a parent context and a path to the context to create.
@@ -42,29 +46,42 @@ public class GenericApplicationContextFactory extends AbstractApplicationContext
* Create an application context factory for the resource specified. The resource can be an actual {@link Resource},
* in which case it will be interpreted as an XML file, or it can be a &#64;Configuration class, or a package name.
*
* @param resource a resource (XML configuration file, &#064;Configuration class or java package to scan)
* @param resources a resource (XML configuration file, &#064;Configuration class or java package to scan)
*/
public GenericApplicationContextFactory(Object resource) {
super(resource);
public GenericApplicationContextFactory(Object... resources) {
super(resources);
}
/**
* @see AbstractApplicationContextFactory#createApplicationContext(ConfigurableApplicationContext, Object)
* @see AbstractApplicationContextFactory#createApplicationContext(ConfigurableApplicationContext, Object...)
*/
@Override
protected ConfigurableApplicationContext createApplicationContext(ConfigurableApplicationContext parent,
Object resource) {
if (resource instanceof Resource) {
return new ResourceXmlApplicationContext(parent, (Resource) resource);
Object... resources) {
if (allObjectsOfType(resources, Resource.class)) {
return new ResourceXmlApplicationContext(parent, resources);
}
if (resource instanceof Class<?>) {
return new ResourceAnnotationApplicationContext(parent, resource);
if (allObjectsOfType(resources, Class.class)) {
return new ResourceAnnotationApplicationContext(parent, resources);
}
if (resource instanceof String) {
return new ResourceAnnotationApplicationContext(parent, resource);
if (allObjectsOfType(resources, String.class)) {
return new ResourceAnnotationApplicationContext(parent, resources);
}
throw new IllegalArgumentException("No application context could be created for resource type: "
+ resource.getClass());
List<Class<?>> types = new ArrayList<Class<?>>();
for (Object resource : resources) {
types.add(resource.getClass());
}
throw new IllegalArgumentException("No application context could be created for resource types: "
+ Arrays.toString(types.toArray()));
}
private boolean allObjectsOfType(Object[] objects, Class<?> type) {
for (Object object : objects) {
if (!type.isInstance(object)) {
return false;
}
}
return true;
}
private abstract class ApplicationContextHelper {
@@ -74,7 +91,7 @@ public class GenericApplicationContextFactory extends AbstractApplicationContext
private final ConfigurableApplicationContext parent;
public ApplicationContextHelper(ConfigurableApplicationContext parent, GenericApplicationContext context,
Object config) {
Object... configs) {
this.parent = parent;
if (parent != null) {
Assert.isTrue(parent.getBeanFactory() instanceof DefaultListableBeanFactory,
@@ -85,14 +102,14 @@ public class GenericApplicationContextFactory extends AbstractApplicationContext
parentBeanFactory = null;
}
context.setParent(parent);
context.setId(generateId(config));
loadConfiguration(config);
context.setId(generateId(configs));
loadConfiguration(configs);
prepareContext(parent, context);
}
protected abstract String generateId(Object config);
protected abstract String generateId(Object... configs);
protected abstract void loadConfiguration(Object config);
protected abstract void loadConfiguration(Object... configs);
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
if (parentBeanFactory != null) {
@@ -114,22 +131,26 @@ public class GenericApplicationContextFactory extends AbstractApplicationContext
/**
* @param parent
*/
public ResourceXmlApplicationContext(ConfigurableApplicationContext parent, Resource resource) {
helper = new ApplicationContextHelper(parent, this, resource) {
public ResourceXmlApplicationContext(ConfigurableApplicationContext parent, Object... resources) {
helper = new ApplicationContextHelper(parent, this, resources) {
@Override
protected String generateId(Object config) {
Resource resource = (Resource) config;
protected String generateId(Object... configs) {
Resource[] resources = Arrays.copyOfRange(configs, 0, configs.length, Resource[].class);
try {
return resource.getURI().toString();
List<String> uris = new ArrayList<String>();
for (Resource resource : resources) {
uris.add(resource.getURI().toString());
}
return StringUtils.collectionToCommaDelimitedString(uris);
}
catch (IOException e) {
return resource.toString();
return Arrays.toString(resources);
}
}
@Override
protected void loadConfiguration(Object config) {
Resource resource = (Resource) config;
load(resource);
protected void loadConfiguration(Object... configs) {
Resource[] resources = Arrays.copyOfRange(configs, 0, configs.length, Resource[].class);
load(resources);
}
};
refresh();
@@ -152,27 +173,31 @@ public class GenericApplicationContextFactory extends AbstractApplicationContext
private final ApplicationContextHelper helper;
public ResourceAnnotationApplicationContext(ConfigurableApplicationContext parent, Object resource) {
helper = new ApplicationContextHelper(parent, this, resource) {
public ResourceAnnotationApplicationContext(ConfigurableApplicationContext parent, Object... resources) {
helper = new ApplicationContextHelper(parent, this, resources) {
@Override
protected String generateId(Object config) {
if (config instanceof Class) {
Class<?> type = (Class<?>) config;
return type.getName();
protected String generateId(Object... configs) {
if (allObjectsOfType(configs, Class.class)) {
Class<?>[] types = Arrays.copyOfRange(configs, 0, configs.length, Class[].class);
List<String> names = new ArrayList<String>();
for (Class<?> type : types) {
names.add(type.getName());
}
return StringUtils.collectionToCommaDelimitedString(names);
}
else {
return config.toString();
return Arrays.toString(configs);
}
}
@Override
protected void loadConfiguration(Object config) {
if (config instanceof Class) {
Class<?> type = (Class<?>) config;
register(type);
protected void loadConfiguration(Object... configs) {
if (allObjectsOfType(configs, Class.class)) {
Class<?>[] types = Arrays.copyOfRange(configs, 0, configs.length, Class[].class);
register(types);
}
else {
String pkg = (String) config;
scan(pkg);
String[] pkgs = Arrays.copyOfRange(configs, 0, configs.length, String[].class);
scan(pkgs);
}
}
};

View File

@@ -21,7 +21,11 @@ import static org.junit.Assert.assertTrue;
import org.junit.Test;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.job.JobSupport;
import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.support.AbstractBeanFactory;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.ClassPathResource;
@@ -133,6 +137,40 @@ public class GenericApplicationContextFactoryTests {
assertEquals(other.hashCode(), factory.hashCode());
}
@Test
public void testEqualsMultileConfigs() throws Exception {
Resource resource1 = new ClassPathResource(ClassUtils.addResourcePathToPackagePath(getClass(),
"abstract-context.xml"));
Resource resource2 = new ClassPathResource(ClassUtils.addResourcePathToPackagePath(getClass(),
"child-context-with-abstract-job.xml"));
GenericApplicationContextFactory factory = new GenericApplicationContextFactory(resource1, resource2);
GenericApplicationContextFactory other = new GenericApplicationContextFactory(resource1, resource2);
assertEquals(other, factory);
assertEquals(other.hashCode(), factory.hashCode());
}
@Test
public void testParentConfigurationInheritedMultipleConfigs() {
Resource resource1 = new ClassPathResource(ClassUtils.addResourcePathToPackagePath(getClass(),
"abstract-context.xml"));
Resource resource2 = new ClassPathResource(ClassUtils.addResourcePathToPackagePath(getClass(),
"child-context-with-abstract-job.xml"));
GenericApplicationContextFactory factory = new GenericApplicationContextFactory(resource1, resource2);
ConfigurableApplicationContext context = factory.createApplicationContext();
assertEquals("concrete-job", context.getBeanNamesForType(Job.class)[0]);
assertEquals("bar", context.getBean("concrete-job", Job.class).getName());
assertEquals(4, context.getBean("foo", Foo.class).values[1], 0.01);
assertNotNull(context.getBean("concrete-job", JobSupport.class).getStep("step31"));
assertNotNull(context.getBean("concrete-job", JobSupport.class).getStep("step32"));
boolean autowiredFound = false;
for (BeanPostProcessor postProcessor : ((AbstractBeanFactory)context.getBeanFactory()).getBeanPostProcessors()) {
if (postProcessor instanceof AutowiredAnnotationBeanPostProcessor) {
autowiredFound = true;
}
}
assertTrue(autowiredFound);
}
public static class Foo {
private double[] values;

View File

@@ -0,0 +1,40 @@
<?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" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<context:annotation-config />
<bean id="abstract-job" abstract="true" class="org.springframework.batch.core.job.JobSupport">
<property name="name" value="${foo}" />
<property name="steps">
<list>
<bean id="step31" class="org.springframework.batch.core.step.StepSupport"/>
<bean id="step32" class="org.springframework.batch.core.step.StepSupport"/>
</list>
</property>
</bean>
<bean id="foo" class="org.springframework.batch.core.configuration.support.GenericApplicationContextFactoryTests$Foo">
<property name="values" value="1,4" />
</bean>
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="properties" value="foo=bar" />
<property name="order" value="1"/>
</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>

View File

@@ -0,0 +1,12 @@
<?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-3.1.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd">
<bean id="concrete-job" parent="abstract-job">
</bean>
</beans>