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