1da30a324ed53c4c9812306669e2adc287b5b6a8
[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 java.util.Map;
19 import java.util.Properties;
20
21 import org.springframework.beans.MutablePropertyValues;
22 import org.springframework.beans.factory.config.BeanDefinition;
23 import org.springframework.beans.factory.config.ConstructorArgumentValues;
24 import org.springframework.beans.factory.support.RootBeanDefinition;
25 import org.springframework.context.support.AbstractApplicationContext;
26 import org.springframework.context.support.ClassPathXmlApplicationContext;
27 import org.springframework.context.support.GenericApplicationContext;
28 import org.wamblee.system.core.AbstractComponent;
29 import org.wamblee.system.core.DefaultScope;
30 import org.wamblee.system.core.ProvidedInterface;
31 import org.wamblee.system.core.RequiredInterface;
32 import org.wamblee.system.core.Scope;
33 import org.wamblee.system.core.SystemAssemblyException;
34
35 /**
36  * Represents a system configured based on spring. The spring config files that
37  * are configured should not contain any PropertyPlaceholderConfigurer objects.
38  * 
39  * @author Erik Brakkee
40  */
41 public class SpringComponent extends AbstractComponent<Scope> {
42
43         private static final String CONTEXT_KEY = "context";
44
45         static final ThreadLocal<SpringComponent> THIS = new ThreadLocal<SpringComponent>();
46         static final ThreadLocal<Scope> SCOPE = new ThreadLocal<Scope>();
47
48         private Properties _properties;
49         private String[] _configFiles;
50         private Map<String, ProvidedInterface> _provided;
51         private Map<RequiredInterface, String> _required;
52
53         /**
54          * Constructs a spring system.
55          * 
56          * @param aName
57          *            Name of the system.
58          * @param aConfigFil
59          *            Spring config files to read.
60          * @param aProvided
61          *            Map of bean name to service descriptor describing the bean
62          *            names that the spring config files use for each required
63          *            service.
64          * @param aRequired
65          *            Map of bean name to service descriptor describing the bean
66          *            names that the spring config files use for each required
67          *            service.
68          */
69         public SpringComponent(String aName, String[] aConfigFiles,
70                         Map<String, ProvidedInterface> aProvided,
71                         Map<RequiredInterface, String> aRequired) {
72                 super(aName, aProvided.values().toArray(new ProvidedInterface[0]),
73                                 aRequired.keySet().toArray(new RequiredInterface[0]));
74                 _properties = new Properties();
75                 _configFiles = aConfigFiles;
76                 _provided = aProvided;
77                 _required = aRequired;
78         }
79
80         /**
81          * Must be called to make a property available in the application context.
82          * 
83          * @param aKey
84          *            Property key.
85          * @param aValue
86          *            Property value.
87          */
88         public void setProperty(String aKey, String aValue) {
89                 _properties.put(aKey, aValue);
90         }
91
92         public void addProperties(Properties aProperties) {
93                 for (Object key : aProperties.keySet()) {
94                         setProperty((String) key, aProperties.getProperty((String) key));
95                 }
96         }
97
98         @Override
99         protected Scope doStart(Scope aExternalScope) {
100
101                 SpringComponent old = THIS.get();
102                 Scope oldScope = SCOPE.get();
103                 THIS.set(this);
104                 Scope scope = new DefaultScope(getProvidedInterfaces(), aExternalScope);
105                 SCOPE.set(scope);
106                 try {
107                         GenericApplicationContext parentContext = new GenericApplicationContext();
108
109                         registerRequiredServices(parentContext);
110
111                         parentContext.refresh();
112                         
113                         System.out.println("Parent context " + parentContext);
114                     
115                         AbstractApplicationContext context = parseConfigFiles(parentContext);
116
117                         context
118                                         .addBeanFactoryPostProcessor(new PropertySetter(_properties));
119                         context.refresh();
120
121                         exposeProvidedServices(context, aExternalScope);
122                         
123                         scope.put(CONTEXT_KEY, context);
124                         return scope; 
125                 } catch (Exception e) {
126                         throw new SystemAssemblyException(
127                                         "Failed to assemble spring system " + getName(), e);
128                 } finally {
129                         THIS.set(old);
130                         SCOPE.set(oldScope);
131                 }
132         }
133
134         private void exposeProvidedServices(AbstractApplicationContext aContext, Scope aScope) {
135                 // Call addService for each provided service.
136
137                 for (String name : _provided.keySet()) {
138                         Object svc = aContext.getBean(name);
139                         if (svc == null) {
140                                 throw new IllegalArgumentException(getQualifiedName() + ": service '"
141                                                 + name + "' is null");
142                         }
143                         addInterface(_provided.get(name), svc, aScope);
144                         System.out.println("addService " + _provided.get(name) + " " + svc);
145                 }
146         }
147
148         private AbstractApplicationContext parseConfigFiles(GenericApplicationContext aParentContext) {
149                 // Parse spring config files
150
151                 return new ClassPathXmlApplicationContext((String[]) _configFiles,
152                                 false, aParentContext);
153         }
154
155         private void registerRequiredServices(GenericApplicationContext aParentContext) {
156                 // Register required services in a parent context
157                 for (RequiredInterface required: getRequiredInterfaces()) { 
158                         String beanName = _required.get(required);
159                         ConstructorArgumentValues cargs = new ConstructorArgumentValues();
160                         cargs.addGenericArgumentValue(required.getName());
161                         BeanDefinition definition = new RootBeanDefinition(
162                                         RequiredServiceBean.class, cargs,
163                                         new MutablePropertyValues());
164                         aParentContext.registerBeanDefinition(beanName, definition);
165                 }
166         }
167
168         @Override
169         protected void doStop(Scope aRuntime) {
170                 AbstractApplicationContext context = (AbstractApplicationContext)aRuntime.get(CONTEXT_KEY);
171                 context.close();
172         }
173 }