001/* 002 * Copyright 2006-2007 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 org.springframework.beans.factory.config.BeanFactoryPostProcessor; 020import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; 021import org.springframework.beans.factory.support.DefaultListableBeanFactory; 022import org.springframework.context.ApplicationContext; 023import org.springframework.context.ConfigurableApplicationContext; 024import org.springframework.context.annotation.AnnotationConfigApplicationContext; 025import org.springframework.context.support.GenericApplicationContext; 026import org.springframework.context.support.GenericXmlApplicationContext; 027import org.springframework.core.io.Resource; 028import org.springframework.util.Assert; 029import org.springframework.util.StringUtils; 030 031import java.io.IOException; 032import java.util.ArrayList; 033import java.util.Arrays; 034import java.util.List; 035 036/** 037 * {@link ApplicationContextFactory} implementation that takes a parent context and a path to the context to create. 038 * When createApplicationContext method is called, the child {@link ApplicationContext} will be returned. The child 039 * context is not re-created every time it is requested, it is lazily initialized and cached. Clients should ensure that 040 * it is closed when it is no longer needed. 041 * 042 */ 043public class GenericApplicationContextFactory extends AbstractApplicationContextFactory { 044 045 /** 046 * Create an application context factory for the resource specified. The resource can be an actual {@link Resource}, 047 * in which case it will be interpreted as an XML file, or it can be a @Configuration class, or a package name. 048 * All types must be the same (mixing XML with a java package for example is not allowed and will result in an 049 * {@link java.lang.IllegalArgumentException}). 050 * 051 * @param resources some resources (XML configuration files, @Configuration classes or java packages to scan) 052 */ 053 public GenericApplicationContextFactory(Object... resources) { 054 super(resources); 055 } 056 057 /** 058 * @see AbstractApplicationContextFactory#createApplicationContext(ConfigurableApplicationContext, Object...) 059 */ 060 @Override 061 protected ConfigurableApplicationContext createApplicationContext(ConfigurableApplicationContext parent, 062 Object... resources) { 063 ConfigurableApplicationContext context; 064 065 if (allObjectsOfType(resources, Resource.class)) { 066 context = new ResourceXmlApplicationContext(parent, resources); 067 } else if (allObjectsOfType(resources, Class.class)) { 068 context = new ResourceAnnotationApplicationContext(parent, resources); 069 } else if (allObjectsOfType(resources, String.class)) { 070 context = new ResourceAnnotationApplicationContext(parent, resources); 071 } else { 072 List<Class<?>> types = new ArrayList<Class<?>>(); 073 for (Object resource : resources) { 074 types.add(resource.getClass()); 075 } 076 throw new IllegalArgumentException("No application context could be created for resource types: " 077 + Arrays.toString(types.toArray())); 078 } 079 080 return context; 081 } 082 083 private boolean allObjectsOfType(Object[] objects, Class<?> type) { 084 for (Object object : objects) { 085 if (!type.isInstance(object)) { 086 return false; 087 } 088 } 089 return true; 090 } 091 092 private abstract class ApplicationContextHelper { 093 094 private final DefaultListableBeanFactory parentBeanFactory; 095 096 private final ConfigurableApplicationContext parent; 097 098 public ApplicationContextHelper(ConfigurableApplicationContext parent, GenericApplicationContext context, 099 Object... config) { 100 this.parent = parent; 101 if (parent != null) { 102 Assert.isTrue(parent.getBeanFactory() instanceof DefaultListableBeanFactory, 103 "The parent application context must have a bean factory of type DefaultListableBeanFactory"); 104 parentBeanFactory = (DefaultListableBeanFactory) parent.getBeanFactory(); 105 } 106 else { 107 parentBeanFactory = null; 108 } 109 context.setParent(parent); 110 context.setId(generateId(config)); 111 loadConfiguration(config); 112 prepareContext(parent, context); 113 } 114 115 protected abstract String generateId(Object... configs); 116 117 protected abstract void loadConfiguration(Object... configs); 118 119 protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) { 120 if (parentBeanFactory != null) { 121 GenericApplicationContextFactory.this.prepareBeanFactory(parentBeanFactory, beanFactory); 122 for (Class<? extends BeanFactoryPostProcessor> cls : getBeanFactoryPostProcessorClasses()) { 123 for (String name : parent.getBeanNamesForType(cls)) { 124 beanFactory.registerSingleton(name, (parent.getBean(name))); 125 } 126 } 127 } 128 } 129 130 } 131 132 private final class ResourceXmlApplicationContext extends GenericXmlApplicationContext { 133 134 private final ApplicationContextHelper helper; 135 136 /** 137 * @param parent 138 */ 139 public ResourceXmlApplicationContext(ConfigurableApplicationContext parent, Object... resources) { 140 141 class ResourceXmlApplicationContextHelper extends ApplicationContextHelper { 142 143 ResourceXmlApplicationContextHelper(ConfigurableApplicationContext parent, GenericApplicationContext context, Object... config) { 144 super(parent, context, config); 145 } 146 147 @Override 148 protected String generateId(Object... configs) { 149 Resource[] resources = Arrays.copyOfRange(configs, 0, configs.length, Resource[].class); 150 try { 151 List<String> uris = new ArrayList<String>(); 152 for (Resource resource : resources) { 153 uris.add(resource.getURI().toString()); 154 } 155 return StringUtils.collectionToCommaDelimitedString(uris); 156 } 157 catch (IOException e) { 158 return Arrays.toString(resources); 159 } 160 } 161 @Override 162 protected void loadConfiguration(Object... configs) { 163 Resource[] resources = Arrays.copyOfRange(configs, 0, configs.length, Resource[].class); 164 load(resources); 165 } 166 } 167 helper = new ResourceXmlApplicationContextHelper(parent, this, resources); 168 refresh(); 169 } 170 171 @Override 172 protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) { 173 super.prepareBeanFactory(beanFactory); 174 helper.prepareBeanFactory(beanFactory); 175 } 176 177 @Override 178 public String toString() { 179 return "ResourceXmlApplicationContext:" + getId(); 180 } 181 182 } 183 184 private final class ResourceAnnotationApplicationContext extends AnnotationConfigApplicationContext { 185 186 private final ApplicationContextHelper helper; 187 188 public ResourceAnnotationApplicationContext(ConfigurableApplicationContext parent, Object... resources) { 189 190 class ResourceAnnotationApplicationContextHelper extends ApplicationContextHelper { 191 192 public ResourceAnnotationApplicationContextHelper(ConfigurableApplicationContext parent, GenericApplicationContext context, Object... config) { 193 super(parent, context, config); 194 } 195 196 @Override 197 protected String generateId(Object... configs) { 198 if (allObjectsOfType(configs, Class.class)) { 199 Class<?>[] types = Arrays.copyOfRange(configs, 0, configs.length, Class[].class); 200 List<String> names = new ArrayList<String>(); 201 for (Class<?> type : types) { 202 names.add(type.getName()); 203 } 204 return StringUtils.collectionToCommaDelimitedString(names); 205 } 206 else { 207 return Arrays.toString(configs); 208 } 209 } 210 @Override 211 protected void loadConfiguration(Object... configs) { 212 if (allObjectsOfType(configs, Class.class)) { 213 Class<?>[] types = Arrays.copyOfRange(configs, 0, configs.length, Class[].class); 214 register(types); 215 } 216 else { 217 String[] pkgs = Arrays.copyOfRange(configs, 0, configs.length, String[].class); 218 scan(pkgs); 219 } 220 } 221 } 222 helper = new ResourceAnnotationApplicationContextHelper(parent, this, resources); 223 refresh(); 224 } 225 226 @Override 227 protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) { 228 super.prepareBeanFactory(beanFactory); 229 helper.prepareBeanFactory(beanFactory); 230 } 231 232 @Override 233 public String toString() { 234 return "ResourceAnnotationApplicationContext:" + getId(); 235 } 236 237 } 238 239}