View Javadoc

1   /*
2    * Copyright 2006-2007 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.springframework.batch.core.configuration.support;
18  
19  import java.util.ArrayList;
20  import java.util.Collection;
21  import java.util.List;
22  
23  import org.apache.commons.logging.Log;
24  import org.apache.commons.logging.LogFactory;
25  import org.springframework.beans.BeansException;
26  import org.springframework.beans.factory.BeanFactoryAware;
27  import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
28  import org.springframework.beans.factory.config.BeanPostProcessor;
29  import org.springframework.beans.factory.config.ConfigurableBeanFactory;
30  import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
31  import org.springframework.beans.factory.config.CustomEditorConfigurer;
32  import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
33  import org.springframework.beans.factory.support.AbstractBeanFactory;
34  import org.springframework.beans.factory.support.DefaultListableBeanFactory;
35  import org.springframework.context.ApplicationContext;
36  import org.springframework.context.ApplicationContextAware;
37  import org.springframework.context.ConfigurableApplicationContext;
38  import org.springframework.util.Assert;
39  
40  /**
41   * {@link ApplicationContextFactory} implementation that takes a parent context and a path to the context to create.
42   * When createApplicationContext method is called, the child {@link ApplicationContext} will be returned. The child
43   * context is not re-created every time it is requested, it is lazily initialized and cached. Clients should ensure that
44   * it is closed when it is no longer needed. If a path is not set, the parent will always be returned.
45   *
46   */
47  public abstract class AbstractApplicationContextFactory implements ApplicationContextFactory, ApplicationContextAware {
48  
49  	private static final Log logger = LogFactory.getLog(AbstractApplicationContextFactory.class);
50  
51  	private Object resource;
52  
53  	private ConfigurableApplicationContext parent;
54  
55  	private boolean copyConfiguration = true;
56  
57  	private Collection<Class<? extends BeanFactoryPostProcessor>> beanFactoryPostProcessorClasses;
58  
59  	private Collection<Class<?>> beanPostProcessorExcludeClasses;
60  
61  	/**
62  	 * Create a factory instance with the resource specified. The resource is a Spring configuration file or java
63  	 * package containing configuration files.
64  	 */
65  	public AbstractApplicationContextFactory(Object resource) {
66  
67  		this.resource = resource;
68  		beanFactoryPostProcessorClasses = new ArrayList<Class<? extends BeanFactoryPostProcessor>>();
69  		beanFactoryPostProcessorClasses.add(PropertyPlaceholderConfigurer.class);
70  		beanFactoryPostProcessorClasses.add(CustomEditorConfigurer.class);
71  		beanPostProcessorExcludeClasses = new ArrayList<Class<?>>();
72  		/*
73  		 * Assume that a BeanPostProcessor that is BeanFactoryAware must be specific to the parent and remove it from
74  		 * the child (e.g. an AutoProxyCreator will not work properly). Unfortunately there might still be a a
75  		 * BeanPostProcessor with a dependency that itself is BeanFactoryAware, but we can't legislate for that here.
76  		 */
77  		beanPostProcessorExcludeClasses.add(BeanFactoryAware.class);
78  	}
79  
80  	/**
81  	 * Flag to indicate that configuration such as bean post processors and custom editors should be copied from the
82  	 * parent context. Defaults to true.
83  	 *
84  	 * @param copyConfiguration the flag value to set
85  	 */
86  	public void setCopyConfiguration(boolean copyConfiguration) {
87  		this.copyConfiguration = copyConfiguration;
88  	}
89  
90  	/**
91  	 * Protected access for subclasses to the flag determining whether configuration should be copied from parent
92  	 * context.
93  	 *
94  	 * @return the flag value
95  	 */
96  	protected final boolean isCopyConfiguration() {
97  		return copyConfiguration;
98  	}
99  
100 	/**
101 	 * Determines which bean factory post processors (like property placeholders) should be copied from the parent
102 	 * context. Defaults to {@link PropertyPlaceholderConfigurer} and {@link CustomEditorConfigurer}.
103 	 *
104 	 * @param beanFactoryPostProcessorClasses array of post processor types to be copied
105 	 */
106 
107 	public void setBeanFactoryPostProcessorClasses(
108 			Class<? extends BeanFactoryPostProcessor>[] beanFactoryPostProcessorClasses) {
109 		this.beanFactoryPostProcessorClasses = new ArrayList<Class<? extends BeanFactoryPostProcessor>>();
110 		for (int i = 0; i < beanFactoryPostProcessorClasses.length; i++) {
111 			this.beanFactoryPostProcessorClasses.add(beanFactoryPostProcessorClasses[i]);
112 		}
113 	}
114 
115 	/**
116 	 * Determines by exclusion which bean post processors should be copied from the parent context. Defaults to
117 	 * {@link BeanFactoryAware} (so any post processors that have a reference to the parent bean factory are not copied
118 	 * into the child). Note that these classes do not themselves have to be {@link BeanPostProcessor} implementations
119 	 * or sub-interfaces.
120 	 *
121 	 * @param beanPostProcessorExcludeClasses the classes to set
122 	 */
123 	public void setBeanPostProcessorExcludeClasses(Class<?>[] beanPostProcessorExcludeClasses) {
124 		this.beanPostProcessorExcludeClasses = new ArrayList<Class<?>>();
125 		for (int i = 0; i < beanPostProcessorExcludeClasses.length; i++) {
126 			this.beanPostProcessorExcludeClasses.add(beanPostProcessorExcludeClasses[i]);
127 		}
128 
129 	}
130 
131 	/**
132 	 * Protected access to the list of bean factory post processor classes that should be copied over to the context
133 	 * from the parent.
134 	 *
135 	 * @return the classes for post processors that were nominated for copying
136 	 */
137 	protected final Collection<Class<? extends BeanFactoryPostProcessor>> getBeanFactoryPostProcessorClasses() {
138 		return beanFactoryPostProcessorClasses;
139 	}
140 
141 	/**
142 	 * Setter for the parent application context.
143 	 *
144 	 * @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext)
145 	 */
146 	@Override
147 	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
148 		if (applicationContext == null) {
149 			return;
150 		}
151 		Assert.isInstanceOf(ConfigurableApplicationContext.class, applicationContext);
152 		parent = (ConfigurableApplicationContext) applicationContext;
153 	}
154 
155 	/**
156 	 * Creates an {@link ApplicationContext} from the provided path.
157 	 *
158 	 * @see ApplicationContextFactory#createApplicationContext()
159 	 */
160 	@Override
161 	public ConfigurableApplicationContext createApplicationContext() {
162 
163 		if (resource == null) {
164 			return parent;
165 		}
166 
167 		return createApplicationContext(parent, resource);
168 
169 	}
170 
171 	protected abstract ConfigurableApplicationContext createApplicationContext(ConfigurableApplicationContext parent,
172 			Object resource);
173 
174 	/**
175 	 * Extension point for special subclasses that want to do more complex things with the context prior to refresh. The
176 	 * default implementation does nothing.
177 	 *
178 	 * @param parent the parent for the new application context
179 	 * @param context the new application context before it is refreshed, but after bean factory is initialized
180 	 *
181 	 * @see AbstractApplicationContextFactory#setBeanFactoryPostProcessorClasses(Class[])
182 	 */
183 	protected void prepareContext(ConfigurableApplicationContext parent, ConfigurableApplicationContext context) {
184 	}
185 
186 	/**
187 	 * Extension point for special subclasses that want to do more complex things with the bean factory prior to
188 	 * refresh. The default implementation copies all configuration from the parent according to the
189 	 * {@link #setCopyConfiguration(boolean) flag} set.
190 	 *
191 	 * @param parent the parent bean factory for the new context (will never be null)
192 	 * @param beanFactory the new bean factory before bean definitions are loaded
193 	 *
194 	 * @see AbstractApplicationContextFactory#setCopyConfiguration(boolean)
195 	 * @see DefaultListableBeanFactory#copyConfigurationFrom(ConfigurableBeanFactory)
196 	 */
197 	protected void prepareBeanFactory(ConfigurableListableBeanFactory parent,
198 			ConfigurableListableBeanFactory beanFactory) {
199 		if (copyConfiguration && parent != null) {
200 			beanFactory.copyConfigurationFrom(parent);
201 			List<BeanPostProcessor> beanPostProcessors = beanFactory instanceof AbstractBeanFactory ? ((AbstractBeanFactory) beanFactory)
202 					.getBeanPostProcessors() : new ArrayList<BeanPostProcessor>();
203 			for (BeanPostProcessor beanPostProcessor : new ArrayList<BeanPostProcessor>(beanPostProcessors)) {
204 				for (Class<?> cls : beanPostProcessorExcludeClasses) {
205 					if (cls.isAssignableFrom(beanPostProcessor.getClass())) {
206 						logger.debug("Removing bean post processor: " + beanPostProcessor + " of type " + cls);
207 						beanPostProcessors.remove(beanPostProcessor);
208 					}
209 				}
210 			}
211 		}
212 	}
213 
214 	@Override
215 	public String toString() {
216 		return "ApplicationContextFactory [resource=" + resource + "]";
217 	}
218 
219 	@Override
220 	public int hashCode() {
221 		return toString().hashCode();
222 	}
223 
224 	@Override
225 	public boolean equals(Object obj) {
226 		if (this == obj)
227 			return true;
228 		if (obj == null)
229 			return false;
230 		return toString().equals(obj.toString());
231 	}
232 
233 }