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