001/* 002 * Copyright 2006-2014 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.batch.core.configuration.support; 018 019import java.util.ArrayList; 020import java.util.Arrays; 021import java.util.Collection; 022import java.util.List; 023 024import org.apache.commons.logging.Log; 025import org.apache.commons.logging.LogFactory; 026 027import org.springframework.beans.BeansException; 028import org.springframework.beans.factory.BeanFactoryAware; 029import org.springframework.beans.factory.config.BeanFactoryPostProcessor; 030import org.springframework.beans.factory.config.BeanPostProcessor; 031import org.springframework.beans.factory.config.ConfigurableBeanFactory; 032import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; 033import org.springframework.beans.factory.config.CustomEditorConfigurer; 034import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer; 035import org.springframework.beans.factory.support.AbstractBeanFactory; 036import org.springframework.beans.factory.support.DefaultListableBeanFactory; 037import org.springframework.context.ApplicationContext; 038import org.springframework.context.ApplicationContextAware; 039import org.springframework.context.ConfigurableApplicationContext; 040import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; 041import org.springframework.util.Assert; 042import org.springframework.util.ClassUtils; 043 044/** 045 * {@link ApplicationContextFactory} implementation that takes a parent context and a path to the context to create. 046 * When createApplicationContext method is called, the child {@link ApplicationContext} will be returned. The child 047 * context is not re-created every time it is requested, it is lazily initialized and cached. Clients should ensure that 048 * it is closed when it is no longer needed. If a path is not set, the parent will always be returned. 049 * 050 */ 051public abstract class AbstractApplicationContextFactory implements ApplicationContextFactory, ApplicationContextAware { 052 053 private static final Log logger = LogFactory.getLog(AbstractApplicationContextFactory.class); 054 055 private Object[] resources; 056 057 private ConfigurableApplicationContext parent; 058 059 private boolean copyConfiguration = true; 060 061 private Collection<Class<? extends BeanFactoryPostProcessor>> beanFactoryPostProcessorClasses; 062 063 private Collection<Class<?>> beanPostProcessorExcludeClasses; 064 065 /** 066 * Create a factory instance with the resource specified. The resources are Spring configuration files or java 067 * packages containing configuration files. 068 * 069 * @param resource resource to be used in the creation of the ApplicationContext. 070 */ 071 public AbstractApplicationContextFactory(Object... resource) { 072 073 this.resources = resource; 074 beanFactoryPostProcessorClasses = new ArrayList<Class<? extends BeanFactoryPostProcessor>>(); 075 beanFactoryPostProcessorClasses.add(PropertyPlaceholderConfigurer.class); 076 beanFactoryPostProcessorClasses.add(PropertySourcesPlaceholderConfigurer.class); 077 beanFactoryPostProcessorClasses.add(CustomEditorConfigurer.class); 078 beanPostProcessorExcludeClasses = new ArrayList<Class<?>>(); 079 /* 080 * Assume that a BeanPostProcessor that is BeanFactoryAware must be specific to the parent and remove it from 081 * the child (e.g. an AutoProxyCreator will not work properly). Unfortunately there might still be a a 082 * BeanPostProcessor with a dependency that itself is BeanFactoryAware, but we can't legislate for that here. 083 */ 084 beanPostProcessorExcludeClasses.add(BeanFactoryAware.class); 085 } 086 087 /** 088 * Flag to indicate that configuration such as bean post processors and custom editors should be copied from the 089 * parent context. Defaults to true. 090 * 091 * @param copyConfiguration the flag value to set 092 */ 093 public void setCopyConfiguration(boolean copyConfiguration) { 094 this.copyConfiguration = copyConfiguration; 095 } 096 097 /** 098 * Protected access for subclasses to the flag determining whether configuration should be copied from parent 099 * context. 100 * 101 * @return the flag value 102 */ 103 protected final boolean isCopyConfiguration() { 104 return copyConfiguration; 105 } 106 107 /** 108 * Determines which bean factory post processors (like property placeholders) should be copied from the parent 109 * context. Defaults to {@link PropertyPlaceholderConfigurer} and {@link CustomEditorConfigurer}. 110 * 111 * @param beanFactoryPostProcessorClasses array of post processor types to be copied 112 */ 113 114 public void setBeanFactoryPostProcessorClasses( 115 Class<? extends BeanFactoryPostProcessor>[] beanFactoryPostProcessorClasses) { 116 this.beanFactoryPostProcessorClasses = new ArrayList<Class<? extends BeanFactoryPostProcessor>>(); 117 for (int i = 0; i < beanFactoryPostProcessorClasses.length; i++) { 118 this.beanFactoryPostProcessorClasses.add(beanFactoryPostProcessorClasses[i]); 119 } 120 } 121 122 /** 123 * Determines by exclusion which bean post processors should be copied from the parent context. Defaults to 124 * {@link BeanFactoryAware} (so any post processors that have a reference to the parent bean factory are not copied 125 * into the child). Note that these classes do not themselves have to be {@link BeanPostProcessor} implementations 126 * or sub-interfaces. 127 * 128 * @param beanPostProcessorExcludeClasses the classes to set 129 */ 130 public void setBeanPostProcessorExcludeClasses(Class<?>[] beanPostProcessorExcludeClasses) { 131 this.beanPostProcessorExcludeClasses = new ArrayList<Class<?>>(); 132 for (int i = 0; i < beanPostProcessorExcludeClasses.length; i++) { 133 this.beanPostProcessorExcludeClasses.add(beanPostProcessorExcludeClasses[i]); 134 } 135 136 } 137 138 /** 139 * Protected access to the list of bean factory post processor classes that should be copied over to the context 140 * from the parent. 141 * 142 * @return the classes for post processors that were nominated for copying 143 */ 144 protected final Collection<Class<? extends BeanFactoryPostProcessor>> getBeanFactoryPostProcessorClasses() { 145 return beanFactoryPostProcessorClasses; 146 } 147 148 /** 149 * Setter for the parent application context. 150 * 151 * @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext) 152 */ 153 @Override 154 public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 155 if (applicationContext == null) { 156 return; 157 } 158 Assert.isInstanceOf(ConfigurableApplicationContext.class, applicationContext); 159 parent = (ConfigurableApplicationContext) applicationContext; 160 } 161 162 /** 163 * Creates an {@link ApplicationContext} from the provided path. 164 * 165 * @see ApplicationContextFactory#createApplicationContext() 166 */ 167 @Override 168 public ConfigurableApplicationContext createApplicationContext() { 169 170 if (resources == null || resources.length == 0) { 171 return parent; 172 } 173 174 return createApplicationContext(parent, resources); 175 176 } 177 178 protected abstract ConfigurableApplicationContext createApplicationContext(ConfigurableApplicationContext parent, 179 Object... resources); 180 181 /** 182 * Extension point for special subclasses that want to do more complex things with the context prior to refresh. The 183 * default implementation does nothing. 184 * 185 * @param parent the parent for the new application context 186 * @param context the new application context before it is refreshed, but after bean factory is initialized 187 * 188 * @see AbstractApplicationContextFactory#setBeanFactoryPostProcessorClasses(Class[]) 189 */ 190 protected void prepareContext(ConfigurableApplicationContext parent, ConfigurableApplicationContext context) { 191 } 192 193 /** 194 * Extension point for special subclasses that want to do more complex things with the bean factory prior to 195 * refresh. The default implementation copies all configuration from the parent according to the 196 * {@link #setCopyConfiguration(boolean) flag} set. 197 * 198 * @param parent the parent bean factory for the new context (will never be null) 199 * @param beanFactory the new bean factory before bean definitions are loaded 200 * 201 * @see AbstractApplicationContextFactory#setCopyConfiguration(boolean) 202 * @see DefaultListableBeanFactory#copyConfigurationFrom(ConfigurableBeanFactory) 203 */ 204 protected void prepareBeanFactory(ConfigurableListableBeanFactory parent, 205 ConfigurableListableBeanFactory beanFactory) { 206 if (copyConfiguration && parent != null) { 207 List<BeanPostProcessor> parentPostProcessors = new ArrayList<BeanPostProcessor>(); 208 List<BeanPostProcessor> childPostProcessors = new ArrayList<BeanPostProcessor>(); 209 210 childPostProcessors.addAll(beanFactory instanceof AbstractBeanFactory ? ((AbstractBeanFactory) beanFactory) 211 .getBeanPostProcessors() : new ArrayList<BeanPostProcessor>()); 212 parentPostProcessors.addAll(parent instanceof AbstractBeanFactory ? ((AbstractBeanFactory) parent) 213 .getBeanPostProcessors() : new ArrayList<BeanPostProcessor>()); 214 215 try { 216 Class<?> applicationContextAwareProcessorClass = 217 ClassUtils.forName("org.springframework.context.support.ApplicationContextAwareProcessor", 218 parent.getBeanClassLoader()); 219 220 for (BeanPostProcessor beanPostProcessor : new ArrayList<BeanPostProcessor>(parentPostProcessors)) { 221 if (applicationContextAwareProcessorClass.isAssignableFrom(beanPostProcessor.getClass())) { 222 logger.debug("Removing parent ApplicationContextAwareProcessor"); 223 parentPostProcessors.remove(beanPostProcessor); 224 } 225 } 226 } 227 catch (ClassNotFoundException e) { 228 throw new IllegalStateException(e); 229 } 230 231 List<BeanPostProcessor> aggregatedPostProcessors = new ArrayList<BeanPostProcessor>(); 232 aggregatedPostProcessors.addAll(childPostProcessors); 233 aggregatedPostProcessors.addAll(parentPostProcessors); 234 235 for (BeanPostProcessor beanPostProcessor : new ArrayList<BeanPostProcessor>(aggregatedPostProcessors)) { 236 for (Class<?> cls : beanPostProcessorExcludeClasses) { 237 if (cls.isAssignableFrom(beanPostProcessor.getClass())) { 238 if (logger.isDebugEnabled()) { 239 logger.debug("Removing bean post processor: " + beanPostProcessor + " of type " + cls); 240 } 241 aggregatedPostProcessors.remove(beanPostProcessor); 242 } 243 } 244 } 245 246 beanFactory.copyConfigurationFrom(parent); 247 248 List<BeanPostProcessor> beanPostProcessors = beanFactory instanceof AbstractBeanFactory ? ((AbstractBeanFactory) beanFactory) 249 .getBeanPostProcessors() : new ArrayList<BeanPostProcessor>(); 250 251 beanPostProcessors.clear(); 252 beanPostProcessors.addAll(aggregatedPostProcessors); 253 } 254 } 255 256 @Override 257 public String toString() { 258 return "ApplicationContextFactory [resources=" + Arrays.toString(resources) + "]"; 259 } 260 261 @Override 262 public int hashCode() { 263 return toString().hashCode(); 264 } 265 266 @Override 267 public boolean equals(Object obj) { 268 if (this == obj) { 269 return true; 270 } 271 if (obj == null) { 272 return false; 273 } 274 return toString().equals(obj.toString()); 275 } 276 277}