RESOLVED - issue BATCH-1338: Allow segregation of jobs by group, type or origin

http://jira.springframework.org/browse/BATCH-1338

Deprecated and replaced ClassPathXmlJobRegistry
This commit is contained in:
dsyer
2009-09-09 12:39:32 +00:00
parent f3cec725f0
commit 0d7c7034ae
8 changed files with 273 additions and 222 deletions

View File

@@ -54,7 +54,6 @@
<config>src/test/resources/org/springframework/batch/core/configuration/xml/StopIncompleteJobParserTests-context.xml</config>
<config>src/test/resources/org/springframework/batch/core/configuration/xml/AutoRegisteringStepScopeForJobElementTests-context.xml</config>
<config>src/test/resources/org/springframework/batch/core/configuration/xml/AutoRegisteringStepScopeForStepElementTests-context.xml</config>
<config>src/test/resources/org/springframework/batch/core/configuration/support/ClassPathXmlJobRegistryContextTests-context.xml</config>
<config>src/test/resources/org/springframework/batch/core/configuration/xml/StopAndRestartJobParserTests-context.xml</config>
<config>src/test/resources/org/springframework/batch/core/configuration/xml/StopRestartOnCompletedStepJobParserTests-context.xml</config>
<config>src/test/resources/org/springframework/batch/core/configuration/xml/StopRestartOnFailedStepJobParserTests-context.xml</config>

View File

@@ -0,0 +1,215 @@
/*
* 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.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, but if there is a unique one
* available in the current application context, then that will be used by
* default.
*
* @author Lucas Ward
* @author Dave Syer
*
* @since 2.0
* @since 2.1 this class does not implement {@link JobRegistry}
*/
public class ClassPathXmlJobLoader implements ApplicationContextAware, InitializingBean, DisposableBean,
ApplicationListener {
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;
}
/**
* Initialize the {@link JobRegistry} if not already injected. Attempts to
* discover a registry from the application context, searching for a unique
* bean of type {@link JobRegistry}.
*
* @throws Exception
*/
public void afterPropertiesSet() throws Exception {
if (jobRegistry == null) {
String[] names = parent.getBeanNamesForType(JobRegistry.class);
Assert.state(names.length == 1, "Precisely one bean of type JobRegistry is required. Found = "
+ names.length);
jobRegistry = (JobRegistry) parent.getBean(names[0]);
}
}
/**
* Create all the application contexts required and set up job registry
* entries with all the instances of {@link Job} found therein.
*
* @see InitializingBean#afterPropertiesSet()
*/
public final void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ContextRefreshedEvent && event.getSource() == parent) {
try {
initialize();
}
catch (DuplicateJobException e) {
throw new IllegalStateException(e);
}
catch (NoSuchJobException e) {
throw new IllegalStateException(e);
}
}
}
/**
* 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 (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()
*/
public void destroy() throws Exception {
for (ConfigurableApplicationContext context : contexts) {
if (context.isActive()) {
context.close();
}
}
for (String jobName : jobRegistry.getJobNames()) {
jobRegistry.unregister(jobName);
}
contexts.clear();
}
}

View File

@@ -1,200 +1,12 @@
/*
* 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.ContextRefreshedEvent;
import org.springframework.core.io.Resource;
/**
* 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. Care must be taken to avoid duplicate names.
* Placeholder for deprecation warning.
*
* @author Lucas Ward
* @author Dave Syer
*
* @since 2.0
* @since 2.1 this class does not implement {@link JobRegistry}: it is a
* {@link ListableJobLocator}
*
* @deprecated in version 2.1, please us {@link ClassPathXmlJobLoader} instead
*/
public class ClassPathXmlJobRegistry implements ListableJobLocator, ApplicationContextAware, DisposableBean,
ApplicationListener {
private static Log logger = LogFactory.getLog(ClassPathXmlJobRegistry.class);
private List<Resource> jobPaths;
private ApplicationContext parent;
private JobRegistry jobRegistry = new MapJobRegistry();
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);
}
/*
* (non-Javadoc)
*
* @see
* org.springframework.context.ApplicationContextAware#setApplicationContext
* (org.springframework.context.ApplicationContext)
*/
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
parent = applicationContext;
}
/*
* (non-Javadoc)
*
* @see
* org.springframework.batch.core.configuration.JobLocator#getJob(java.lang
* .String)
*/
public Job getJob(String name) throws NoSuchJobException {
return jobRegistry.getJob(name);
}
public Collection<String> getJobNames() {
return jobRegistry.getJobNames();
}
/**
* Create all the application contexts required and set up job registry
* entries with all the instances of {@link Job} found therein.
*
* @see InitializingBean#afterPropertiesSet()
*/
public final void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ContextRefreshedEvent && event.getSource() == parent) {
try {
initialize();
}
catch (DuplicateJobException e) {
throw new IllegalStateException(e);
}
catch (NoSuchJobException e) {
throw new IllegalStateException(e);
}
}
}
/**
* 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 (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()
*/
public void destroy() throws Exception {
for (ConfigurableApplicationContext context : contexts) {
if (context.isActive()) {
context.close();
}
}
for (String jobName : jobRegistry.getJobNames()) {
jobRegistry.unregister(jobName);
}
contexts.clear();
}
public abstract class ClassPathXmlJobRegistry {
}

View File

@@ -8,6 +8,7 @@ import java.util.Collection;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.configuration.JobRegistry;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@@ -20,10 +21,10 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class ClassPathXmlJobRegistryContextTests {
public class ClassPathXmlJobLoaderContextTests {
@Autowired
private ClassPathXmlJobRegistry registry;
private JobRegistry registry;
@Test
public void testLocateJob() throws Exception{

View File

@@ -6,10 +6,12 @@ import static org.junit.Assert.fail;
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.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
@@ -20,9 +22,15 @@ import org.springframework.core.io.Resource;
* @author Lucas Ward
*
*/
public class ClassPathXmlJobRegistryTests {
public class ClassPathXmlJobLoaderTests {
private ClassPathXmlJobRegistry registry = new ClassPathXmlJobRegistry();
private ClassPathXmlJobLoader loader = new ClassPathXmlJobLoader();
private MapJobRegistry registry = new MapJobRegistry();
@Before
public void setUp() {
loader.setJobRegistry(registry);
}
@Test
public void testLocateJob() throws Exception {
@@ -31,11 +39,11 @@ public class ClassPathXmlJobRegistryTests {
new ClassPathResource("org/springframework/batch/core/launch/support/job.xml"),
new ClassPathResource("org/springframework/batch/core/launch/support/job2.xml") };
registry.setJobPaths(jobPaths);
loader.setJobPaths(jobPaths);
GenericApplicationContext applicationContext = new GenericApplicationContext();
applicationContext.refresh();
registry.setApplicationContext(applicationContext);
registry.initialize();
loader.setApplicationContext(applicationContext);
loader.initialize();
Collection<String> names = registry.getJobNames();
assertEquals(2, names.size());
@@ -53,11 +61,11 @@ public class ClassPathXmlJobRegistryTests {
Resource[] jobPaths = new Resource[] { new ClassPathResource(
"org/springframework/batch/core/launch/support/test-environment.xml") };
registry.setJobPaths(jobPaths);
loader.setJobPaths(jobPaths);
GenericApplicationContext applicationContext = new GenericApplicationContext();
applicationContext.refresh();
registry.setApplicationContext(applicationContext);
registry.initialize();
loader.setApplicationContext(applicationContext);
loader.initialize();
}
@Test
@@ -65,11 +73,23 @@ public class ClassPathXmlJobRegistryTests {
Resource[] jobPaths = new Resource[] { new ClassPathResource(
"org/springframework/batch/core/launch/support/2jobs.xml") };
registry.setJobPaths(jobPaths);
loader.setJobPaths(jobPaths);
GenericApplicationContext applicationContext = new GenericApplicationContext();
applicationContext.refresh();
registry.setApplicationContext(applicationContext);
registry.initialize();
loader.setApplicationContext(applicationContext);
loader.initialize();
assertEquals(2, registry.getJobNames().size());
}
@Test
public void testChildContextOverridesBeanPostProcessor() throws Exception {
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();
assertEquals(2, registry.getJobNames().size());
}
@@ -79,9 +99,9 @@ public class ClassPathXmlJobRegistryTests {
Resource[] jobPaths = new Resource[] {
new ClassPathResource("org/springframework/batch/core/launch/support/2jobs.xml"),
new ClassPathResource("org/springframework/batch/core/launch/support/error.xml") };
registry.setJobPaths(jobPaths);
loader.setJobPaths(jobPaths);
try {
registry.initialize();
loader.initialize();
fail("Expected BeanCreationException");
}
catch (BeanCreationException e) {
@@ -94,10 +114,10 @@ public class ClassPathXmlJobRegistryTests {
Resource[] jobPaths = new Resource[] { new ClassPathResource(
"org/springframework/batch/core/launch/support/2jobs.xml") };
registry.setJobPaths(jobPaths);
registry.initialize();
loader.setJobPaths(jobPaths);
loader.initialize();
assertEquals(2, registry.getJobNames().size());
registry.destroy();
loader.destroy();
assertEquals(0, registry.getJobNames().size());
}

View File

@@ -0,0 +1,12 @@
<?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>

View File

@@ -1,10 +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.ClassPathXmlJobRegistry">
<property name="jobPaths" value="classpath*:org/springframework/batch/core/launch/support/job*.xml"/>
</bean>
</beans>

View File

@@ -30,9 +30,11 @@
<bean id="jobExplorer" class="org.springframework.batch.core.explore.support.JobExplorerFactoryBean"
p:dataSource-ref="dataSource" />
<bean id="jobRegistry" class="org.springframework.batch.core.configuration.support.ClassPathXmlJobRegistry" >
<bean id="jobLoader" class="org.springframework.batch.core.configuration.support.ClassPathXmlJobLoader" >
<property name="jobPaths" value="jobs/skipSampleJob.xml" />
</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" />