diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/AbstractApplicationContextFactory.java b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/AbstractApplicationContextFactory.java index 5c977b743..b74c8cf9f 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/AbstractApplicationContextFactory.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/AbstractApplicationContextFactory.java @@ -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>(); 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 diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/GenericApplicationContextFactory.java b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/GenericApplicationContextFactory.java index 34e92ce86..013038767 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/GenericApplicationContextFactory.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/GenericApplicationContextFactory.java @@ -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> types = new ArrayList>(); + 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 uris = new ArrayList(); + 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 names = new ArrayList(); + 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); } } }; diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/support/GenericApplicationContextFactoryTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/support/GenericApplicationContextFactoryTests.java index 5f6db5f65..9abf524b4 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/support/GenericApplicationContextFactoryTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/support/GenericApplicationContextFactoryTests.java @@ -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; diff --git a/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/support/abstract-context.xml b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/support/abstract-context.xml new file mode 100644 index 000000000..2f79e8208 --- /dev/null +++ b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/support/abstract-context.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/support/child-context-with-abstract-job.xml b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/support/child-context-with-abstract-job.xml new file mode 100644 index 000000000..292c87aef --- /dev/null +++ b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/support/child-context-with-abstract-job.xml @@ -0,0 +1,12 @@ + + + + + + +