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

Added groupName and extension points in JobRegistryBeanPostProcessor and ClassPathXmlJobRegistry
This commit is contained in:
dsyer
2009-09-08 07:49:37 +00:00
parent ac08658c48
commit f9b186ada3
8 changed files with 197 additions and 42 deletions

View File

@@ -32,14 +32,28 @@ public class ApplicationContextJobFactory extends AbstractGroupAwareJobFactory {
final ApplicationContextFactory applicationContextFactory;
/**
* @param groupName the name of the group that the job belongs to
* @param jobName the id of the {@link Job} in the application context to be
* created
* @param applicationContextFactory a factory for an application context
* containing a job with the job name provided
*/
public ApplicationContextJobFactory(ApplicationContextFactory applicationContextFactory, String jobName) {
super(jobName);
public ApplicationContextJobFactory(String groupName, String jobName,
ApplicationContextFactory applicationContextFactory) {
super(groupName, jobName);
this.applicationContextFactory = applicationContextFactory;
}
/**
* @param jobName the id of the {@link Job} in the application context to be
* created
* @param applicationContextFactory a factory for an application context
* containing a job with the job name provided
*/
public ApplicationContextJobFactory(String jobName, ApplicationContextFactory applicationContextFactory) {
this(null, jobName, applicationContextFactory);
}
/**
* Create an {@link ApplicationContext} from the factory provided and pull
* out a bean with the name given during initialization.

View File

@@ -44,7 +44,10 @@ import org.springframework.core.io.Resource;
*
* @author Lucas Ward
* @author Dave Syer
*
* @since 2.0
* @since 2.1 this class does not implement {@link JobRegistry}: it is a
* {@link ListableJobLocator}
*/
public class ClassPathXmlJobRegistry implements ListableJobLocator, ApplicationContextAware, InitializingBean,
DisposableBean {
@@ -114,8 +117,9 @@ public class ClassPathXmlJobRegistry implements ListableJobLocator, ApplicationC
for (String name : names) {
logger.debug("Registering job: " + name + " from context: " + resource);
ApplicationContextJobFactory jobFactory = new ApplicationContextJobFactory(applicationContextFactory,
name);
String groupName = getGroupName(context, resource, name);
ApplicationContextJobFactory jobFactory = new ApplicationContextJobFactory(groupName, name,
applicationContextFactory);
jobRegistry.register(jobFactory);
}
}
@@ -126,6 +130,20 @@ public class ClassPathXmlJobRegistry implements ListableJobLocator, ApplicationC
}
/**
* Determine a group name for the job to be registered. Default
* implementation does nothing, but provides an extension point for
* specialised subclasses.
*
* @param context the application context containing the job
* @param resource the resource that was used to create the context
* @param jobName the jobName
* @return a group name for the job (or null if not needed)
*/
protected String getGroupName(ApplicationContext context, Resource resource, String jobName) {
return null;
}
/**
* Close the contexts that were created in {@link #afterPropertiesSet()}.
*

View File

@@ -24,28 +24,50 @@ import org.springframework.batch.core.configuration.JobLocator;
import org.springframework.batch.core.configuration.JobRegistry;
import org.springframework.beans.BeansException;
import org.springframework.beans.FatalBeanException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.util.Assert;
/**
* A {@link BeanPostProcessor} that registers {@link Job} beans
* with a {@link JobRegistry}. Include a bean of this type along
* with your job configuration, and use the same
* {@link JobRegistry} as a {@link JobLocator} when
* you need to locate a {@link JobLocator} to launch.
* A {@link BeanPostProcessor} that registers {@link Job} beans with a
* {@link JobRegistry}. Include a bean of this type along with your job
* configuration, and use the same {@link JobRegistry} as a {@link JobLocator}
* when you need to locate a {@link Job} to launch.
*
* @author Dave Syer
*
*/
public class JobRegistryBeanPostProcessor implements BeanPostProcessor, InitializingBean, DisposableBean {
public class JobRegistryBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware, InitializingBean,
DisposableBean {
// It doesn't make sense for this to have a default value...
private JobRegistry jobRegistry = null;
private Collection<String> jobNames = new HashSet<String>();
private String groupName = null;
private DefaultListableBeanFactory beanFactory;
/**
* The group name for jobs registered by this component. Optional (defaults
* to null, which means that jobs are registered with their bean names).
* Useful where there is a hierarchy of application contexts all
* contributing to the same {@link JobRegistry}: child contexts can then
* define an instance with a unique group name to avoid clashes between job
* names.
*
* @param groupName the groupName to set
*/
public void setGroupName(String groupName) {
this.groupName = groupName;
}
/**
* Injection setter for {@link JobRegistry}.
*
@@ -55,6 +77,19 @@ public class JobRegistryBeanPostProcessor implements BeanPostProcessor, Initiali
this.jobRegistry = jobRegistry;
}
/*
* (non-Javadoc)
*
* @see
* org.springframework.beans.factory.BeanFactoryAware#setBeanFactory(org
* .springframework.beans.factory.BeanFactory)
*/
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
if (beanFactory instanceof DefaultListableBeanFactory) {
this.beanFactory = (DefaultListableBeanFactory) beanFactory;
}
}
/**
* Make sure the registry is set before use.
*
@@ -65,8 +100,8 @@ public class JobRegistryBeanPostProcessor implements BeanPostProcessor, Initiali
}
/**
* De-register all the {@link Job} instances that were
* regsistered by this post processor.
* De-register all the {@link Job} instances that were regsistered by this
* post processor.
* @see org.springframework.beans.factory.DisposableBean#destroy()
*/
public void destroy() throws Exception {
@@ -78,8 +113,7 @@ public class JobRegistryBeanPostProcessor implements BeanPostProcessor, Initiali
/**
* If the bean is an instance of {@link Job} then register it.
* @throws FatalBeanException if there is a
* {@link DuplicateJobException}.
* @throws FatalBeanException if there is a {@link DuplicateJobException}.
*
* @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessAfterInitialization(java.lang.Object,
* java.lang.String)
@@ -88,8 +122,13 @@ public class JobRegistryBeanPostProcessor implements BeanPostProcessor, Initiali
if (bean instanceof Job) {
Job job = (Job) bean;
try {
jobRegistry.register(new ReferenceJobFactory(job));
jobNames.add(job.getName());
String groupName = this.groupName;
if (beanFactory != null) {
groupName = getGroupName(beanFactory.getBeanDefinition(beanName), job);
}
ReferenceJobFactory jobFactory = new ReferenceJobFactory(groupName, job);
jobRegistry.register(jobFactory);
jobNames.add(jobFactory.getJobName());
}
catch (DuplicateJobException e) {
throw new FatalBeanException("Cannot register job configuration", e);
@@ -98,6 +137,19 @@ public class JobRegistryBeanPostProcessor implements BeanPostProcessor, Initiali
return bean;
}
/**
* Determine a group name for the job to be registered. Default
* implementation just returns the {@link #setGroupName(String) groupName}
* configured. Provides an extension point for specialised subclasses.
*
* @param beanDefinition the bean definition for the job
* @param job the job
* @return a group name for the job (or null if not needed)
*/
protected String getGroupName(BeanDefinition beanDefinition, Job job) {
return groupName;
}
/**
* Do nothing.
*
@@ -107,5 +159,4 @@ public class JobRegistryBeanPostProcessor implements BeanPostProcessor, Initiali
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}

View File

@@ -29,12 +29,20 @@ public class ReferenceJobFactory extends AbstractGroupAwareJobFactory {
private Job job;
/**
* @param groupName the name of the group that this job belongs to
* @param job the {@link Job} to return from {@link #createJob()}.
*/
public ReferenceJobFactory(String groupName, Job job) {
super(groupName, job.getName());
this.job = job;
}
/**
* @param job the {@link Job} to return from {@link #createJob()}.
*/
public ReferenceJobFactory(Job job) {
super(job.getName());
this.job = job;
this(null, job);
}
/**

View File

@@ -129,7 +129,7 @@ public class JobRegistryBackgroundJobRunner {
factory.setApplicationContext(parentContext);
factory.setPath(path);
logger.info("Registering Job definition: " + names[k]);
registry.register(new ApplicationContextJobFactory(factory, names[k]));
registry.register(new ApplicationContextJobFactory(names[k], factory));
}
}

View File

@@ -1,5 +1,6 @@
package org.springframework.batch.core.configuration.support;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import org.junit.Test;
@@ -9,20 +10,25 @@ import org.springframework.context.support.StaticApplicationContext;
public class ApplicationContextJobFactoryTests {
private AbstractGroupAwareJobFactory factory = new ApplicationContextJobFactory(
new StubApplicationContextFactory(), "job");
@Test
public void testFactoryContext() throws Exception {
ApplicationContextJobFactory factory = new ApplicationContextJobFactory("job",
new StubApplicationContextFactory());
assertNotNull(factory.createJob());
}
private static class StubApplicationContextFactory implements
ApplicationContextFactory {
@Test
public void testGroupName() throws Exception {
ApplicationContextJobFactory factory = new ApplicationContextJobFactory("jobs", "job",
new StubApplicationContextFactory());
assertEquals("jobs$job", factory.getJobName());
}
private static class StubApplicationContextFactory implements ApplicationContextFactory {
public ConfigurableApplicationContext createApplicationContext() {
StaticApplicationContext context = new StaticApplicationContext();
context.registerSingleton("job", JobSupport.class);
return context ;
return context;
}
}

View File

@@ -15,10 +15,14 @@
*/
package org.springframework.batch.core.configuration.support;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.util.Collection;
import junit.framework.TestCase;
import org.junit.Test;
import org.springframework.batch.core.configuration.DuplicateJobException;
import org.springframework.batch.core.job.JobSupport;
import org.springframework.batch.core.launch.NoSuchJobException;
@@ -29,11 +33,12 @@ import org.springframework.context.support.ClassPathXmlApplicationContext;
* @author Dave Syer
*
*/
public class JobRegistryBeanPostProcessorTests extends TestCase {
public class JobRegistryBeanPostProcessorTests {
private JobRegistryBeanPostProcessor processor = new JobRegistryBeanPostProcessor();
public void testInitialization() throws Exception {
@Test
public void testInitializationFails() throws Exception {
try {
processor.afterPropertiesSet();
fail("Expected IllegalArgumentException");
@@ -44,33 +49,48 @@ public class JobRegistryBeanPostProcessorTests extends TestCase {
}
}
@Test
public void testBeforeInitialization() throws Exception {
// should be a no-op
assertEquals("foo", processor.postProcessAfterInitialization("foo", "bar"));
assertEquals("foo", processor.postProcessBeforeInitialization("foo", "bar"));
}
@Test
public void testAfterInitializationWithWrongType() throws Exception {
// should be a no-op
assertEquals("foo", processor.postProcessAfterInitialization("foo", "bar"));
}
@Test
public void testAfterInitializationWithCorrectType() throws Exception {
MapJobRegistry registry = new MapJobRegistry();
processor.setJobRegistry(registry);
JobSupport configuration = new JobSupport();
configuration.setBeanName("foo");
assertEquals(configuration, processor.postProcessAfterInitialization(configuration, "bar"));
assertEquals(configuration.getName(), registry.getJob("foo").getName());
JobSupport job = new JobSupport();
job.setBeanName("foo");
assertEquals(job, processor.postProcessAfterInitialization(job, "bar"));
assertEquals(job.getName(), registry.getJob("foo").getName());
}
@Test
public void testAfterInitializationWithGroupName() throws Exception {
MapJobRegistry registry = new MapJobRegistry();
processor.setJobRegistry(registry);
processor.setGroupName("jobs");
JobSupport job = new JobSupport();
job.setBeanName("foo");
assertEquals(job, processor.postProcessAfterInitialization(job, "bar"));
assertEquals("[jobs$foo]", registry.getJobNames().toString());
}
@Test
public void testAfterInitializationWithDuplicate() throws Exception {
MapJobRegistry registry = new MapJobRegistry();
processor.setJobRegistry(registry);
JobSupport configuration = new JobSupport();
configuration.setBeanName("foo");
processor.postProcessAfterInitialization(configuration, "bar");
JobSupport job = new JobSupport();
job.setBeanName("foo");
processor.postProcessAfterInitialization(job, "bar");
try {
processor.postProcessAfterInitialization(configuration, "spam");
processor.postProcessAfterInitialization(job, "spam");
fail("Expected FatalBeanException");
}
catch (FatalBeanException e) {
@@ -79,12 +99,13 @@ public class JobRegistryBeanPostProcessorTests extends TestCase {
}
}
@Test
public void testUnregisterOnDestroy() throws Exception {
MapJobRegistry registry = new MapJobRegistry();
processor.setJobRegistry(registry);
JobSupport configuration = new JobSupport();
configuration.setBeanName("foo");
assertEquals(configuration, processor.postProcessAfterInitialization(configuration, "bar"));
JobSupport job = new JobSupport();
job.setBeanName("foo");
assertEquals(job, processor.postProcessAfterInitialization(job, "bar"));
processor.destroy();
try {
assertEquals(null, registry.getJob("foo"));
@@ -95,6 +116,7 @@ public class JobRegistryBeanPostProcessorTests extends TestCase {
}
}
@Test
public void testExecutionWithApplicationContext() throws Exception {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("test-context.xml", getClass());
MapJobRegistry registry = (MapJobRegistry) context.getBean("registry");
@@ -117,4 +139,5 @@ public class JobRegistryBeanPostProcessorTests extends TestCase {
assertEquals(context.getBean("test-job-with-concrete-parent-and-name"), registry.getJob("oof"));
assertEquals(context.getBean("test-job-with-concrete-parent-and-bean-name"), registry.getJob("rab"));
}
}

View File

@@ -0,0 +1,35 @@
/*
* 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 static org.junit.Assert.*;
import org.junit.Test;
import org.springframework.batch.core.job.JobSupport;
/**
* @author Dave Syer
*
*/
public class ReferenceJobFactoryTests {
@Test
public void testGroupName() throws Exception {
ReferenceJobFactory factory = new ReferenceJobFactory("jobs", new JobSupport("foo"));
assertEquals("jobs$foo", factory.getJobName());
}
}