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