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