BATCH-2227: Allow multiple configs (xmls, java-files, packages) to be
loaded in ApplicationContextFactory
This commit is contained in:
committed by
Chris Schaefer
parent
6e4b0cb045
commit
90aca9c98b
@@ -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
|
||||
|
||||
@@ -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 @Configuration class, or a package name.
|
||||
*
|
||||
* @param resource a resource (XML configuration file, @Configuration class or java package to scan)
|
||||
* @param resources a resource (XML configuration file, @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);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
Reference in New Issue
Block a user