BATCH-1473: Refactored context creation components
This commit is contained in:
@@ -64,7 +64,6 @@
|
||||
<config>src/test/resources/org/springframework/batch/core/scope/util/PlaceholderTargetSourceErrorTests-context.xml</config>
|
||||
<config>src/test/resources/org/springframework/batch/core/configuration/support/child-context.xml</config>
|
||||
<config>src/test/resources/org/springframework/batch/core/configuration/xml/ChunkElementParentAttributeParserTests-context.xml</config>
|
||||
<config>src/test/resources/org/springframework/batch/core/configuration/support/ClassPathXmlJobLoaderContextTests-context.xml</config>
|
||||
<config>src/test/resources/org/springframework/batch/core/configuration/xml/DuplicateTransitionJobParserTests-context.xml</config>
|
||||
<config>src/test/resources/org/springframework/batch/core/launch/support/error.xml</config>
|
||||
<config>src/test/resources/org/springframework/batch/core/step/item/FaultTolerantExceptionClassesTests-context.xml</config>
|
||||
@@ -103,6 +102,7 @@
|
||||
<config>src/test/resources/org/springframework/batch/core/configuration/xml/PartitionStepParserTests-context.xml</config>
|
||||
<config>src/test/resources/org/springframework/batch/core/configuration/xml/ChunkElementSkipPolicyParserTests-context.xml</config>
|
||||
<config>src/test/resources/org/springframework/batch/core/configuration/xml/JobStepParserTests-context.xml</config>
|
||||
<config>src/test/resources/org/springframework/batch/core/configuration/support/AutomaticJobRegistrarContextTests-context.xml</config>
|
||||
</configs>
|
||||
<configSets>
|
||||
<configSet>
|
||||
|
||||
@@ -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<ApplicationContextFactory> applicationContextFactories = new ArrayList<ApplicationContextFactory>();
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<Class<? extends BeanFactoryPostProcessor>> beanFactoryPostProcessorClasses;
|
||||
|
||||
private final Object lock = new Object();
|
||||
private Collection<Class<?>> beanPostProcessorExcludeClasses;
|
||||
|
||||
public ClassPathXmlApplicationContextFactory() {
|
||||
beanFactoryPostProcessorClasses = new ArrayList<Class<? extends BeanFactoryPostProcessor>>();
|
||||
beanFactoryPostProcessorClasses.add(PropertyPlaceholderConfigurer.class);
|
||||
beanFactoryPostProcessorClasses.add(CustomEditorConfigurer.class);
|
||||
beanPostProcessorExcludeClasses = new ArrayList<Class<?>>();
|
||||
/*
|
||||
* 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<Class<?>>();
|
||||
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<BeanPostProcessor> beanPostProcessors = beanFactory.getBeanPostProcessors();
|
||||
for (BeanPostProcessor beanPostProcessor : new ArrayList<BeanPostProcessor>(beanPostProcessors)) {
|
||||
for (Class<?> cls : beanPostProcessorExcludeClasses) {
|
||||
if (cls.isAssignableFrom(beanPostProcessor.getClass())) {
|
||||
logger.debug("Removing bean post processor: " + beanPostProcessor + " of type " + cls);
|
||||
beanPostProcessors.remove(beanPostProcessor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<Resource> jobPaths;
|
||||
|
||||
private ApplicationContext parent;
|
||||
|
||||
private JobRegistry jobRegistry;
|
||||
|
||||
private Collection<ConfigurableApplicationContext> contexts = new HashSet<ConfigurableApplicationContext>();
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* <code>classpath*:/config/*-job-context.xml</code>).
|
||||
*
|
||||
* @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();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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<Resource> resources = new ArrayList<Resource>();
|
||||
|
||||
private boolean copyConfiguration = true;
|
||||
|
||||
private Class<? extends BeanFactoryPostProcessor>[] 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.
|
||||
* <code>classpath*:/config/*-context.xml</code>).
|
||||
*
|
||||
* @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<? extends BeanFactoryPostProcessor>[] 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<ApplicationContextFactory> applicationContextFactories = new ArrayList<ApplicationContextFactory>();
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<ConfigurableApplicationContext> contexts = new HashSet<ConfigurableApplicationContext>();
|
||||
|
||||
/**
|
||||
* 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<Job> load(ApplicationContextFactory factory) throws DuplicateJobException {
|
||||
|
||||
Collection<String> 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<Job> result = new ArrayList<Job>();
|
||||
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 <code>overnightJob</code> and the group
|
||||
* <code>financeDepartment</code>, which would result in a {@link Job} with
|
||||
* identical functionality but named <code>financeDepartment$overnightJob</code>
|
||||
* identical functionality but named <code>financeDepartment.overnightJob</code>
|
||||
* . The use of a "." separator for elements is deliberate, since it is a "safe"
|
||||
* character in a <a href="http://www.w3.org/Addressing/URL">URL</a>.
|
||||
*
|
||||
|
||||
@@ -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<Job> load(ApplicationContextFactory factory) throws DuplicateJobException;
|
||||
|
||||
/**
|
||||
* Unregister all the jobs and close all the contexts created by this loader.
|
||||
*/
|
||||
void clear();
|
||||
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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<ConfigurableApplicationContext> contexts = new HashSet<ConfigurableApplicationContext>();
|
||||
private JobRegistry jobRegistry;
|
||||
|
||||
private static List<Exception> errors = Collections.synchronizedList(new ArrayList<Exception>());
|
||||
|
||||
@@ -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<String> 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() {
|
||||
|
||||
@@ -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;
|
||||
@@ -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<String> 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<ApplicationContextFactory> applicationContextFactories = new ArrayList<ApplicationContextFactory>();
|
||||
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]));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<? extends BeanFactoryPostProcessor>[] classes = (Class<? extends BeanFactoryPostProcessor>[]) 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]);
|
||||
|
||||
@@ -30,7 +30,6 @@ import org.springframework.util.ClassUtils;
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class OsgiBundleXmlApplicationContextFactoryTests {
|
||||
|
||||
private OsgiBundleXmlApplicationContextFactory factory = new OsgiBundleXmlApplicationContextFactory();
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans" 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.xsd">
|
||||
|
||||
<bean class="org.springframework.batch.core.configuration.support.AutomaticJobRegistrar">
|
||||
<property name="applicationContextFactories">
|
||||
<bean class="org.springframework.batch.core.configuration.support.ClasspathXmlApplicationContextsFactoryBean">
|
||||
<property name="resources" value="classpath*:org/springframework/batch/core/launch/support/job*.xml" />
|
||||
</bean>
|
||||
</property>
|
||||
<property name="jobLoader">
|
||||
<bean class="org.springframework.batch.core.configuration.support.DefaultJobLoader">
|
||||
<property name="jobRegistry" ref="jobRegistry" />
|
||||
</bean>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="jobRegistry" class="org.springframework.batch.core.configuration.support.MapJobRegistry" />
|
||||
|
||||
</beans>
|
||||
@@ -1,12 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans" 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.xsd">
|
||||
|
||||
<bean class="org.springframework.batch.core.configuration.support.ClassPathXmlJobLoader">
|
||||
<property name="jobPaths" value="classpath*:org/springframework/batch/core/launch/support/job*.xml" />
|
||||
<property name="jobRegistry" ref="jobRegistry"/>
|
||||
</bean>
|
||||
|
||||
<bean id="jobRegistry" class="org.springframework.batch.core.configuration.support.MapJobRegistry" />
|
||||
|
||||
</beans>
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop"
|
||||
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:p="http://www.springframework.org/schema/p"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="
|
||||
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
|
||||
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
|
||||
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
|
||||
|
||||
<import resource="test-environment.xml" />
|
||||
|
||||
<bean id="jobLoader" class="org.springframework.batch.core.configuration.support.DefaultJobLoader">
|
||||
<property name="jobRegistry" ref="jobRegistry"/>
|
||||
</bean>
|
||||
|
||||
<bean id="jobRegistry" class="org.springframework.batch.core.configuration.support.MapJobRegistry" />
|
||||
|
||||
</beans>
|
||||
@@ -1,46 +1,48 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:batch="http://www.springframework.org/schema/batch"
|
||||
xmlns:aop="http://www.springframework.org/schema/aop"
|
||||
xmlns:tx="http://www.springframework.org/schema/tx"
|
||||
xmlns:p="http://www.springframework.org/schema/p"
|
||||
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:batch="http://www.springframework.org/schema/batch"
|
||||
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/batch http://www.springframework.org/schema/batch/spring-batch-2.1.xsd
|
||||
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
|
||||
|
||||
<import resource="data-source-context.xml" />
|
||||
<import resource="classpath:/org/springframework/batch/sample/config/common-context.xml" />
|
||||
|
||||
|
||||
<bean id="jobLauncher" class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
|
||||
<property name="jobRepository" ref="jobRepository" />
|
||||
</bean>
|
||||
|
||||
<bean id="jobRepository"
|
||||
class="org.springframework.batch.core.repository.support.JobRepositoryFactoryBean"
|
||||
|
||||
<bean id="jobRepository" class="org.springframework.batch.core.repository.support.JobRepositoryFactoryBean"
|
||||
p:dataSource-ref="dataSource" p:transactionManager-ref="transactionManager" />
|
||||
|
||||
<bean id="mapJobRepository"
|
||||
class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean"
|
||||
|
||||
<bean id="mapJobRepository" class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean"
|
||||
lazy-init="true" autowire-candidate="false" />
|
||||
|
||||
<bean id="jobOperator" class="org.springframework.batch.core.launch.support.SimpleJobOperator"
|
||||
p:jobLauncher-ref="jobLauncher" p:jobExplorer-ref="jobExplorer" p:jobRepository-ref="jobRepository"
|
||||
p:jobRegistry-ref="jobRegistry" />
|
||||
|
||||
<bean id="jobExplorer" class="org.springframework.batch.core.explore.support.JobExplorerFactoryBean"
|
||||
|
||||
<bean id="jobOperator" class="org.springframework.batch.core.launch.support.SimpleJobOperator" p:jobLauncher-ref="jobLauncher"
|
||||
p:jobExplorer-ref="jobExplorer" p:jobRepository-ref="jobRepository" p:jobRegistry-ref="jobRegistry" />
|
||||
|
||||
<bean id="jobExplorer" class="org.springframework.batch.core.explore.support.JobExplorerFactoryBean"
|
||||
p:dataSource-ref="dataSource" />
|
||||
|
||||
<bean id="jobLoader" class="org.springframework.batch.core.configuration.support.ClassPathXmlJobLoader" >
|
||||
<property name="jobPaths" value="jobs/skipSampleJob.xml" />
|
||||
<property name="jobRegistry" ref="jobRegistry"/>
|
||||
|
||||
<bean class="org.springframework.batch.core.configuration.support.AutomaticJobRegistrar">
|
||||
<property name="applicationContextFactories">
|
||||
<bean class="org.springframework.batch.core.configuration.support.ClasspathXmlApplicationContextsFactoryBean">
|
||||
<property name="resources" value="classpath*:jobs/skipSampleJob.xml" />
|
||||
</bean>
|
||||
</property>
|
||||
<property name="jobLoader">
|
||||
<bean class="org.springframework.batch.core.configuration.support.DefaultJobLoader">
|
||||
<property name="jobRegistry" ref="jobRegistry" />
|
||||
</bean>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="jobRegistry" class="org.springframework.batch.core.configuration.support.MapJobRegistry" />
|
||||
|
||||
|
||||
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
|
||||
<property name="dataSource" ref="dataSource" />
|
||||
</bean>
|
||||
|
||||
|
||||
<bean id="logAdvice" class="org.springframework.batch.sample.common.LogAdvice" />
|
||||
|
||||
</beans>
|
||||
|
||||
Reference in New Issue
Block a user