diff --git a/spring-batch-core/.springBeans b/spring-batch-core/.springBeans index 0729082d2..2fe6e96d3 100644 --- a/spring-batch-core/.springBeans +++ b/spring-batch-core/.springBeans @@ -64,7 +64,6 @@ src/test/resources/org/springframework/batch/core/scope/util/PlaceholderTargetSourceErrorTests-context.xml src/test/resources/org/springframework/batch/core/configuration/support/child-context.xml src/test/resources/org/springframework/batch/core/configuration/xml/ChunkElementParentAttributeParserTests-context.xml - src/test/resources/org/springframework/batch/core/configuration/support/ClassPathXmlJobLoaderContextTests-context.xml src/test/resources/org/springframework/batch/core/configuration/xml/DuplicateTransitionJobParserTests-context.xml src/test/resources/org/springframework/batch/core/launch/support/error.xml src/test/resources/org/springframework/batch/core/step/item/FaultTolerantExceptionClassesTests-context.xml @@ -103,6 +102,7 @@ src/test/resources/org/springframework/batch/core/configuration/xml/PartitionStepParserTests-context.xml src/test/resources/org/springframework/batch/core/configuration/xml/ChunkElementSkipPolicyParserTests-context.xml src/test/resources/org/springframework/batch/core/configuration/xml/JobStepParserTests-context.xml + src/test/resources/org/springframework/batch/core/configuration/support/AutomaticJobRegistrarContextTests-context.xml diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/AutomaticJobRegistrar.java b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/AutomaticJobRegistrar.java new file mode 100644 index 000000000..5937af919 --- /dev/null +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/AutomaticJobRegistrar.java @@ -0,0 +1,167 @@ +/* + * Copyright 2006-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.batch.core.configuration.support; + +import java.util.ArrayList; +import java.util.Collection; + +import org.springframework.batch.core.Job; +import org.springframework.batch.core.configuration.DuplicateJobException; +import org.springframework.batch.core.configuration.JobRegistry; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.ApplicationListener; +import org.springframework.context.Lifecycle; +import org.springframework.context.event.ContextClosedEvent; +import org.springframework.context.event.ContextRefreshedEvent; +import org.springframework.util.Assert; + +/** + * Loads and unloads {@link Job Jobs} when the application context is created + * and destroyed. Each resource provided is loaded as an application context + * with the current context as its parent, and then all the jobs from the child + * context are registered under their bean names. A {@link JobRegistry} is + * required. + * + * @author Lucas Ward + * @author Dave Syer + * + * @since 2.1 + */ +public class AutomaticJobRegistrar implements Lifecycle, ApplicationListener, ApplicationContextAware, InitializingBean { + + private Collection applicationContextFactories = new ArrayList(); + + private JobLoader jobLoader; + + private ApplicationContext applicationContext; + + private volatile boolean running = false; + + private Object lifecycleMonitor = new Object(); + + /** + * The enclosing application context, which can be used to check if + * {@link ApplicationEvent events} come from the expected source. + * + * @param applicationContext the enclosing application context if there is + * one + * @see ApplicationContextAware#setApplicationContext(ApplicationContext) + */ + public void setApplicationContext(ApplicationContext applicationContext) { + this.applicationContext = applicationContext; + } + + /** + * Add some factories to the set that will be used to load contexts and jobs. + * + * @param applicationContextFactories the {@link ApplicationContextFactory} + * values to use + */ + public void setApplicationContextFactories(ApplicationContextFactory[] applicationContextFactories) { + for (ApplicationContextFactory applicationContextFactory : applicationContextFactories) { + this.applicationContextFactories.add(applicationContextFactory); + } + } + + /** + * The job loader that will be used to load and manage jobs. + * + * @param jobLoader the {@link JobLoader} to set + */ + public void setJobLoader(JobLoader jobLoader) { + this.jobLoader = jobLoader; + } + + /** + * @throws Exception + */ + public void afterPropertiesSet() { + + Assert.state(jobLoader != null, "A JobLoader must be provided"); + + } + + /** + * Creates all the application contexts required and set up job registry + * entries with all the instances of {@link Job} found therein. Also closes + * the contexts when the enclosing context is closed. + * + * @see InitializingBean#afterPropertiesSet() + */ + public final void onApplicationEvent(ApplicationEvent event) { + // TODO: With Spring 3 a SmartLifecycle is started automatically + if (event.getSource() == applicationContext) { + if (event instanceof ContextRefreshedEvent) { + start(); + } + else if (event instanceof ContextClosedEvent) { + stop(); + } + } + } + + /** + * Delegates to {@link JobLoader#clear()}. + * + * @see Lifecycle#stop() + */ + public void stop() { + synchronized (this.lifecycleMonitor) { + jobLoader.clear(); + running = false; + } + } + + /** + * Take all the contexts from the factories provided and pass them to teh + * {@link JobLoader}. + * + * @see Lifecycle#start() + */ + public void start() { + synchronized (this.lifecycleMonitor) { + if (running) { + return; + } + for (ApplicationContextFactory factory : applicationContextFactories) { + try { + jobLoader.load(factory); + } + catch (DuplicateJobException e) { + throw new IllegalStateException(e); + } + } + running = true; + } + } + + /** + * Check if this component has been started. + * + * @return true if started successfully and not stopped + * @see Lifecycle#isRunning() + */ + public boolean isRunning() { + synchronized (this.lifecycleMonitor) { + return running; + } + } + +} diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/ClassPathXmlApplicationContextFactory.java b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/ClassPathXmlApplicationContextFactory.java index 619c6cf0d..d00ae9990 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/ClassPathXmlApplicationContextFactory.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/ClassPathXmlApplicationContextFactory.java @@ -18,9 +18,14 @@ package org.springframework.batch.core.configuration.support; import java.util.ArrayList; import java.util.Collection; +import java.util.List; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.springframework.beans.BeansException; +import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.config.BeanFactoryPostProcessor; +import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.beans.factory.config.CustomEditorConfigurer; import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer; @@ -43,22 +48,31 @@ import org.springframework.util.Assert; */ public class ClassPathXmlApplicationContextFactory implements ApplicationContextFactory, ApplicationContextAware { + private static final Log logger = LogFactory.getLog(ClassPathXmlApplicationContextFactory.class); + + private Resource resource; + private ConfigurableApplicationContext parent; - - private Resource path; - - private ResourceXmlApplicationContext context; - + private boolean copyConfiguration = true; private Collection> beanFactoryPostProcessorClasses; - private final Object lock = new Object(); + private Collection> beanPostProcessorExcludeClasses; public ClassPathXmlApplicationContextFactory() { beanFactoryPostProcessorClasses = new ArrayList>(); beanFactoryPostProcessorClasses.add(PropertyPlaceholderConfigurer.class); beanFactoryPostProcessorClasses.add(CustomEditorConfigurer.class); + beanPostProcessorExcludeClasses = new ArrayList>(); + /* + * Assume that a BeanPostProcessor that is BeanFactoryAware must be + * specific to the parent and remove it from the child (e.g. an + * AutoProxyCreator will not work properly). Unfortunately there might + * still be a a BeanPostProcessor with a dependency that itself is + * BeanFactoryAware, but we can legislate for that here. + */ + beanPostProcessorExcludeClasses.add(BeanFactoryAware.class); } /** @@ -66,10 +80,10 @@ public class ClassPathXmlApplicationContextFactory implements ApplicationContext * {@link ApplicationContext}. Use imports to centralise the configuration * in one file. * - * @param path the resource path to the xml to load for the child context. + * @param resource the resource path to the xml to load for the child context. */ - public void setPath(Resource path) { - this.path = path; + public void setResource(Resource resource) { + this.resource = resource; } /** @@ -109,6 +123,23 @@ public class ClassPathXmlApplicationContextFactory implements ApplicationContext } } + /** + * Determines by exclusion which bean post processors should be copied from + * the parent context. Defaults to {@link BeanFactoryAware} (so any post + * processors that have a reference to the parent bean factory are not + * copied into the child). Note that these classes do not themselves have to + * be {@link BeanPostProcessor} implementations or sub-interfaces. + * + * @param beanPostProcessorExcludeClasses the classes to set + */ + public void setBeanPostProcessorExcludeClasses(Class[] beanPostProcessorExcludeClasses) { + this.beanPostProcessorExcludeClasses = new ArrayList>(); + for (int i = 0; i < beanPostProcessorExcludeClasses.length; i++) { + this.beanPostProcessorExcludeClasses.add(beanPostProcessorExcludeClasses[i]); + } + + } + /** * Protected access to the list of bean factory post processor classes that * should be copied over to the context from the parent. @@ -125,6 +156,9 @@ public class ClassPathXmlApplicationContextFactory implements ApplicationContext * @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext) */ public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + if (applicationContext==null) { + return; + } Assert.isInstanceOf(ConfigurableApplicationContext.class, applicationContext); parent = (ConfigurableApplicationContext) applicationContext; } @@ -136,19 +170,11 @@ public class ClassPathXmlApplicationContextFactory implements ApplicationContext */ public ConfigurableApplicationContext createApplicationContext() { - if (path == null) { + if (resource == null) { return parent; } - if (context == null) { - // Lazy initialization of cached context - synchronized (lock) { - if (context == null) { - context = new ResourceXmlApplicationContext(parent); - } - } - } - return context; + return new ResourceXmlApplicationContext(parent); } @@ -188,7 +214,7 @@ public class ClassPathXmlApplicationContextFactory implements ApplicationContext @Override protected Resource[] getConfigResources() { - return new Resource[] { path }; + return new Resource[] { resource }; } } @@ -218,7 +244,8 @@ public class ClassPathXmlApplicationContextFactory implements ApplicationContext /** * 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. + * copies all configuration from the parent according to the + * {@link #setCopyConfiguration(boolean) flag} set. * * @param parent the parent bean factory for the new context (will never be * null) @@ -231,7 +258,16 @@ public class ClassPathXmlApplicationContextFactory implements ApplicationContext protected void prepareBeanFactory(DefaultListableBeanFactory parent, DefaultListableBeanFactory beanFactory) { if (copyConfiguration && parent != null) { beanFactory.copyConfigurationFrom(parent); + @SuppressWarnings("unchecked") + List beanPostProcessors = beanFactory.getBeanPostProcessors(); + for (BeanPostProcessor beanPostProcessor : new ArrayList(beanPostProcessors)) { + for (Class cls : beanPostProcessorExcludeClasses) { + if (cls.isAssignableFrom(beanPostProcessor.getClass())) { + logger.debug("Removing bean post processor: " + beanPostProcessor + " of type " + cls); + beanPostProcessors.remove(beanPostProcessor); + } + } + } } } - } diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/ClassPathXmlJobLoader.java b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/ClassPathXmlJobLoader.java deleted file mode 100644 index f64a63a98..000000000 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/ClassPathXmlJobLoader.java +++ /dev/null @@ -1,208 +0,0 @@ -/* - * Copyright 2006-2007 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.batch.core.configuration.support; - -import java.util.Arrays; -import java.util.Collection; -import java.util.HashSet; -import java.util.List; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.springframework.batch.core.Job; -import org.springframework.batch.core.configuration.DuplicateJobException; -import org.springframework.batch.core.configuration.JobFactory; -import org.springframework.batch.core.configuration.JobLocator; -import org.springframework.batch.core.configuration.JobRegistry; -import org.springframework.batch.core.configuration.ListableJobLocator; -import org.springframework.batch.core.launch.NoSuchJobException; -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.DisposableBean; -import org.springframework.beans.factory.InitializingBean; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; -import org.springframework.context.ApplicationEvent; -import org.springframework.context.ApplicationListener; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.context.event.ContextClosedEvent; -import org.springframework.context.event.ContextRefreshedEvent; -import org.springframework.core.io.Resource; -import org.springframework.util.Assert; - -/** - * Implementation of the {@link ListableJobLocator} interface that assumes all - * Jobs will be loaded from class path xml resources. Each resource provided is - * loaded as an application context with the current context as its parent, and - * then all the jobs from the child context are registered under their bean - * names. A {@link JobRegistry} is required. - * - * @author Lucas Ward - * @author Dave Syer - * - * @since 2.0 - * @since 2.1 this class does not implement {@link JobRegistry} - */ -public class ClassPathXmlJobLoader implements ApplicationContextAware, ApplicationListener, InitializingBean { - - private static Log logger = LogFactory.getLog(ClassPathXmlJobLoader.class); - - private List jobPaths; - - private ApplicationContext parent; - - private JobRegistry jobRegistry; - - private Collection contexts = new HashSet(); - - /** - * A set of resources to load. Each resource should be a Spring - * configuration file which is loaded into an application context whose - * parent is the current context. In a configuration file the resources can - * be given as a pattern (e.g. - * classpath*:/config/*-job-context.xml). - * - * @param jobPaths - */ - public void setJobPaths(Resource[] jobPaths) { - this.jobPaths = Arrays.asList(jobPaths); - } - - /** - * The {@link JobRegistry} to use for jobs created. If not provided an - * instance will be discovered from the application context. - * - * @param jobRegistry - */ - public void setJobRegistry(JobRegistry jobRegistry) { - this.jobRegistry = jobRegistry; - } - - /* - * (non-Javadoc) - * - * @see - * org.springframework.context.ApplicationContextAware#setApplicationContext - * (org.springframework.context.ApplicationContext) - */ - public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { - parent = applicationContext; - } - - /** - * @throws Exception - */ - public void afterPropertiesSet() throws Exception { - Assert.state(jobRegistry != null, "A JobRegistry must be provided"); - } - - /** - * Creates all the application contexts required and set up job registry - * entries with all the instances of {@link Job} found therein. Also closes - * the contexts when their parent is closed. - * - * @see InitializingBean#afterPropertiesSet() - */ - public final void onApplicationEvent(ApplicationEvent event) { - if (event.getSource() == parent) { - if (event instanceof ContextRefreshedEvent) { - try { - initialize(); - } - catch (DuplicateJobException e) { - throw new IllegalStateException(e); - } - catch (NoSuchJobException e) { - throw new IllegalStateException(e); - } - } - else if (event instanceof ContextClosedEvent) { - clear(); - } - } - } - - /** - * Create jobs as instructed and register them so they can be accessed via - * the {@link JobLocator} interface. Normally called from - * {@link #onApplicationEvent(ApplicationEvent)} when the parent context is - * refreshed. - * - * @throws DuplicateJobException if the job registry detects a duplicate job - * @throws NoSuchJobException if no jobs are registered, since this is - * usually an error - */ - protected void initialize() throws DuplicateJobException, NoSuchJobException { - - for (Resource resource : jobPaths) { - - ConfigurableApplicationContext context = createApplicationContext(parent, resource); - contexts.add(context); - String[] names = context.getBeanNamesForType(Job.class); - - for (String name : names) { - logger.debug("Registering job: " + name + " from context: " + resource); - JobFactory jobFactory = new ReferenceJobFactory((Job) context.getBean(name)); - jobRegistry.register(jobFactory); - } - - } - - if (!jobPaths.isEmpty() && jobRegistry.getJobNames().isEmpty()) { - throw new NoSuchJobException("Could not locate any jobs in resources provided."); - } - - } - - /** - * Create an application context from the resource provided. Extension point - * for subclasses if they need to customize the context in any way. The - * default uses a {@link ClassPathXmlApplicationContextFactory}. - * - * @param parent the parent application context (or null if there is none) - * @param resource the location of the XML configuration - * - * @return an application context containing jobs - */ - protected ConfigurableApplicationContext createApplicationContext(ApplicationContext parent, Resource resource) { - ClassPathXmlApplicationContextFactory applicationContextFactory = new ClassPathXmlApplicationContextFactory(); - applicationContextFactory.setPath(resource); - if (parent != null) { - applicationContextFactory.setApplicationContext(parent); - } - return applicationContextFactory.createApplicationContext(); - } - - /** - * Close the contexts that were created in {@link #afterPropertiesSet()}. - * - * @see DisposableBean#destroy() - */ - protected void clear() { - - for (ConfigurableApplicationContext context : contexts) { - if (context.isActive()) { - context.close(); - } - } - for (String jobName : jobRegistry.getJobNames()) { - jobRegistry.unregister(jobName); - } - contexts.clear(); - - } - -} diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/ClassPathXmlJobRegistry.java b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/ClassPathXmlJobRegistry.java index d215727ff..728c7a8f4 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/ClassPathXmlJobRegistry.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/ClassPathXmlJobRegistry.java @@ -5,7 +5,7 @@ package org.springframework.batch.core.configuration.support; * * @author Dave Syer * - * @deprecated in version 2.1, please us {@link ClassPathXmlJobLoader} instead + * @deprecated in version 2.1, please us {@link AutomaticJobRegistrar} instead */ public abstract class ClassPathXmlJobRegistry { diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/ClasspathXmlApplicationContextsFactoryBean.java b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/ClasspathXmlApplicationContextsFactoryBean.java new file mode 100644 index 000000000..e44e82034 --- /dev/null +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/ClasspathXmlApplicationContextsFactoryBean.java @@ -0,0 +1,166 @@ +/* + * Copyright 2006-2010 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.batch.core.configuration.support; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.BeanFactoryAware; +import org.springframework.beans.factory.FactoryBean; +import org.springframework.beans.factory.config.BeanFactoryPostProcessor; +import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.beans.factory.config.CustomEditorConfigurer; +import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.core.io.Resource; + +/** + * A convenient factory for creating a set of {@link ApplicationContextFactory} + * components from a set of {@link Resource resources}. + * + * @author Dave Syer + * + */ +public class ClasspathXmlApplicationContextsFactoryBean implements FactoryBean, ApplicationContextAware { + + private List resources = new ArrayList(); + + private boolean copyConfiguration = true; + + private Class[] beanFactoryPostProcessorClasses; + + private Class[] beanPostProcessorExcludeClasses; + + private ApplicationContext applicationContext; + + /** + * A set of resources to load using a + * {@link ClassPathXmlApplicationContextFactory}. Each resource should be a + * Spring configuration file which is loaded into an application context + * whose parent is the current context. In a configuration file the + * resources can be given as a pattern (e.g. + * classpath*:/config/*-context.xml). + * + * @param resources + */ + public void setResources(Resource[] resources) { + this.resources = Arrays.asList(resources); + } + + /** + * 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; + } + + /** + * Determines which bean factory post processors (like property + * placeholders) should be copied from the parent context. Defaults to + * {@link PropertyPlaceholderConfigurer} and {@link CustomEditorConfigurer}. + * + * @param copyBeanFactoryPostProcessors the flag value to set + */ + + public void setBeanFactoryPostProcessorClasses( + Class[] beanFactoryPostProcessorClasses) { + this.beanFactoryPostProcessorClasses = beanFactoryPostProcessorClasses; + } + + /** + * Determines by exclusion which bean post processors should be copied from + * the parent context. Defaults to {@link BeanFactoryAware} (so any post + * processors that have a reference to the parent bean factory are not + * copied into the child). Note that these classes do not themselves have to + * be {@link BeanPostProcessor} implementations or sub-interfaces. + * + * @param beanPostProcessorExcludeClasses the classes to set + */ + public void setBeanPostProcessorExcludeClasses(Class[] beanPostProcessorExcludeClasses) { + this.beanPostProcessorExcludeClasses = beanPostProcessorExcludeClasses; + } + + /** + * Create an {@link ApplicationContextFactory} from each resource provided + * in {@link #setResources(Resource[])}. + * + * @return an array of {@link ApplicationContextFactory} + * @throws Exception + * @see org.springframework.beans.factory.FactoryBean#getObject() + */ + public Object getObject() throws Exception { + + if (resources == null) { + return new ApplicationContextFactory[0]; + } + + List applicationContextFactories = new ArrayList(); + for (Resource resource : resources) { + ClassPathXmlApplicationContextFactory factory = new ClassPathXmlApplicationContextFactory(); + factory.setCopyConfiguration(copyConfiguration); + if (beanFactoryPostProcessorClasses != null) { + factory.setBeanFactoryPostProcessorClasses(beanFactoryPostProcessorClasses); + } + if (beanPostProcessorExcludeClasses != null) { + factory.setBeanPostProcessorExcludeClasses(beanPostProcessorExcludeClasses); + } + factory.setResource(resource); + factory.setApplicationContext(applicationContext); + applicationContextFactories.add(factory); + } + return applicationContextFactories.toArray(new ApplicationContextFactory[applicationContextFactories.size()]); + } + + /** + * The type of obkect returned by this factory - an array of + * {@link ApplicationContextFactory}. + * + * @return array of {@link ApplicationContextFactory} + * @see FactoryBean#getObjectType() + */ + public Class getObjectType() { + return ApplicationContextFactory[].class; + } + + /** + * Oprimization hint for bean factory. + * @return true + * @see FactoryBean#isSingleton() + */ + public boolean isSingleton() { + return true; + } + + /** + * An application context that can be used as a parent context for all the + * factories. + * + * @param applicationContext the {@link ApplicationContext} to set + * @throws BeansException + * @see ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext) + */ + public void setApplicationContext(ApplicationContext applicationContext) { + this.applicationContext = applicationContext; + } + +} diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/DefaultJobLoader.java b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/DefaultJobLoader.java new file mode 100644 index 000000000..f935301e9 --- /dev/null +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/DefaultJobLoader.java @@ -0,0 +1,115 @@ +/* + * Copyright 2006-2010 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.batch.core.configuration.support; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.batch.core.Job; +import org.springframework.batch.core.configuration.DuplicateJobException; +import org.springframework.batch.core.configuration.JobFactory; +import org.springframework.batch.core.configuration.JobRegistry; +import org.springframework.batch.core.launch.NoSuchJobException; +import org.springframework.context.ConfigurableApplicationContext; + +/** + * @author Dave Syer + * + */ +public class DefaultJobLoader implements JobLoader { + + private static Log logger = LogFactory.getLog(DefaultJobLoader.class); + + private JobRegistry jobRegistry; + + private Collection contexts = new HashSet(); + + /** + * Default constructor useful for decalarative configuration. + */ + public DefaultJobLoader() { + this(null); + } + + /** + * Create a job loader with the job registry provided. + * @param jobRegistry a {@link JobRegistry} + */ + public DefaultJobLoader(JobRegistry jobRegistry) { + this.jobRegistry = jobRegistry; + } + + /** + * The {@link JobRegistry} to use for jobs created. + * + * @param jobRegistry + */ + public void setJobRegistry(JobRegistry jobRegistry) { + this.jobRegistry = jobRegistry; + } + + /** + * Unregister all the jobs and close all the contexts created by this + * loader. + * + * @see JobLoader#clear() + */ + public void clear() { + for (ConfigurableApplicationContext context : contexts) { + if (context.isActive()) { + context.close(); + } + } + for (String jobName : jobRegistry.getJobNames()) { + jobRegistry.unregister(jobName); + } + contexts.clear(); + } + + public Collection load(ApplicationContextFactory factory) throws DuplicateJobException { + + Collection jobNamesBefore = jobRegistry.getJobNames(); + ConfigurableApplicationContext context = factory.createApplicationContext(); + // Try to detect auto-registration (e.g. through a bean post processor) + boolean autoRegistrationDetected = jobRegistry.getJobNames().size() > jobNamesBefore.size(); + + contexts.add(context); + String[] names = context.getBeanNamesForType(Job.class); + Collection result = new ArrayList(); + + for (String name : names) { + if (!autoRegistrationDetected) { + logger.debug("Registering job: " + name + " from context: " + context.getDisplayName()); + JobFactory jobFactory = new ReferenceJobFactory((Job) context.getBean(name)); + jobRegistry.register(jobFactory); + } + try { + result.add(jobRegistry.getJob(name)); + } + catch (NoSuchJobException e) { + // should not happen; + throw new IllegalStateException("Could not retrieve job that was should have been registered", e); + } + } + + return result; + + } + +} diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/GroupAwareJob.java b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/GroupAwareJob.java index e087b06e2..3b3e6ba23 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/GroupAwareJob.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/GroupAwareJob.java @@ -24,7 +24,7 @@ import org.springframework.batch.core.JobParametersIncrementer; * to make it fit a naming convention for type or origin. E.g. the source job * might be overnightJob and the group * financeDepartment, which would result in a {@link Job} with - * identical functionality but named financeDepartment$overnightJob + * identical functionality but named financeDepartment.overnightJob * . The use of a "." separator for elements is deliberate, since it is a "safe" * character in a URL. * diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/JobLoader.java b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/JobLoader.java new file mode 100644 index 000000000..b856ff0b4 --- /dev/null +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/JobLoader.java @@ -0,0 +1,44 @@ +/* + * Copyright 2009-2010 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.batch.core.configuration.support; + +import java.util.Collection; + +import org.springframework.batch.core.Job; +import org.springframework.batch.core.configuration.DuplicateJobException; + + +/** + * @author Dave Syer + * + * @since 2.1 + */ +public interface JobLoader { + + /** + * @param factory a factory for an application context (containing jobs) + * @return a collection of the jobs created + * + * @throws DuplicateJobException if a job with the same name was already registered + */ + Collection load(ApplicationContextFactory factory) throws DuplicateJobException; + + /** + * Unregister all the jobs and close all the contexts created by this loader. + */ + void clear(); + +} diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/OsgiBundleXmlApplicationContextFactory.java b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/OsgiBundleXmlApplicationContextFactory.java index c4e7aad9d..3d5734a0f 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/OsgiBundleXmlApplicationContextFactory.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/OsgiBundleXmlApplicationContextFactory.java @@ -25,10 +25,10 @@ import org.springframework.osgi.context.BundleContextAware; import org.springframework.osgi.context.support.OsgiBundleXmlApplicationContext; /** - * @author Dave Syer + * {@link ApplicationContextFactory} that can be used to load a context from an + * XML location in a bundle. * - * @deprecated with no plans to replace (stateful creation of application - * context should no longer be required as it was in Spring Batch 1.1.x) + * @author Dave Syer * */ public class OsgiBundleXmlApplicationContextFactory implements BundleContextAware, ApplicationContextFactory, diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/launch/support/JobRegistryBackgroundJobRunner.java b/spring-batch-core/src/main/java/org/springframework/batch/core/launch/support/JobRegistryBackgroundJobRunner.java index b0ca7a8e6..2f4e1bac7 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/launch/support/JobRegistryBackgroundJobRunner.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/launch/support/JobRegistryBackgroundJobRunner.java @@ -18,9 +18,7 @@ package org.springframework.batch.core.launch.support; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collection; import java.util.Collections; -import java.util.HashSet; import java.util.List; import org.apache.commons.logging.Log; @@ -30,13 +28,12 @@ import org.springframework.batch.core.configuration.DuplicateJobException; import org.springframework.batch.core.configuration.JobFactory; import org.springframework.batch.core.configuration.JobRegistry; import org.springframework.batch.core.configuration.support.ClassPathXmlApplicationContextFactory; -import org.springframework.batch.core.configuration.support.JobRegistryBeanPostProcessor; -import org.springframework.batch.core.configuration.support.ReferenceJobFactory; +import org.springframework.batch.core.configuration.support.DefaultJobLoader; +import org.springframework.batch.core.configuration.support.JobLoader; import org.springframework.batch.core.launch.JobLauncher; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.config.AutowireCapableBeanFactory; import org.springframework.context.ApplicationContext; -import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.core.io.Resource; import org.springframework.util.Assert; @@ -74,7 +71,7 @@ public class JobRegistryBackgroundJobRunner { private static Log logger = LogFactory.getLog(JobRegistryBackgroundJobRunner.class); - private JobRegistry registry; + private JobLoader jobLoader; private ApplicationContext parentContext = null; @@ -82,7 +79,7 @@ public class JobRegistryBackgroundJobRunner { final private String parentContextPath; - private Collection contexts = new HashSet(); + private JobRegistry jobRegistry; private static List errors = Collections.synchronizedList(new ArrayList()); @@ -95,11 +92,21 @@ public class JobRegistryBackgroundJobRunner { } /** - * Public setter for the {@link JobRegistry}. - * @param registry the registry to set + * A loader for the jobs that are going to be registered. + * + * @param jobLoader the {@link JobLoader} to set */ - public void setRegistry(JobRegistry registry) { - this.registry = registry; + public void setJobLoader(JobLoader jobLoader) { + this.jobLoader = jobLoader; + } + + /** + * A job registry that can be used to create a job loader (if none is provided). + * + * @param jobRegistry the {@link JobRegistry} to set + */ + public void setJobRegistry(JobRegistry jobRegistry) { + this.jobRegistry = jobRegistry; } /** @@ -115,9 +122,10 @@ public class JobRegistryBackgroundJobRunner { private void register(String[] paths) throws DuplicateJobException, IOException { + maybeCreateJobLoader(); + for (int i = 0; i < paths.length; i++) { - boolean postProcessorExists = parentContext.getBeanNamesForType(JobRegistryBeanPostProcessor.class).length > 0; Resource[] resources = parentContext.getResources(paths[i]); for (int j = 0; j < resources.length; j++) { @@ -125,21 +133,10 @@ public class JobRegistryBackgroundJobRunner { Resource path = resources[j]; logger.info("Registering Job definitions from " + Arrays.toString(resources)); - ConfigurableApplicationContext context = createApplicationContext(parentContext, path); - contexts.add(context); - String[] names = context.getBeanNamesForType(Job.class); - - Collection registered = registry.getJobNames(); - // If there is a JobRegistryBeanPostProcessor in there already - // then they can be registered automatically - for (String name : names) { - if (!registered.contains(name) && postProcessorExists) { - logger.debug("Registering job: " + name + " from context: " + path); - JobFactory jobFactory = new ReferenceJobFactory((Job) context.getBean(name)); - registry.register(jobFactory); - } - } - + ClassPathXmlApplicationContextFactory factory = new ClassPathXmlApplicationContextFactory(); + factory.setApplicationContext(parentContext); + factory.setResource(path); + jobLoader.load(factory); } } @@ -147,22 +144,30 @@ public class JobRegistryBackgroundJobRunner { } /** - * Create an application context from the resource provided. Extension point - * for subclasses if they need to customize the context in any way. The - * default uses a {@link ClassPathXmlApplicationContextFactory}. - * - * @param parent the parent application context (or null if there is none) - * @param resource the location of the XML configuration - * - * @return an application context containing jobs + * If there is no {@link JobLoader} then try and create one from existing + * bean definitions. */ - protected ConfigurableApplicationContext createApplicationContext(ApplicationContext parent, Resource resource) { - ClassPathXmlApplicationContextFactory applicationContextFactory = new ClassPathXmlApplicationContextFactory(); - applicationContextFactory.setPath(resource); - if (parent != null) { - applicationContextFactory.setApplicationContext(parent); + private void maybeCreateJobLoader() { + + if (jobLoader != null) { + return; } - return applicationContextFactory.createApplicationContext(); + + String[] names = parentContext.getBeanNamesForType(JobLoader.class); + if (names.length == 0) { + if (parentContext.containsBean("jobLoader")) { + jobLoader = (JobLoader) parentContext.getBean("jobLoader", JobLoader.class); + return; + } + if (jobRegistry != null) { + jobLoader = new DefaultJobLoader(jobRegistry); + return; + } + } + + jobLoader = (JobLoader) parentContext.getBean(names[0], JobLoader.class); + return; + } /** @@ -249,15 +254,7 @@ public class JobRegistryBackgroundJobRunner { * @see org.springframework.beans.factory.DisposableBean#destroy() */ private void destroy() throws Exception { - for (ConfigurableApplicationContext context : contexts) { - if (context.isActive()) { - context.close(); - } - } - for (String jobName : registry.getJobNames()) { - registry.unregister(jobName); - } - contexts.clear(); + jobLoader.clear(); } private void run() { diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/support/ClassPathXmlJobLoaderContextTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/support/AutomaticJobRegistrarContextTests.java similarity index 95% rename from spring-batch-core/src/test/java/org/springframework/batch/core/configuration/support/ClassPathXmlJobLoaderContextTests.java rename to spring-batch-core/src/test/java/org/springframework/batch/core/configuration/support/AutomaticJobRegistrarContextTests.java index 3b5c52b4b..0c9b3ea2c 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/support/ClassPathXmlJobLoaderContextTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/support/AutomaticJobRegistrarContextTests.java @@ -21,7 +21,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration -public class ClassPathXmlJobLoaderContextTests { +public class AutomaticJobRegistrarContextTests { @Autowired private JobRegistry registry; diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/support/ClassPathXmlJobLoaderTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/support/AutomaticJobRegistrarTests.java similarity index 58% rename from spring-batch-core/src/test/java/org/springframework/batch/core/configuration/support/ClassPathXmlJobLoaderTests.java rename to spring-batch-core/src/test/java/org/springframework/batch/core/configuration/support/AutomaticJobRegistrarTests.java index 0f646a4a6..26fbd9a38 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/support/ClassPathXmlJobLoaderTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/support/AutomaticJobRegistrarTests.java @@ -1,16 +1,18 @@ package org.springframework.batch.core.configuration.support; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import java.util.ArrayList; import java.util.Collection; import org.junit.Before; import org.junit.Test; import org.springframework.batch.core.Job; -import org.springframework.batch.core.launch.NoSuchJobException; import org.springframework.beans.factory.BeanCreationException; +import org.springframework.context.ApplicationContext; import org.springframework.context.event.ContextClosedEvent; import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.context.support.ClassPathXmlApplicationContext; @@ -20,18 +22,21 @@ import org.springframework.core.io.Resource; /** * - * + * @author Dave Syer * @author Lucas Ward * */ -public class ClassPathXmlJobLoaderTests { +public class AutomaticJobRegistrarTests { + + private AutomaticJobRegistrar registrar = new AutomaticJobRegistrar(); - private ClassPathXmlJobLoader loader = new ClassPathXmlJobLoader(); private MapJobRegistry registry = new MapJobRegistry(); - + @Before public void setUp() { - loader.setJobRegistry(registry); + DefaultJobLoader jobLoader = new DefaultJobLoader(); + jobLoader.setJobRegistry(registry); + registrar.setJobLoader(jobLoader); } @Test @@ -41,11 +46,11 @@ public class ClassPathXmlJobLoaderTests { new ClassPathResource("org/springframework/batch/core/launch/support/job.xml"), new ClassPathResource("org/springframework/batch/core/launch/support/job2.xml") }; - loader.setJobPaths(jobPaths); GenericApplicationContext applicationContext = new GenericApplicationContext(); applicationContext.refresh(); - loader.setApplicationContext(applicationContext); - loader.initialize(); + setUpApplicationContextFactories(jobPaths, applicationContext); + registrar.setApplicationContext(applicationContext); + registrar.start(); Collection names = registry.getJobNames(); assertEquals(2, names.size()); @@ -58,16 +63,16 @@ public class ClassPathXmlJobLoaderTests { assertEquals("test-job2", job.getName()); } - @Test(expected = NoSuchJobException.class) + @Test public void testNoJobFound() throws Exception { Resource[] jobPaths = new Resource[] { new ClassPathResource( "org/springframework/batch/core/launch/support/test-environment.xml") }; - loader.setJobPaths(jobPaths); GenericApplicationContext applicationContext = new GenericApplicationContext(); applicationContext.refresh(); - loader.setApplicationContext(applicationContext); - loader.initialize(); + setUpApplicationContextFactories(jobPaths, applicationContext); + registrar.setApplicationContext(applicationContext); + registrar.start(); } @Test @@ -75,11 +80,11 @@ public class ClassPathXmlJobLoaderTests { Resource[] jobPaths = new Resource[] { new ClassPathResource( "org/springframework/batch/core/launch/support/2jobs.xml") }; - loader.setJobPaths(jobPaths); GenericApplicationContext applicationContext = new GenericApplicationContext(); applicationContext.refresh(); - loader.setApplicationContext(applicationContext); - loader.initialize(); + setUpApplicationContextFactories(jobPaths, applicationContext); + registrar.setApplicationContext(applicationContext); + registrar.start(); assertEquals(2, registry.getJobNames().size()); } @@ -88,10 +93,11 @@ public class ClassPathXmlJobLoaderTests { Resource[] jobPaths = new Resource[] { new ClassPathResource( "org/springframework/batch/core/launch/support/2jobs.xml") }; - loader.setApplicationContext(new ClassPathXmlApplicationContext( - "/org/springframework/batch/core/launch/support/test-environment-with-registry-and-auto-register.xml")); - loader.setJobPaths(jobPaths); - loader.initialize(); + ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext( + "/org/springframework/batch/core/launch/support/test-environment-with-registry-and-auto-register.xml"); + registrar.setApplicationContext(applicationContext); + setUpApplicationContextFactories(jobPaths, applicationContext); + registrar.start(); assertEquals(2, registry.getJobNames().size()); } @@ -101,9 +107,9 @@ public class ClassPathXmlJobLoaderTests { Resource[] jobPaths = new Resource[] { new ClassPathResource("org/springframework/batch/core/launch/support/2jobs.xml"), new ClassPathResource("org/springframework/batch/core/launch/support/error.xml") }; - loader.setJobPaths(jobPaths); + setUpApplicationContextFactories(jobPaths, null); try { - loader.initialize(); + registrar.start(); fail("Expected BeanCreationException"); } catch (BeanCreationException e) { @@ -112,47 +118,74 @@ public class ClassPathXmlJobLoaderTests { } @Test - public void testDestroy() throws Exception { + public void testClear() throws Exception { Resource[] jobPaths = new Resource[] { new ClassPathResource( "org/springframework/batch/core/launch/support/2jobs.xml") }; - loader.setJobPaths(jobPaths); - loader.initialize(); + setUpApplicationContextFactories(jobPaths, null); + registrar.start(); assertEquals(2, registry.getJobNames().size()); - loader.clear(); + registrar.stop(); assertEquals(0, registry.getJobNames().size()); } + @Test + public void testStartStopRunning() throws Exception { + + Resource[] jobPaths = new Resource[] { new ClassPathResource( + "org/springframework/batch/core/launch/support/2jobs.xml") }; + setUpApplicationContextFactories(jobPaths, null); + registrar.start(); + assertTrue(registrar.isRunning()); + registrar.start(); + assertEquals(2, registry.getJobNames().size()); + registrar.stop(); + assertFalse(registrar.isRunning()); + + } + @Test public void testInitCalledOnContextRefreshed() throws Exception { Resource[] jobPaths = new Resource[] { new ClassPathResource( "org/springframework/batch/core/launch/support/2jobs.xml") }; - loader.setApplicationContext(new ClassPathXmlApplicationContext( + registrar.setApplicationContext(new ClassPathXmlApplicationContext( "/org/springframework/batch/core/launch/support/test-environment-with-registry-and-auto-register.xml")); - loader.setJobPaths(jobPaths); GenericApplicationContext applicationContext = new GenericApplicationContext(); applicationContext.refresh(); - loader.setApplicationContext(applicationContext); - loader.onApplicationEvent(new ContextRefreshedEvent(applicationContext)); + setUpApplicationContextFactories(jobPaths, applicationContext); + registrar.setApplicationContext(applicationContext); + registrar.onApplicationEvent(new ContextRefreshedEvent(applicationContext)); assertEquals(2, registry.getJobNames().size()); } @Test - public void testDestroyCalledOnContextClosed() throws Exception { + public void testClearCalledOnContextClosed() throws Exception { Resource[] jobPaths = new Resource[] { new ClassPathResource( "org/springframework/batch/core/launch/support/2jobs.xml") }; - loader.setJobPaths(jobPaths); GenericApplicationContext applicationContext = new GenericApplicationContext(); applicationContext.refresh(); - loader.setApplicationContext(applicationContext); - loader.initialize(); + setUpApplicationContextFactories(jobPaths, applicationContext); + registrar.setApplicationContext(applicationContext); + registrar.start(); assertEquals(2, registry.getJobNames().size()); - loader.onApplicationEvent(new ContextClosedEvent(applicationContext)); + registrar.onApplicationEvent(new ContextClosedEvent(applicationContext)); assertEquals(0, registry.getJobNames().size()); } + private void setUpApplicationContextFactories(Resource[] jobPaths, ApplicationContext parent) { + Collection applicationContextFactories = new ArrayList(); + for (Resource resource : jobPaths) { + ClassPathXmlApplicationContextFactory factory = new ClassPathXmlApplicationContextFactory(); + factory.setResource(resource); + factory.setApplicationContext(parent); + applicationContextFactories.add(factory); + } + registrar.setApplicationContextFactories(applicationContextFactories + .toArray(new ApplicationContextFactory[jobPaths.length])); + } + } diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/support/ClassPathXmlApplicationContextFactoryTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/support/ClassPathXmlApplicationContextFactoryTests.java index 60af27a6b..25ccdaac1 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/support/ClassPathXmlApplicationContextFactoryTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/support/ClassPathXmlApplicationContextFactoryTests.java @@ -36,20 +36,20 @@ public class ClassPathXmlApplicationContextFactoryTests { @Test public void testCreateJob() { - factory.setPath(new ClassPathResource(ClassUtils.addResourcePathToPackagePath(getClass(), "trivial-context.xml"))); + factory.setResource(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"))); + factory.setResource(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"))); + factory.setResource(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()); @@ -59,7 +59,7 @@ public class ClassPathXmlApplicationContextFactoryTests { @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.setResource(new ClassPathResource(ClassUtils.addResourcePathToPackagePath(getClass(), "child-context.xml"))); @SuppressWarnings("unchecked") Class[] classes = (Class[]) new Class[0]; factory.setBeanFactoryPostProcessorClasses(classes); @@ -72,7 +72,7 @@ public class ClassPathXmlApplicationContextFactoryTests { @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.setResource(new ClassPathResource(ClassUtils.addResourcePathToPackagePath(getClass(), "child-context.xml"))); factory.setCopyConfiguration(false); ConfigurableApplicationContext context = factory.createApplicationContext(); assertEquals("test-job", context.getBeanNamesForType(Job.class)[0]); diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/support/OsgiBundleXmlApplicationContextFactoryTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/support/OsgiBundleXmlApplicationContextFactoryTests.java index c5ba04c75..1c94c2673 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/support/OsgiBundleXmlApplicationContextFactoryTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/support/OsgiBundleXmlApplicationContextFactoryTests.java @@ -30,7 +30,6 @@ import org.springframework.util.ClassUtils; * @author Dave Syer * */ -@SuppressWarnings("deprecation") public class OsgiBundleXmlApplicationContextFactoryTests { private OsgiBundleXmlApplicationContextFactory factory = new OsgiBundleXmlApplicationContextFactory(); diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/launch/support/JobRegistryBackgroundJobRunnerTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/launch/support/JobRegistryBackgroundJobRunnerTests.java index f2d817698..43db9ca37 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/launch/support/JobRegistryBackgroundJobRunnerTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/launch/support/JobRegistryBackgroundJobRunnerTests.java @@ -48,6 +48,14 @@ public class JobRegistryBackgroundJobRunnerTests { assertEquals(0, JobRegistryBackgroundJobRunner.getErrors().size()); } + @Test + public void testMainWithJobLoader() throws Exception { + JobRegistryBackgroundJobRunner.main( + ClassUtils.addResourcePathToPackagePath(getClass(), "test-environment-with-loader.xml"), ClassUtils + .addResourcePathToPackagePath(getClass(), "job.xml")); + assertEquals(0, JobRegistryBackgroundJobRunner.getErrors().size()); + } + @Before public void setUp() throws Exception { JobRegistryBackgroundJobRunner.getErrors().clear(); diff --git a/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/support/AutomaticJobRegistrarContextTests-context.xml b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/support/AutomaticJobRegistrarContextTests-context.xml new file mode 100644 index 000000000..e124e6e83 --- /dev/null +++ b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/support/AutomaticJobRegistrarContextTests-context.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + diff --git a/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/support/ClassPathXmlJobLoaderContextTests-context.xml b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/support/ClassPathXmlJobLoaderContextTests-context.xml deleted file mode 100644 index f9b49e6d2..000000000 --- a/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/support/ClassPathXmlJobLoaderContextTests-context.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - diff --git a/spring-batch-core/src/test/resources/org/springframework/batch/core/launch/support/test-environment-with-loader.xml b/spring-batch-core/src/test/resources/org/springframework/batch/core/launch/support/test-environment-with-loader.xml new file mode 100644 index 000000000..4255234ad --- /dev/null +++ b/spring-batch-core/src/test/resources/org/springframework/batch/core/launch/support/test-environment-with-loader.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + diff --git a/spring-batch-samples/src/main/resources/skipSample-job-launcher-context.xml b/spring-batch-samples/src/main/resources/skipSample-job-launcher-context.xml index 33b0eb244..d30b8d632 100644 --- a/spring-batch-samples/src/main/resources/skipSample-job-launcher-context.xml +++ b/spring-batch-samples/src/main/resources/skipSample-job-launcher-context.xml @@ -1,46 +1,48 @@ - - + - - - - - - - - + + - - - - + + + + + + + + + + + + - + - +