From def829f87a0bfc701d90cb1358cb15d32aa7f6ea Mon Sep 17 00:00:00 2001 From: dsyer Date: Wed, 9 Sep 2009 11:42:12 +0000 Subject: [PATCH] REOPENED - issue BATCH-1338: Allow segregation of jobs by type or origin Fixed broken build re-jig JobRegistryBackgroundJobRunner --- .../support/ClassPathXmlJobRegistry.java | 34 ++++++--- .../support/JobRegistryBeanPostProcessor.java | 2 +- .../JobRegistryBackgroundJobRunner.java | 72 +++++++++++++++---- .../sample/launch/RemoteLauncherTests.java | 12 ++-- 4 files changed, 93 insertions(+), 27 deletions(-) diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/ClassPathXmlJobRegistry.java b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/ClassPathXmlJobRegistry.java index 0bb101fec..70648b83f 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/ClassPathXmlJobRegistry.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/ClassPathXmlJobRegistry.java @@ -25,6 +25,7 @@ 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; @@ -112,7 +113,7 @@ public class ClassPathXmlJobRegistry implements ListableJobLocator, ApplicationC * * @see InitializingBean#afterPropertiesSet() */ - public void onApplicationEvent(ApplicationEvent event) { + public final void onApplicationEvent(ApplicationEvent event) { if (event instanceof ContextRefreshedEvent && event.getSource() == parent) { try { initialize(); @@ -139,21 +140,17 @@ public class ClassPathXmlJobRegistry implements ListableJobLocator, ApplicationC protected void initialize() throws DuplicateJobException, NoSuchJobException { for (Resource resource : jobPaths) { - ClassPathXmlApplicationContextFactory applicationContextFactory = new ClassPathXmlApplicationContextFactory(); - applicationContextFactory.setPath(resource); - if (parent != null) { - applicationContextFactory.setApplicationContext(parent); - } - ConfigurableApplicationContext context = applicationContextFactory.createApplicationContext(); + + 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); - ApplicationContextJobFactory jobFactory = new ApplicationContextJobFactory(name, - applicationContextFactory); + JobFactory jobFactory = new ReferenceJobFactory((Job) context.getBean(name)); jobRegistry.register(jobFactory); } + } if (jobRegistry.getJobNames().isEmpty()) { @@ -162,6 +159,25 @@ public class ClassPathXmlJobRegistry implements ListableJobLocator, ApplicationC } + /** + * 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()}. * diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/JobRegistryBeanPostProcessor.java b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/JobRegistryBeanPostProcessor.java index 633e71e73..eb676c913 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/JobRegistryBeanPostProcessor.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/JobRegistryBeanPostProcessor.java @@ -123,7 +123,7 @@ public class JobRegistryBeanPostProcessor implements BeanPostProcessor, BeanFact Job job = (Job) bean; try { String groupName = this.groupName; - if (beanFactory != null) { + if (beanFactory != null && beanFactory.containsBean(beanName)) { groupName = getGroupName(beanFactory.getBeanDefinition(beanName), job); } job = groupName==null ? job : new GroupAwareJob(groupName, job); diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/launch/support/JobRegistryBackgroundJobRunner.java b/spring-batch-core/src/main/java/org/springframework/batch/core/launch/support/JobRegistryBackgroundJobRunner.java index e292d9f86..b0ca7a8e6 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/launch/support/JobRegistryBackgroundJobRunner.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/launch/support/JobRegistryBackgroundJobRunner.java @@ -18,7 +18,9 @@ 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; @@ -27,14 +29,14 @@ 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.configuration.support.ApplicationContextJobFactory; 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.launch.JobLauncher; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.config.AutowireCapableBeanFactory; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.beans.factory.xml.XmlBeanFactory; 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; @@ -80,6 +82,8 @@ public class JobRegistryBackgroundJobRunner { final private String parentContextPath; + private Collection contexts = new HashSet(); + private static List errors = Collections.synchronizedList(new ArrayList()); /** @@ -113,6 +117,7 @@ public class JobRegistryBackgroundJobRunner { 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++) { @@ -120,16 +125,19 @@ public class JobRegistryBackgroundJobRunner { Resource path = resources[j]; logger.info("Registering Job definitions from " + Arrays.toString(resources)); - ConfigurableListableBeanFactory beanFactory = new XmlBeanFactory(path, parentContext - .getAutowireCapableBeanFactory()); - String[] names = beanFactory.getBeanNamesForType(Job.class); + ConfigurableApplicationContext context = createApplicationContext(parentContext, path); + contexts.add(context); + String[] names = context.getBeanNamesForType(Job.class); - for (int k = 0; k < names.length; k++) { - ClassPathXmlApplicationContextFactory factory = new ClassPathXmlApplicationContextFactory(); - factory.setApplicationContext(parentContext); - factory.setPath(path); - logger.info("Registering Job definition: " + names[k]); - registry.register(new ApplicationContextJobFactory(names[k], factory)); + Collection registered = registry.getJobNames(); + // If there is a JobRegistryBeanPostProcessor in there already + // then they can be registered automatically + for (String name : names) { + if (!registered.contains(name) && postProcessorExists) { + logger.debug("Registering job: " + name + " from context: " + path); + JobFactory jobFactory = new ReferenceJobFactory((Job) context.getBean(name)); + registry.register(jobFactory); + } } } @@ -138,6 +146,25 @@ 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 + */ + protected ConfigurableApplicationContext createApplicationContext(ApplicationContext parent, Resource resource) { + ClassPathXmlApplicationContextFactory applicationContextFactory = new ClassPathXmlApplicationContextFactory(); + applicationContextFactory.setPath(resource); + if (parent != null) { + applicationContextFactory.setApplicationContext(parent); + } + return applicationContextFactory.createApplicationContext(); + } + /** * Supply a list of application context locations, starting with the parent * context, and followed by the children. The parent must contain a @@ -194,7 +221,7 @@ public class JobRegistryBackgroundJobRunner { } } errors.clear(); - + // Paths to individual job configurations. final String[] paths = new String[args.length - 1]; System.arraycopy(args, 1, paths, 0, paths.length); @@ -203,6 +230,7 @@ public class JobRegistryBackgroundJobRunner { launcher.register(paths); if (System.getProperty(EMBEDDED) != null) { + launcher.destroy(); return; } @@ -211,9 +239,27 @@ public class JobRegistryBackgroundJobRunner { .println("Started application. Interrupt (CTRL-C) or call JobRegistryBackgroundJobRunner.stop() to exit."); JobRegistryBackgroundJobRunner.class.wait(); } + launcher.destroy(); } + /** + * De-register all the {@link Job} instances that were regsistered by this + * post processor. + * @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(); + } + private void run() { final ApplicationContext parent = new ClassPathXmlApplicationContext(parentContextPath); parent.getAutowireCapableBeanFactory().autowireBeanProperties(this, diff --git a/spring-batch-samples/src/test/java/org/springframework/batch/sample/launch/RemoteLauncherTests.java b/spring-batch-samples/src/test/java/org/springframework/batch/sample/launch/RemoteLauncherTests.java index 6f1a0b3e1..45ea7a5b2 100644 --- a/spring-batch-samples/src/test/java/org/springframework/batch/sample/launch/RemoteLauncherTests.java +++ b/spring-batch-samples/src/test/java/org/springframework/batch/sample/launch/RemoteLauncherTests.java @@ -58,7 +58,9 @@ public class RemoteLauncherTests { @Test public void testConnect() throws Exception { String message = errors.isEmpty() ? "" : errors.get(0).getMessage(); - assertEquals(message, 0, errors.size()); + if (!errors.isEmpty()) { + fail(message); + } assertTrue(isConnected()); } @@ -94,11 +96,11 @@ public class RemoteLauncherTests { // sleep long enough to avoid race conditions (serializable tx isolation // doesn't work with HSQL) Thread.sleep(SLEEP_INTERVAL); -// assertEquals(1, launcher.getRunningExecutions("loopJob").size()); + // assertEquals(1, launcher.getRunningExecutions("loopJob").size()); launcher.stop(executionId); Thread.sleep(SLEEP_INTERVAL); -// assertEquals(0, launcher.getRunningExecutions("loopJob").size()); + // assertEquals(0, launcher.getRunningExecutions("loopJob").size()); logger.debug(launcher.getSummary(executionId)); long resumedId = launcher.restart(executionId); assertNotSame("Picked up the same execution after pause and resume", executionId, resumedId); @@ -107,7 +109,7 @@ public class RemoteLauncherTests { launcher.stop(resumedId); Thread.sleep(SLEEP_INTERVAL); -// assertEquals(0, launcher.getRunningExecutions("loopJob").size()); + // assertEquals(0, launcher.getRunningExecutions("loopJob").size()); logger.debug(launcher.getSummary(resumedId)); long resumeId2 = launcher.restart(resumedId); assertNotSame("Picked up the same execution after pause and resume", executionId, resumeId2); @@ -131,6 +133,8 @@ public class RemoteLauncherTests { JobRegistryBackgroundJobRunner.main("adhoc-job-launcher-context.xml", "jobs/adhocLoopJob.xml"); } catch (Exception e) { + // e.printStackTrace(); + logger.error(e); errors.add(e); } }