2 * Copyright 2007 the original author or authors.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 package org.wamblee.system.spring;
18 import org.springframework.beans.MutablePropertyValues;
19 import org.springframework.beans.factory.config.BeanDefinition;
20 import org.springframework.beans.factory.config.ConstructorArgumentValues;
21 import org.springframework.beans.factory.support.RootBeanDefinition;
23 import org.springframework.context.support.AbstractApplicationContext;
24 import org.springframework.context.support.ClassPathXmlApplicationContext;
25 import org.springframework.context.support.GenericApplicationContext;
27 import org.wamblee.system.core.AbstractComponent;
28 import org.wamblee.system.core.DefaultScope;
29 import org.wamblee.system.core.ProvidedInterface;
30 import org.wamblee.system.core.RequiredInterface;
31 import org.wamblee.system.core.Scope;
32 import org.wamblee.system.core.SystemAssemblyException;
34 import java.util.HashMap;
36 import java.util.Properties;
40 * Represents a system configured based on spring. The spring config files
41 * that are configured should not contain any PropertyPlaceholderConfigurer
44 * @author Erik Brakkee
46 public class SpringComponent extends AbstractComponent<Scope> {
50 private static final String CONTEXT_KEY = "context";
55 static final ThreadLocal<SpringComponent> THIS = new ThreadLocal<SpringComponent>();
60 static final ThreadLocal<Scope> SCOPE = new ThreadLocal<Scope>();
65 private Properties properties;
70 private String[] configFiles;
75 private Map<String, ProvidedInterface> provided;
80 private Map<RequiredInterface, String> required;
85 private Map<String, Properties> propertyObjects;
88 * Constructs a spring system.
93 * Spring config files to read.
95 * Map of bean name to service descriptor describing the bean
96 * names that the spring config files use for each required
99 * Map of bean name to service descriptor describing the bean
100 * names that the spring config files use for each required
103 public SpringComponent(String aName, String[] aConfigFiles,
104 Map<String, ProvidedInterface> aProvided,
105 Map<RequiredInterface, String> aRequired) {
106 super(aName, aProvided.values().toArray(new ProvidedInterface[0]),
107 aRequired.keySet().toArray(new RequiredInterface[0]));
108 properties = new Properties();
109 configFiles = aConfigFiles;
110 provided = aProvided;
111 required = aRequired;
112 propertyObjects = new HashMap<String, Properties>();
116 * Must be called to make a property available in the application
119 * @param aKey Property key.
120 * @param aValue Property value.
122 public void setProperty(String aKey, String aValue) {
123 properties.put(aKey, aValue);
129 * @param aProperties DOCUMENT ME!
131 public void addProperties(Properties aProperties) {
132 for (Object key : aProperties.keySet()) {
133 setProperty((String) key, aProperties.getProperty((String) key));
140 * @param aBeanname DOCUMENT ME!
141 * @param aProperties DOCUMENT ME!
143 public void addProperties(String aBeanname, Properties aProperties) {
144 propertyObjects.put(aBeanname, aProperties);
150 * @param aBeanname DOCUMENT ME!
152 * @return DOCUMENT ME!
154 public Properties getProperties(String aBeanname) {
155 return propertyObjects.get(aBeanname);
161 * @param aExternalScope DOCUMENT ME!
163 * @return DOCUMENT ME!
165 * @throws SystemAssemblyException DOCUMENT ME!
168 protected Scope doStart(Scope aExternalScope) {
169 SpringComponent old = THIS.get();
170 Scope oldScope = SCOPE.get();
173 Scope scope = new DefaultScope(getProvidedInterfaces()
174 .toArray(new ProvidedInterface[0]), aExternalScope);
178 GenericApplicationContext parentContext = new GenericApplicationContext();
180 registerRequiredServices(parentContext);
181 registerPropertyObjects(parentContext);
183 parentContext.refresh();
185 System.out.println("Parent context " + parentContext);
187 AbstractApplicationContext context = parseConfigFiles(parentContext);
189 context.addBeanFactoryPostProcessor(new PropertySetter(properties));
192 exposeProvidedServices(context, aExternalScope);
194 scope.put(CONTEXT_KEY, context);
197 } catch (Exception e) {
198 throw new SystemAssemblyException(
199 "Failed to assemble spring system " + getName(), e);
209 * @param aContext DOCUMENT ME!
210 * @param aScope DOCUMENT ME!
212 * @throws IllegalArgumentException DOCUMENT ME!
214 private void exposeProvidedServices(AbstractApplicationContext aContext,
216 // Call addService for each provided service.
217 for (String name : provided.keySet()) {
218 Object svc = aContext.getBean(name);
221 throw new IllegalArgumentException(getQualifiedName()
222 + ": service '" + name + "' is null");
225 addInterface(provided.get(name), svc, aScope);
226 System.out.println("addService " + provided.get(name) + " " + svc);
233 * @param aParentContext DOCUMENT ME!
235 * @return DOCUMENT ME!
237 private AbstractApplicationContext parseConfigFiles(
238 GenericApplicationContext aParentContext) {
239 // Parse spring config files
240 return new ClassPathXmlApplicationContext((String[]) configFiles,
241 false, aParentContext);
247 * @param aParentContext DOCUMENT ME!
249 private void registerRequiredServices(
250 GenericApplicationContext aParentContext) {
251 // Register required services in a parent context
252 for (RequiredInterface requiredIntf : getRequiredInterfaces()) {
253 String beanName = required.get(requiredIntf);
255 if ((beanName != null) && (beanName.length() > 0)) {
256 ConstructorArgumentValues cargs = new ConstructorArgumentValues();
257 cargs.addGenericArgumentValue(requiredIntf.getName());
259 BeanDefinition definition = new RootBeanDefinition(RequiredServiceBean.class,
260 cargs, new MutablePropertyValues());
261 aParentContext.registerBeanDefinition(beanName, definition);
263 // The required interface is not required by the spring config but by the sub-class directly.
271 * @param aParentContext DOCUMENT ME!
273 private void registerPropertyObjects(
274 GenericApplicationContext aParentContext) {
275 for (String beanName : propertyObjects.keySet()) {
276 ConstructorArgumentValues cargs = new ConstructorArgumentValues();
277 cargs.addGenericArgumentValue(PropertySetter.createPropertyFile(
278 propertyObjects.get(beanName)));
280 BeanDefinition definition = new RootBeanDefinition(ConfiguredProperties.class,
281 cargs, new MutablePropertyValues());
282 aParentContext.registerBeanDefinition(beanName, definition);
289 * @param aRuntime DOCUMENT ME!
292 protected void doStop(Scope aRuntime) {
293 AbstractApplicationContext context = (AbstractApplicationContext) aRuntime