ad6c4b0fd2362b79cd47d2b00c3e6b2ebd80b651
[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 /**
40  * Represents a system configured based on spring. The spring config files
41  * that are configured should not contain any PropertyPlaceholderConfigurer
42  * objects.
43  *
44  * @author Erik Brakkee
45  */
46 public class SpringComponent extends AbstractComponent<Scope> {
47     /**
48      * DOCUMENT ME!
49      */
50     private static final String CONTEXT_KEY = "context";
51
52     /**
53      * DOCUMENT ME!
54      */
55     static final ThreadLocal<SpringComponent> THIS = new ThreadLocal<SpringComponent>();
56
57     /**
58      * DOCUMENT ME!
59      */
60     static final ThreadLocal<Scope> SCOPE = new ThreadLocal<Scope>();
61
62     /**
63      * DOCUMENT ME!
64      */
65     private Properties properties;
66
67     /**
68      * DOCUMENT ME!
69      */
70     private String[] configFiles;
71
72     /**
73      * DOCUMENT ME!
74      */
75     private Map<String, ProvidedInterface> provided;
76
77     /**
78      * DOCUMENT ME!
79      */
80     private Map<RequiredInterface, String> required;
81
82     /**
83      * DOCUMENT ME!
84      */
85     private Map<String, Properties> propertyObjects;
86
87 /**
88          * Constructs a spring system.
89          * 
90          * @param aName
91          *            Name of the system.
92          * @param aConfigFil
93          *            Spring config files to read.
94          * @param aProvided
95          *            Map of bean name to service descriptor describing the bean
96          *            names that the spring config files use for each required
97          *            service.
98          * @param aRequired
99          *            Map of bean name to service descriptor describing the bean
100          *            names that the spring config files use for each required
101          *            service.
102          */
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>();
113     }
114
115     /**
116      * Must be called to make a property available in the application
117      * context.
118      *
119      * @param aKey Property key.
120      * @param aValue Property value.
121      */
122     public void setProperty(String aKey, String aValue) {
123         properties.put(aKey, aValue);
124     }
125
126     /**
127      * DOCUMENT ME!
128      *
129      * @param aProperties DOCUMENT ME!
130      */
131     public void addProperties(Properties aProperties) {
132         for (Object key : aProperties.keySet()) {
133             setProperty((String) key, aProperties.getProperty((String) key));
134         }
135     }
136
137     /**
138      * DOCUMENT ME!
139      *
140      * @param aBeanname DOCUMENT ME!
141      * @param aProperties DOCUMENT ME!
142      */
143     public void addProperties(String aBeanname, Properties aProperties) {
144         propertyObjects.put(aBeanname, aProperties);
145     }
146
147     /**
148      * DOCUMENT ME!
149      *
150      * @param aBeanname DOCUMENT ME!
151      *
152      * @return DOCUMENT ME!
153      */
154     public Properties getProperties(String aBeanname) {
155         return propertyObjects.get(aBeanname);
156     }
157
158     /**
159      * DOCUMENT ME!
160      *
161      * @param aExternalScope DOCUMENT ME!
162      *
163      * @return DOCUMENT ME!
164      *
165      * @throws SystemAssemblyException DOCUMENT ME!
166      */
167     @Override
168     protected Scope doStart(Scope aExternalScope) {
169         SpringComponent old      = THIS.get();
170         Scope           oldScope = SCOPE.get();
171         THIS.set(this);
172
173         Scope scope = new DefaultScope(getProvidedInterfaces()
174                 .toArray(new ProvidedInterface[0]), aExternalScope);
175         SCOPE.set(scope);
176
177         try {
178             GenericApplicationContext parentContext = new GenericApplicationContext();
179
180             registerRequiredServices(parentContext);
181             registerPropertyObjects(parentContext);
182
183             parentContext.refresh();
184
185             System.out.println("Parent context " + parentContext);
186
187             AbstractApplicationContext context = parseConfigFiles(parentContext);
188
189             context.addBeanFactoryPostProcessor(new PropertySetter(properties));
190             context.refresh();
191
192             exposeProvidedServices(context, aExternalScope);
193
194             scope.put(CONTEXT_KEY, context);
195
196             return scope;
197         } catch (Exception e) {
198             throw new SystemAssemblyException(
199                 "Failed to assemble spring system " + getName(), e);
200         } finally {
201             THIS.set(old);
202             SCOPE.set(oldScope);
203         }
204     }
205
206     /**
207      * DOCUMENT ME!
208      *
209      * @param aContext DOCUMENT ME!
210      * @param aScope DOCUMENT ME!
211      *
212      * @throws IllegalArgumentException DOCUMENT ME!
213      */
214     private void exposeProvidedServices(AbstractApplicationContext aContext,
215         Scope aScope) {
216         // Call addService for each provided service.
217         for (String name : provided.keySet()) {
218             Object svc = aContext.getBean(name);
219
220             if (svc == null) {
221                 throw new IllegalArgumentException(getQualifiedName()
222                     + ": service '" + name + "' is null");
223             }
224
225             addInterface(provided.get(name), svc, aScope);
226             System.out.println("addService " + provided.get(name) + " " + svc);
227         }
228     }
229
230     /**
231      * DOCUMENT ME!
232      *
233      * @param aParentContext DOCUMENT ME!
234      *
235      * @return DOCUMENT ME!
236      */
237     private AbstractApplicationContext parseConfigFiles(
238         GenericApplicationContext aParentContext) {
239         // Parse spring config files
240         return new ClassPathXmlApplicationContext((String[]) configFiles,
241             false, aParentContext);
242     }
243
244     /**
245      * DOCUMENT ME!
246      *
247      * @param aParentContext DOCUMENT ME!
248      */
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);
254
255             if ((beanName != null) && (beanName.length() > 0)) {
256                 ConstructorArgumentValues cargs = new ConstructorArgumentValues();
257                 cargs.addGenericArgumentValue(requiredIntf.getName());
258
259                 BeanDefinition definition = new RootBeanDefinition(RequiredServiceBean.class,
260                         cargs, new MutablePropertyValues());
261                 aParentContext.registerBeanDefinition(beanName, definition);
262             } else {
263                 // The required interface is not required by the spring config but by the sub-class directly.
264             }
265         }
266     }
267
268     /**
269      * DOCUMENT ME!
270      *
271      * @param aParentContext DOCUMENT ME!
272      */
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)));
279
280             BeanDefinition definition = new RootBeanDefinition(ConfiguredProperties.class,
281                     cargs, new MutablePropertyValues());
282             aParentContext.registerBeanDefinition(beanName, definition);
283         }
284     }
285
286     /**
287      * DOCUMENT ME!
288      *
289      * @param aRuntime DOCUMENT ME!
290      */
291     @Override
292     protected void doStop(Scope aRuntime) {
293         AbstractApplicationContext context = (AbstractApplicationContext) aRuntime
294             .get(CONTEXT_KEY);
295         context.close();
296     }
297 }