Removed DOCUMENT ME comments that were generated and applied source code
[utils] / system / spring / src / main / java / org / wamblee / system / spring / SpringComponent.java
1 /*
2  * Copyright 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 package org.wamblee.system.spring;
17
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;
22
23 import org.springframework.context.support.AbstractApplicationContext;
24 import org.springframework.context.support.ClassPathXmlApplicationContext;
25 import org.springframework.context.support.GenericApplicationContext;
26
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;
33
34 import java.util.HashMap;
35 import java.util.Map;
36 import java.util.Properties;
37
38 /**
39  * Represents a system configured based on spring. The spring config files that
40  * are configured should not contain any PropertyPlaceholderConfigurer objects.
41  * 
42  * @author Erik Brakkee
43  */
44 public class SpringComponent extends AbstractComponent<Scope> {
45     private static final String CONTEXT_KEY = "context";
46
47     static final ThreadLocal<SpringComponent> THIS = new ThreadLocal<SpringComponent>();
48
49     static final ThreadLocal<Scope> SCOPE = new ThreadLocal<Scope>();
50
51     private Properties properties;
52
53     private String[] configFiles;
54
55     private Map<String, ProvidedInterface> provided;
56
57     private Map<RequiredInterface, String> required;
58
59     private Map<String, Properties> propertyObjects;
60
61     /**
62      * Constructs a spring system.
63      * 
64      * @param aName
65      *            Name of the system.
66      * @param aConfigFil
67      *            Spring config files to read.
68      * @param aProvided
69      *            Map of bean name to service descriptor describing the bean
70      *            names that the spring config files use for each required
71      *            service.
72      * @param aRequired
73      *            Map of bean name to service descriptor describing the bean
74      *            names that the spring config files use for each required
75      *            service.
76      */
77     public SpringComponent(String aName, String[] aConfigFiles,
78         Map<String, ProvidedInterface> aProvided,
79         Map<RequiredInterface, String> aRequired) {
80         super(aName, aProvided.values().toArray(new ProvidedInterface[0]),
81             aRequired.keySet().toArray(new RequiredInterface[0]));
82         properties = new Properties();
83         configFiles = aConfigFiles;
84         provided = aProvided;
85         required = aRequired;
86         propertyObjects = new HashMap<String, Properties>();
87     }
88
89     /**
90      * Must be called to make a property available in the application context.
91      * 
92      * @param aKey
93      *            Property key.
94      * @param aValue
95      *            Property value.
96      */
97     public void setProperty(String aKey, String aValue) {
98         properties.put(aKey, aValue);
99     }
100
101     public void addProperties(Properties aProperties) {
102         for (Object key : aProperties.keySet()) {
103             setProperty((String) key, aProperties.getProperty((String) key));
104         }
105     }
106
107     public void addProperties(String aBeanname, Properties aProperties) {
108         propertyObjects.put(aBeanname, aProperties);
109     }
110
111     public Properties getProperties(String aBeanname) {
112         return propertyObjects.get(aBeanname);
113     }
114
115     @Override
116     protected Scope doStart(Scope aExternalScope) {
117         SpringComponent old = THIS.get();
118         Scope oldScope = SCOPE.get();
119         THIS.set(this);
120
121         Scope scope = new DefaultScope(getProvidedInterfaces().toArray(
122             new ProvidedInterface[0]), aExternalScope);
123         SCOPE.set(scope);
124
125         try {
126             GenericApplicationContext parentContext = new GenericApplicationContext();
127
128             registerRequiredServices(parentContext);
129             registerPropertyObjects(parentContext);
130
131             parentContext.refresh();
132
133             System.out.println("Parent context " + parentContext);
134
135             AbstractApplicationContext context = parseConfigFiles(parentContext);
136
137             context.addBeanFactoryPostProcessor(new PropertySetter(properties));
138             context.refresh();
139
140             exposeProvidedServices(context, aExternalScope);
141
142             scope.put(CONTEXT_KEY, context);
143
144             return scope;
145         } catch (Exception e) {
146             throw new SystemAssemblyException(
147                 "Failed to assemble spring system " + getName(), e);
148         } finally {
149             THIS.set(old);
150             SCOPE.set(oldScope);
151         }
152     }
153
154     private void exposeProvidedServices(AbstractApplicationContext aContext,
155         Scope aScope) {
156         // Call addService for each provided service.
157         for (String name : provided.keySet()) {
158             Object svc = aContext.getBean(name);
159
160             if (svc == null) {
161                 throw new IllegalArgumentException(getQualifiedName() +
162                     ": service '" + name + "' is null");
163             }
164
165             addInterface(provided.get(name), svc, aScope);
166             System.out.println("addService " + provided.get(name) + " " + svc);
167         }
168     }
169
170     private AbstractApplicationContext parseConfigFiles(
171         GenericApplicationContext aParentContext) {
172         // Parse spring config files
173         return new ClassPathXmlApplicationContext((String[]) configFiles,
174             false, aParentContext);
175     }
176
177     private void registerRequiredServices(
178         GenericApplicationContext aParentContext) {
179         // Register required services in a parent context
180         for (RequiredInterface requiredIntf : getRequiredInterfaces()) {
181             String beanName = required.get(requiredIntf);
182
183             if ((beanName != null) && (beanName.length() > 0)) {
184                 ConstructorArgumentValues cargs = new ConstructorArgumentValues();
185                 cargs.addGenericArgumentValue(requiredIntf.getName());
186
187                 BeanDefinition definition = new RootBeanDefinition(
188                     RequiredServiceBean.class, cargs,
189                     new MutablePropertyValues());
190                 aParentContext.registerBeanDefinition(beanName, definition);
191             } else {
192                 // The required interface is not required by the spring config
193                 // but by the sub-class directly.
194             }
195         }
196     }
197
198     private void registerPropertyObjects(
199         GenericApplicationContext aParentContext) {
200         for (String beanName : propertyObjects.keySet()) {
201             ConstructorArgumentValues cargs = new ConstructorArgumentValues();
202             cargs.addGenericArgumentValue(PropertySetter
203                 .createPropertyFile(propertyObjects.get(beanName)));
204
205             BeanDefinition definition = new RootBeanDefinition(
206                 ConfiguredProperties.class, cargs, new MutablePropertyValues());
207             aParentContext.registerBeanDefinition(beanName, definition);
208         }
209     }
210
211     @Override
212     protected void doStop(Scope aRuntime) {
213         AbstractApplicationContext context = (AbstractApplicationContext) aRuntime
214             .get(CONTEXT_KEY);
215         context.close();
216     }
217 }