Skip to content

Commit

Permalink
BATCH-1473: Refactored context creation components
Browse files Browse the repository at this point in the history
  • Loading branch information
dsyer committed Jan 2, 2010
1 parent 4911ea7 commit 891ff55
Show file tree
Hide file tree
Showing 20 changed files with 748 additions and 363 deletions.
2 changes: 1 addition & 1 deletion spring-batch-core/.springBeans
Expand Up @@ -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>
Expand Down Expand Up @@ -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>
Expand Down
@@ -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;
}
}

}
Expand Up @@ -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;
Expand All @@ -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;
}

/**
Expand Down Expand Up @@ -109,6 +123,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.
Expand All @@ -125,6 +156,9 @@ protected final Collection<Class<? extends BeanFactoryPostProcessor>> getBeanFac
* @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;
}
Expand All @@ -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);

}

Expand Down Expand Up @@ -188,7 +214,7 @@ protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {

@Override
protected Resource[] getConfigResources() {
return new Resource[] { path };
return new Resource[] { resource };
}

}
Expand Down Expand Up @@ -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)
Expand All @@ -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);
}
}
}
}
}

}

0 comments on commit 891ff55

Please sign in to comment.