Permalink
Browse files

BATCH-1473: Refactored context creation components

  • Loading branch information...
1 parent 4911ea7 commit 891ff55bf0629c52c6c485b50c60486ce228dd7f dsyer committed Jan 2, 2010
Showing with 748 additions and 363 deletions.
  1. +1 −1 spring-batch-core/.springBeans
  2. +167 −0 ...ore/src/main/java/org/springframework/batch/core/configuration/support/AutomaticJobRegistrar.java
  3. +57 −21 ...a/org/springframework/batch/core/configuration/support/ClassPathXmlApplicationContextFactory.java
  4. +0 −208 ...ore/src/main/java/org/springframework/batch/core/configuration/support/ClassPathXmlJobLoader.java
  5. +1 −1 ...e/src/main/java/org/springframework/batch/core/configuration/support/ClassPathXmlJobRegistry.java
  6. +166 −0 .../springframework/batch/core/configuration/support/ClasspathXmlApplicationContextsFactoryBean.java
  7. +115 −0 ...tch-core/src/main/java/org/springframework/batch/core/configuration/support/DefaultJobLoader.java
  8. +1 −1 ...-batch-core/src/main/java/org/springframework/batch/core/configuration/support/GroupAwareJob.java
  9. +44 −0 spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/JobLoader.java
  10. +3 −3 .../org/springframework/batch/core/configuration/support/OsgiBundleXmlApplicationContextFactory.java
  11. +47 −50 ...e/src/main/java/org/springframework/batch/core/launch/support/JobRegistryBackgroundJobRunner.java
  12. +1 −1 ...uration/support/{ClassPathXmlJobLoaderContextTests.java → AutomaticJobRegistrarContextTests.java}
  13. +68 −35 ...ch/core/configuration/support/{ClassPathXmlJobLoaderTests.java → AutomaticJobRegistrarTests.java}
  14. +5 −5 .../springframework/batch/core/configuration/support/ClassPathXmlApplicationContextFactoryTests.java
  15. +0 −1 ...springframework/batch/core/configuration/support/OsgiBundleXmlApplicationContextFactoryTests.java
  16. +8 −0 .../test/java/org/springframework/batch/core/launch/support/JobRegistryBackgroundJobRunnerTests.java
  17. +20 −0 ...rg/springframework/batch/core/configuration/support/AutomaticJobRegistrarContextTests-context.xml
  18. +0 −12 ...rg/springframework/batch/core/configuration/support/ClassPathXmlJobLoaderContextTests-context.xml
  19. +18 −0 ...src/test/resources/org/springframework/batch/core/launch/support/test-environment-with-loader.xml
  20. +26 −24 spring-batch-samples/src/main/resources/skipSample-job-launcher-context.xml
@@ -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 @@
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,33 +48,42 @@
*/
public class ClassPathXmlApplicationContextFactory implements ApplicationContextFactory, ApplicationContextAware {
- private ConfigurableApplicationContext parent;
-
- private Resource path;
+ private static final Log logger = LogFactory.getLog(ClassPathXmlApplicationContextFactory.class);
- private ResourceXmlApplicationContext context;
+ private Resource resource;
+ private ConfigurableApplicationContext parent;
+
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);
}
/**
* Setter for the path to the xml to load to create an
* {@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;
}
/**
@@ -110,6 +124,23 @@ public void setBeanFactoryPostProcessorClasses(
}
/**
+ * 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 void setBeanFactoryPostProcessorClasses(
* @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 void setApplicationContext(ApplicationContext applicationContext) throws
*/
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 @@ protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
@Override
protected Resource[] getConfigResources() {
- return new Resource[] { path };
+ return new Resource[] { resource };
}
}
@@ -218,7 +244,8 @@ protected void prepareContext(ConfigurableApplicationContext parent, Configurabl
/**
* 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 @@ protected void prepareContext(ConfigurableApplicationContext parent, Configurabl
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);
+ }
+ }
+ }
}
}
-
}
Oops, something went wrong.

0 comments on commit 891ff55

Please sign in to comment.