From 7683700662370749fef71084d3108884af06a09b Mon Sep 17 00:00:00 2001 From: Michael Minella Date: Tue, 4 Nov 2014 16:12:50 -0600 Subject: [PATCH] Filter out duplicate ApplicationContextAwareProcessors in parent/child contexts scenarios AbstractApplicationContextFactory, when configuring a child context, copies all of the BeanPostProcessors from the parent to the child. Normally this is a good thing. However, in the case of teh ApplicationContextAwareProcessor, the child context ends up with two instances, one for the parent context and one for the child context. This brings two issues to light: 1. Unknown which context is being injected - On beans implementing ApplicationContextAware, it can't be determined which context (the parent or the child) will be injected since both BPPs will be called in an indeterimant order. 2. Errors occur with ApplicationObjectSupport - If the child context contains a bean that extends ApplicationObjectSupport, when the second ApplicationContextAwareProcessor is called, the bean will throw an exception since it is only allowed to be initialized with one application context. This fix adds additional logic to remove the ApplicationContextAwareProcessor from the parent context before adding all the BPPs to the child. This fix addresses BATCH-2319. --- .../AbstractApplicationContextFactory.java | 61 +++++++++++++++---- .../JobLoaderConfigurationTests.java | 15 +++-- 2 files changed, 59 insertions(+), 17 deletions(-) diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/AbstractApplicationContextFactory.java b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/AbstractApplicationContextFactory.java index 86ed7749a..a1971f9a2 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/AbstractApplicationContextFactory.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/AbstractApplicationContextFactory.java @@ -16,8 +16,14 @@ package org.springframework.batch.core.configuration.support; +import java.util.ArrayList; +import java.util.Arrays; +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; @@ -33,11 +39,7 @@ import org.springframework.context.ApplicationContextAware; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; import org.springframework.util.Assert; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; +import org.springframework.util.ClassUtils; /** * {@link ApplicationContextFactory} implementation that takes a parent context and a path to the context to create. @@ -200,17 +202,50 @@ public abstract class AbstractApplicationContextFactory implements ApplicationCo protected void prepareBeanFactory(ConfigurableListableBeanFactory parent, ConfigurableListableBeanFactory beanFactory) { if (copyConfiguration && parent != null) { + List parentPostProcessors = new ArrayList(); + List childPostProcessors = new ArrayList(); + + childPostProcessors.addAll(beanFactory instanceof AbstractBeanFactory ? ((AbstractBeanFactory) beanFactory) + .getBeanPostProcessors() : new ArrayList()); + parentPostProcessors.addAll(parent instanceof AbstractBeanFactory ? ((AbstractBeanFactory) parent) + .getBeanPostProcessors() : new ArrayList()); + + try { + Class applicationContextAwareProcessorClass = + ClassUtils.forName("org.springframework.context.support.ApplicationContextAwareProcessor", + parent.getBeanClassLoader()); + + for (BeanPostProcessor beanPostProcessor : new ArrayList(parentPostProcessors)) { + if (applicationContextAwareProcessorClass.isAssignableFrom(beanPostProcessor.getClass())) { + logger.debug("Removing parent ApplicationContextAwareProcessor"); + parentPostProcessors.remove(beanPostProcessor); + } + } + } + catch (ClassNotFoundException e) { + throw new IllegalStateException(e); + } + + List aggregatedPostProcessors = new ArrayList(); + aggregatedPostProcessors.addAll(childPostProcessors); + aggregatedPostProcessors.addAll(parentPostProcessors); + + for (BeanPostProcessor beanPostProcessor : new ArrayList(aggregatedPostProcessors)) { + for (Class cls : beanPostProcessorExcludeClasses) { + if (cls.isAssignableFrom(beanPostProcessor.getClass())) { + logger.debug("Removing bean post processor: " + beanPostProcessor + " of type " + cls); + aggregatedPostProcessors.remove(beanPostProcessor); + } + } + } + beanFactory.copyConfigurationFrom(parent); + List beanPostProcessors = beanFactory instanceof AbstractBeanFactory ? ((AbstractBeanFactory) beanFactory) .getBeanPostProcessors() : new ArrayList(); - for (BeanPostProcessor beanPostProcessor : new ArrayList(beanPostProcessors)) { - for (Class cls : beanPostProcessorExcludeClasses) { - if (cls.isAssignableFrom(beanPostProcessor.getClass())) { - logger.debug("Removing bean post processor: " + beanPostProcessor + " of type " + cls); - beanPostProcessors.remove(beanPostProcessor); - } - } - } + + beanPostProcessors.clear(); + beanPostProcessors.addAll(aggregatedPostProcessors); } } diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/annotation/JobLoaderConfigurationTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/annotation/JobLoaderConfigurationTests.java index 2e1f801ec..bfc96b79e 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/annotation/JobLoaderConfigurationTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/annotation/JobLoaderConfigurationTests.java @@ -15,7 +15,12 @@ */ package org.springframework.batch.core.configuration.annotation; +import static org.junit.Assert.assertEquals; + +import javax.annotation.PostConstruct; + import org.junit.Test; + import org.springframework.batch.core.BatchStatus; import org.springframework.batch.core.Job; import org.springframework.batch.core.JobExecution; @@ -36,10 +41,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; - -import javax.annotation.PostConstruct; - -import static org.junit.Assert.assertEquals; +import org.springframework.context.support.ApplicationObjectSupport; /** * @author Dave Syer @@ -114,6 +116,11 @@ public class JobLoaderConfigurationTests { @Configuration public static class TestConfiguration { + @Bean + public ApplicationObjectSupport fakeApplicationObjectSupport() { + return new ApplicationObjectSupport() {}; + } + @Autowired private JobBuilderFactory jobs;