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.io.IOException;
20  
21  import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
22  import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
23  import org.springframework.beans.factory.support.DefaultListableBeanFactory;
24  import org.springframework.context.ApplicationContext;
25  import org.springframework.context.ConfigurableApplicationContext;
26  import org.springframework.context.annotation.AnnotationConfigApplicationContext;
27  import org.springframework.context.support.GenericApplicationContext;
28  import org.springframework.context.support.GenericXmlApplicationContext;
29  import org.springframework.core.io.Resource;
30  import org.springframework.util.Assert;
31  
32  /**
33   * {@link ApplicationContextFactory} implementation that takes a parent context and a path to the context to create.
34   * When createApplicationContext method is called, the child {@link ApplicationContext} will be returned. The child
35   * context is not re-created every time it is requested, it is lazily initialized and cached. Clients should ensure that
36   * it is closed when it is no longer needed.
37   * 
38   */
39  public class GenericApplicationContextFactory extends AbstractApplicationContextFactory {
40  
41  	/**
42  	 * Create an application context factory for the resource specified. The resource can be an actual {@link Resource},
43  	 * in which case it will be interpreted as an XML file, or it can be a @Configuration class, or a package name.
44  	 * 
45  	 * @param resource a resource (XML configuration file, @Configuration class or java package to scan)
46  	 */
47  	public GenericApplicationContextFactory(Object resource) {
48  		super(resource);
49  	}
50  
51  	/**
52  	 * @see AbstractApplicationContextFactory#createApplicationContext(ConfigurableApplicationContext, Object)
53  	 */
54  	@Override
55  	protected ConfigurableApplicationContext createApplicationContext(ConfigurableApplicationContext parent,
56  			Object resource) {
57  		if (resource instanceof Resource) {
58  			return new ResourceXmlApplicationContext(parent, (Resource) resource);
59  		}
60  		if (resource instanceof Class<?>) {
61  			return new ResourceAnnotationApplicationContext(parent, (Class<?>) resource);
62  		}
63  		if (resource instanceof String) {
64  			return new ResourceAnnotationApplicationContext(parent, (String) resource);
65  		}
66  		throw new IllegalArgumentException("No application context could be created for resource type: "
67  				+ resource.getClass());
68  	}
69  
70  	private abstract class ApplicationContextHelper {
71  
72  		private final DefaultListableBeanFactory parentBeanFactory;
73  
74  		private final ConfigurableApplicationContext parent;
75  
76  		public ApplicationContextHelper(ConfigurableApplicationContext parent, GenericApplicationContext context,
77  				Object config) {
78  			this.parent = parent;
79  			if (parent != null) {
80  				Assert.isTrue(parent.getBeanFactory() instanceof DefaultListableBeanFactory,
81  						"The parent application context must have a bean factory of type DefaultListableBeanFactory");
82  				parentBeanFactory = (DefaultListableBeanFactory) parent.getBeanFactory();
83  			}
84  			else {
85  				parentBeanFactory = null;
86  			}
87  			context.setParent(parent);
88  			context.setId(generateId(config));
89  			loadConfiguration(config);
90  			prepareContext(parent, context);
91  		}
92  
93  		protected abstract String generateId(Object config);
94  
95  		protected abstract void loadConfiguration(Object config);
96  
97  		protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
98  			if (parentBeanFactory != null) {
99  				GenericApplicationContextFactory.this.prepareBeanFactory(parentBeanFactory, beanFactory);
100 				for (Class<? extends BeanFactoryPostProcessor> cls : getBeanFactoryPostProcessorClasses()) {
101 					for (String name : parent.getBeanNamesForType(cls)) {
102 						beanFactory.registerSingleton(name, ((BeanFactoryPostProcessor) parent.getBean(name)));
103 					}
104 				}
105 			}
106 		}
107 
108 	}
109 
110 	private final class ResourceXmlApplicationContext extends GenericXmlApplicationContext {
111 
112 		private final ApplicationContextHelper helper;
113 
114 		/**
115 		 * @param parent
116 		 */
117 		public ResourceXmlApplicationContext(ConfigurableApplicationContext parent, Resource resource) {
118 			helper = new ApplicationContextHelper(parent, this, resource) {
119 				@Override
120 				protected String generateId(Object config) {
121 					Resource resource = (Resource) config;
122 					try {
123 						return resource.getURI().toString();
124 					}
125 					catch (IOException e) {
126 						return resource.toString();
127 					}
128 				}
129 				@Override
130 				protected void loadConfiguration(Object config) {
131 					Resource resource = (Resource) config;
132 					load(resource);
133 				}
134 			};
135 			refresh();
136 		}
137 
138 		@Override
139 		protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
140 			super.prepareBeanFactory(beanFactory);
141 			helper.prepareBeanFactory(beanFactory);
142 		}
143 
144 		@Override
145 		public String toString() {
146 			return "ResourceXmlApplicationContext:" + getId();
147 		}
148 
149 	}
150 
151 	private final class ResourceAnnotationApplicationContext extends AnnotationConfigApplicationContext {
152 
153 		private final ApplicationContextHelper helper;
154 
155 		public ResourceAnnotationApplicationContext(ConfigurableApplicationContext parent, Object resource) {
156 			helper = new ApplicationContextHelper(parent, this, resource) {
157 				@Override
158 				protected String generateId(Object config) {
159 					if (config instanceof Class) {
160 						Class<?> type = (Class<?>) config;
161 						return type.getName();
162 					}
163 					else {
164 						return config.toString();
165 					}
166 				}
167 				@Override
168 				protected void loadConfiguration(Object config) {
169 					if (config instanceof Class) {
170 						Class<?> type = (Class<?>) config;
171 						register(type);
172 					}
173 					else {
174 						String pkg = (String) config;
175 						scan(pkg);
176 					}
177 				}
178 			};
179 			refresh();
180 		}
181 
182 		@Override
183 		protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
184 			super.prepareBeanFactory(beanFactory);
185 			helper.prepareBeanFactory(beanFactory);
186 		}
187 
188 		@Override
189 		public String toString() {
190 			return "ResourceAnnotationApplicationContext:" + getId();
191 		}
192 
193 	}
194 
195 }