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