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 org.springframework.beans.factory.config.BeanFactoryPostProcessor; |
20 | import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; |
21 | import org.springframework.beans.factory.support.DefaultListableBeanFactory; |
22 | import org.springframework.context.ApplicationContext; |
23 | import org.springframework.context.ConfigurableApplicationContext; |
24 | import org.springframework.context.annotation.AnnotationConfigApplicationContext; |
25 | import org.springframework.context.support.GenericApplicationContext; |
26 | import org.springframework.context.support.GenericXmlApplicationContext; |
27 | import org.springframework.core.io.Resource; |
28 | import org.springframework.util.Assert; |
29 | import org.springframework.util.StringUtils; |
30 | |
31 | import java.io.IOException; |
32 | import java.util.ArrayList; |
33 | import java.util.Arrays; |
34 | import java.util.List; |
35 | |
36 | /** |
37 | * {@link ApplicationContextFactory} implementation that takes a parent context and a path to the context to create. |
38 | * When createApplicationContext method is called, the child {@link ApplicationContext} will be returned. The child |
39 | * context is not re-created every time it is requested, it is lazily initialized and cached. Clients should ensure that |
40 | * it is closed when it is no longer needed. |
41 | * |
42 | */ |
43 | public class GenericApplicationContextFactory extends AbstractApplicationContextFactory { |
44 | |
45 | /** |
46 | * Create an application context factory for the resource specified. The resource can be an actual {@link Resource}, |
47 | * in which case it will be interpreted as an XML file, or it can be a @Configuration class, or a package name. |
48 | * All types must be the same (mixing XML with a java package for example is not allowed and will result in an |
49 | * {@link java.lang.IllegalArgumentException}). |
50 | * |
51 | * @param resources some resources (XML configuration files, @Configuration classes or java packages to scan) |
52 | */ |
53 | public GenericApplicationContextFactory(Object... resources) { |
54 | super(resources); |
55 | } |
56 | |
57 | /** |
58 | * @see AbstractApplicationContextFactory#createApplicationContext(ConfigurableApplicationContext, Object...) |
59 | */ |
60 | @Override |
61 | protected ConfigurableApplicationContext createApplicationContext(ConfigurableApplicationContext parent, |
62 | Object... resources) { |
63 | ConfigurableApplicationContext context; |
64 | |
65 | if (allObjectsOfType(resources, Resource.class)) { |
66 | context = new ResourceXmlApplicationContext(parent, resources); |
67 | } else if (allObjectsOfType(resources, Class.class)) { |
68 | context = new ResourceAnnotationApplicationContext(parent, resources); |
69 | } else if (allObjectsOfType(resources, String.class)) { |
70 | context = new ResourceAnnotationApplicationContext(parent, resources); |
71 | } else { |
72 | List<Class<?>> types = new ArrayList<Class<?>>(); |
73 | for (Object resource : resources) { |
74 | types.add(resource.getClass()); |
75 | } |
76 | throw new IllegalArgumentException("No application context could be created for resource types: " |
77 | + Arrays.toString(types.toArray())); |
78 | } |
79 | |
80 | return context; |
81 | } |
82 | |
83 | private boolean allObjectsOfType(Object[] objects, Class<?> type) { |
84 | for (Object object : objects) { |
85 | if (!type.isInstance(object)) { |
86 | return false; |
87 | } |
88 | } |
89 | return true; |
90 | } |
91 | |
92 | private abstract class ApplicationContextHelper { |
93 | |
94 | private final DefaultListableBeanFactory parentBeanFactory; |
95 | |
96 | private final ConfigurableApplicationContext parent; |
97 | |
98 | public ApplicationContextHelper(ConfigurableApplicationContext parent, GenericApplicationContext context, |
99 | 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... config); |
116 | |
117 | protected abstract void loadConfiguration(Object... config); |
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 | helper = new ApplicationContextHelper(parent, this, resources) { |
141 | @Override |
142 | protected String generateId(Object... configs) { |
143 | Resource[] resources = Arrays.copyOfRange(configs, 0, configs.length, Resource[].class); |
144 | try { |
145 | List<String> uris = new ArrayList<String>(); |
146 | for (Resource resource : resources) { |
147 | uris.add(resource.getURI().toString()); |
148 | } |
149 | return StringUtils.collectionToCommaDelimitedString(uris); |
150 | } |
151 | catch (IOException e) { |
152 | return Arrays.toString(resources); |
153 | } |
154 | } |
155 | @Override |
156 | protected void loadConfiguration(Object... configs) { |
157 | Resource[] resources = Arrays.copyOfRange(configs, 0, configs.length, Resource[].class); |
158 | load(resources); |
159 | } |
160 | }; |
161 | refresh(); |
162 | } |
163 | |
164 | @Override |
165 | protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) { |
166 | super.prepareBeanFactory(beanFactory); |
167 | helper.prepareBeanFactory(beanFactory); |
168 | } |
169 | |
170 | @Override |
171 | public String toString() { |
172 | return "ResourceXmlApplicationContext:" + getId(); |
173 | } |
174 | |
175 | } |
176 | |
177 | private final class ResourceAnnotationApplicationContext extends AnnotationConfigApplicationContext { |
178 | |
179 | private final ApplicationContextHelper helper; |
180 | |
181 | public ResourceAnnotationApplicationContext(ConfigurableApplicationContext parent, Object... resources) { |
182 | helper = new ApplicationContextHelper(parent, this, resources) { |
183 | @Override |
184 | protected String generateId(Object... configs) { |
185 | if (allObjectsOfType(configs, Class.class)) { |
186 | Class<?>[] types = Arrays.copyOfRange(configs, 0, configs.length, Class[].class); |
187 | List<String> names = new ArrayList<String>(); |
188 | for (Class<?> type : types) { |
189 | names.add(type.getName()); |
190 | } |
191 | return StringUtils.collectionToCommaDelimitedString(names); |
192 | } else { |
193 | return Arrays.toString(configs); |
194 | } |
195 | } |
196 | @Override |
197 | protected void loadConfiguration(Object... configs) { |
198 | if (allObjectsOfType(configs, Class.class)) { |
199 | Class<?>[] types = Arrays.copyOfRange(configs, 0, configs.length, Class[].class); |
200 | register(types); |
201 | } else { |
202 | String[] pkgs = Arrays.copyOfRange(configs, 0, configs.length, String[].class); |
203 | scan(pkgs); |
204 | } |
205 | } |
206 | }; |
207 | refresh(); |
208 | } |
209 | |
210 | @Override |
211 | protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) { |
212 | super.prepareBeanFactory(beanFactory); |
213 | helper.prepareBeanFactory(beanFactory); |
214 | } |
215 | |
216 | @Override |
217 | public String toString() { |
218 | return "ResourceAnnotationApplicationContext:" + getId(); |
219 | } |
220 | |
221 | } |
222 | |
223 | } |