(no commit message)
[utils] / system / spring / src / main / java / org / wamblee / system / spring / SpringComponent.java
1 package org.wamblee.system.spring;
2
3 import java.util.Map;
4 import java.util.Properties;
5
6 import org.springframework.beans.MutablePropertyValues;
7 import org.springframework.beans.factory.config.BeanDefinition;
8 import org.springframework.beans.factory.config.ConstructorArgumentValues;
9 import org.springframework.beans.factory.support.RootBeanDefinition;
10 import org.springframework.context.support.AbstractApplicationContext;
11 import org.springframework.context.support.ClassPathXmlApplicationContext;
12 import org.springframework.context.support.GenericApplicationContext;
13 import org.wamblee.system.AbstractComponent;
14 import org.wamblee.system.ProvidedInterface;
15 import org.wamblee.system.RequiredInterface;
16 import org.wamblee.system.SystemAssemblyException;
17
18 /**
19  * Represents a system configured based on spring. The spring config files that
20  * are configured should not contain any PropertyPlaceholderConfigurer objects.
21  * 
22  * @author Erik Brakkee
23  */
24 public class SpringComponent extends AbstractComponent {
25
26         static final ThreadLocal<SpringComponent> THIS = new ThreadLocal<SpringComponent>();
27
28         private Properties _properties;
29         private String[] _configFiles;
30         private Map<String, ProvidedInterface> _provided;
31         private Map<RequiredInterface, String> _required;
32         /**
33          * Parent application context containing required services.
34          */
35         private GenericApplicationContext _parentContext;
36
37         /**
38          * Application context containing parsed objects.
39          */
40         private AbstractApplicationContext _context;
41
42         /**
43          * Constructs a spring system.
44          * 
45          * @param aName
46          *            Name of the system.
47          * @param aConfigFil
48          *            Spring config files to read.
49          * @param aProvided
50          *            Map of bean name to service descriptor describing the bean
51          *            names that the spring config files use for each required
52          *            service.
53          * @param aRequired
54          *            Map of bean name to service descriptor describing the bean
55          *            names that the spring config files use for each required
56          *            service.
57          */
58         public SpringComponent(String aName, String[] aConfigFiles,
59                         Map<String, ProvidedInterface> aProvided,
60                         Map<RequiredInterface, String> aRequired) {
61                 super(aName, aProvided.values().toArray(new ProvidedInterface[0]),
62                                 aRequired.keySet().toArray(new RequiredInterface[0]));
63                 _properties = new Properties();
64                 _configFiles = aConfigFiles;
65                 _provided = aProvided;
66                 _required = aRequired;
67         }
68
69         /**
70          * Must be called to make a property available in the application context.
71          * 
72          * @param aKey
73          *            Property key.
74          * @param aValue
75          *            Property value.
76          */
77         public void setProperty(String aKey, String aValue) {
78                 _properties.put(aKey, aValue);
79         }
80
81         public void addProperties(Properties aProperties) {
82                 for (Object key : aProperties.keySet()) {
83                         setProperty((String) key, aProperties.getProperty((String) key));
84                 }
85         }
86
87         @Override
88         protected void doStart(String aContext) {
89
90                 SpringComponent old = THIS.get();
91                 THIS.set(this);
92                 try {
93                         _parentContext = new GenericApplicationContext();
94
95                         registerRequiredServices();
96
97                         _parentContext.refresh();
98
99                         parseConfigFiles();
100
101                         _context
102                                         .addBeanFactoryPostProcessor(new PropertySetter(_properties));
103                         _context.refresh();
104
105                         exposeProvidedServices(aContext);
106                 } catch (Exception e) {
107                         throw new SystemAssemblyException(
108                                         "Failed to assemble spring system", e);
109                 } finally {
110                         THIS.set(old);
111                 }
112         }
113
114         private void exposeProvidedServices(String aContext) {
115                 // Call addService for each provided service.
116
117                 for (String name : _provided.keySet()) {
118                         Object svc = _context.getBean(name);
119                         if (svc == null) {
120                                 throw new IllegalArgumentException(aContext + ": service '"
121                                                 + name + "' is null");
122                         }
123                         addService(aContext, _provided.get(name), svc);
124                 }
125         }
126
127         private void parseConfigFiles() {
128                 // Parse spring config files
129
130                 _context = new ClassPathXmlApplicationContext((String[]) _configFiles,
131                                 _parentContext);
132         }
133
134         private void registerRequiredServices() {
135                 // Register required services in a parent context
136                 for (RequiredInterface required: getRequiredServices()) { 
137                         String beanName = _required.get(required);
138                         ConstructorArgumentValues cargs = new ConstructorArgumentValues();
139                         cargs.addGenericArgumentValue(required.getName());
140                         BeanDefinition definition = new RootBeanDefinition(
141                                         RequiredServiceBean.class, cargs,
142                                         new MutablePropertyValues());
143                         _parentContext.registerBeanDefinition(beanName, definition);
144                 }
145         }
146
147         @Override
148         protected void doStop() {
149                 _context.close();
150         }
151 }