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 java.util.HashMap;
20 import java.util.Properties;
22 import org.springframework.beans.MutablePropertyValues;
23 import org.springframework.beans.factory.config.BeanDefinition;
24 import org.springframework.beans.factory.config.ConstructorArgumentValues;
25 import org.springframework.beans.factory.support.RootBeanDefinition;
26 import org.springframework.context.support.AbstractApplicationContext;
27 import org.springframework.context.support.ClassPathXmlApplicationContext;
28 import org.springframework.context.support.GenericApplicationContext;
29 import org.wamblee.system.core.AbstractComponent;
30 import org.wamblee.system.core.DefaultScope;
31 import org.wamblee.system.core.ProvidedInterface;
32 import org.wamblee.system.core.RequiredInterface;
33 import org.wamblee.system.core.Scope;
34 import org.wamblee.system.core.SystemAssemblyException;
37 * Represents a system configured based on spring. The spring config files that
38 * are configured should not contain any PropertyPlaceholderConfigurer objects.
40 * @author Erik Brakkee
42 public class SpringComponent extends AbstractComponent<Scope> {
44 private static final String CONTEXT_KEY = "context";
46 static final ThreadLocal<SpringComponent> THIS = new ThreadLocal<SpringComponent>();
47 static final ThreadLocal<Scope> SCOPE = new ThreadLocal<Scope>();
49 private Properties _properties;
50 private String[] _configFiles;
51 private Map<String, ProvidedInterface> _provided;
52 private Map<RequiredInterface, String> _required;
53 private Map<String, Properties> _propertyObjects;
56 * Constructs a spring system.
61 * Spring config files to read.
63 * Map of bean name to service descriptor describing the bean
64 * names that the spring config files use for each required
67 * Map of bean name to service descriptor describing the bean
68 * names that the spring config files use for each required
71 public SpringComponent(String aName, String[] aConfigFiles,
72 Map<String, ProvidedInterface> aProvided,
73 Map<RequiredInterface, String> aRequired) {
74 super(aName, aProvided.values().toArray(new ProvidedInterface[0]),
75 aRequired.keySet().toArray(new RequiredInterface[0]));
76 _properties = new Properties();
77 _configFiles = aConfigFiles;
78 _provided = aProvided;
79 _required = aRequired;
80 _propertyObjects = new HashMap<String, Properties>();
85 * Must be called to make a property available in the application context.
92 public void setProperty(String aKey, String aValue) {
93 _properties.put(aKey, aValue);
96 public void addProperties(Properties aProperties) {
97 for (Object key : aProperties.keySet()) {
98 setProperty((String) key, aProperties.getProperty((String) key));
102 public void addProperties(String aBeanname, Properties aProperties) {
103 _propertyObjects.put(aBeanname, aProperties);
106 public Properties getProperties(String aBeanname) {
107 return _propertyObjects.get(aBeanname);
111 protected Scope doStart(Scope aExternalScope) {
113 SpringComponent old = THIS.get();
114 Scope oldScope = SCOPE.get();
116 Scope scope = new DefaultScope(getProvidedInterfaces().toArray(new ProvidedInterface[0]), aExternalScope);
119 GenericApplicationContext parentContext = new GenericApplicationContext();
121 registerRequiredServices(parentContext);
122 registerPropertyObjects(parentContext);
124 parentContext.refresh();
126 System.out.println("Parent context " + parentContext);
128 AbstractApplicationContext context = parseConfigFiles(parentContext);
131 .addBeanFactoryPostProcessor(new PropertySetter(_properties));
134 exposeProvidedServices(context, aExternalScope);
136 scope.put(CONTEXT_KEY, context);
138 } catch (Exception e) {
139 throw new SystemAssemblyException(
140 "Failed to assemble spring system " + getName(), e);
147 private void exposeProvidedServices(AbstractApplicationContext aContext, Scope aScope) {
148 // Call addService for each provided service.
150 for (String name : _provided.keySet()) {
151 Object svc = aContext.getBean(name);
153 throw new IllegalArgumentException(getQualifiedName() + ": service '"
154 + name + "' is null");
156 addInterface(_provided.get(name), svc, aScope);
157 System.out.println("addService " + _provided.get(name) + " " + svc);
161 private AbstractApplicationContext parseConfigFiles(GenericApplicationContext aParentContext) {
162 // Parse spring config files
164 return new ClassPathXmlApplicationContext((String[]) _configFiles,
165 false, aParentContext);
168 private void registerRequiredServices(GenericApplicationContext aParentContext) {
169 // Register required services in a parent context
170 for (RequiredInterface required: getRequiredInterfaces()) {
171 String beanName = _required.get(required);
172 if ( beanName != null && beanName.length() > 0) {
173 ConstructorArgumentValues cargs = new ConstructorArgumentValues();
174 cargs.addGenericArgumentValue(required.getName());
175 BeanDefinition definition = new RootBeanDefinition(
176 RequiredServiceBean.class, cargs,
177 new MutablePropertyValues());
178 aParentContext.registerBeanDefinition(beanName, definition);
180 // The required interface is not required by the spring config but by the sub-class directly.
185 private void registerPropertyObjects(GenericApplicationContext aParentContext) {
186 for (String beanName: _propertyObjects.keySet()) {
187 ConstructorArgumentValues cargs = new ConstructorArgumentValues();
188 cargs.addGenericArgumentValue(PropertySetter.createPropertyFile(_propertyObjects.get(beanName)));
189 BeanDefinition definition = new RootBeanDefinition(
190 ConfiguredProperties.class, cargs,
191 new MutablePropertyValues());
192 aParentContext.registerBeanDefinition(beanName, definition);
198 protected void doStop(Scope aRuntime) {
199 AbstractApplicationContext context = (AbstractApplicationContext)aRuntime.get(CONTEXT_KEY);