added spring subsystem implementation.
[utils] / system / spring / src / main / java / org / wamblee / system / spring / SpringSystem.java
1 package org.wamblee.system.spring;
2
3 import java.util.Map;
4
5 import org.springframework.beans.MutablePropertyValues;
6 import org.springframework.beans.factory.config.BeanDefinition;
7 import org.springframework.beans.factory.config.ConstructorArgumentValues;
8 import org.springframework.beans.factory.support.RootBeanDefinition;
9 import org.springframework.context.ApplicationContext;
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.AbstractSubSystem;
14 import org.wamblee.system.CompositeSystem;
15 import org.wamblee.system.Service;
16 import org.wamblee.system.ServiceDescriptor;
17 import org.wamblee.system.ServiceRegistry;
18 import org.wamblee.system.SystemAssemblyException;
19
20 /**
21  * Represents a system configured based on spring.
22  */
23 public class SpringSystem extends AbstractSubSystem {
24
25         /**
26          * Singleton access to the service registry. Required while starting up.
27          */
28         static ThreadLocal<ServiceRegistry> REGISTRY = new ThreadLocal<ServiceRegistry>();
29
30         private String[] _configFiles;
31         private Map<String, ServiceDescriptor> _provided;
32         private Map<ServiceDescriptor, String> _required;
33         /**
34          * Parent application context containing required services.
35          */
36         private GenericApplicationContext _parentContext;
37
38         /**
39          * Application context containing parsed objects.
40          */
41         private AbstractApplicationContext _context;
42
43         /**
44          * Construcst a spring system.
45          * 
46          * @param aName
47          *            Name of the system.
48          * @param aConfigFiles
49          *            Spring config files to read.
50          * @param aProvided
51          *            Map of bean name to service descriptor describing the bean
52          *            names that the spring config files use for each required
53          *            service.
54          * @param aRequired
55          *            Map of bean name to service descriptor describing the bean
56          *            names that the spring config files use for each required
57          *            service.
58          */
59         public SpringSystem(String aName, ServiceRegistry aRegistry, String[] aConfigFiles,
60                         Map<String, ServiceDescriptor> aProvided,
61                         Map<ServiceDescriptor, String> aRequired) {
62                 super(aName, aRegistry, aProvided.values().toArray(new ServiceDescriptor[0]),
63                                 aRequired.keySet().toArray(new ServiceDescriptor[0]));
64                 _configFiles = aConfigFiles;
65                 _provided = aProvided;
66                 _required = aRequired;
67         }
68
69         @Override
70         protected void doStart(String aContext, 
71                         Service[] aRequiredServices) {
72                 ServiceRegistry old = REGISTRY.get(); 
73                 try {   
74                         REGISTRY.set(getRegistry()); 
75                         try {
76
77                                 registerRequiredServices(aRequiredServices);
78
79                                 parseConfigFiles();
80
81                                 exposeProvidedServices(aContext);
82                         } catch (Exception e) {
83                                 throw new SystemAssemblyException(
84                                                 "Failed to assemble spring system", e);
85                         }
86                 } finally {
87                         REGISTRY.set(old);
88                 }
89         }
90
91         private void exposeProvidedServices(String aContext) {
92                 // Call addService for each provided service.
93
94                 for (String name : _provided.keySet()) {
95                         Object svc = _context.getBean(name);
96                         if (svc == null) {
97                                 throw new IllegalArgumentException(aContext + ": service '"
98                                                 + name + "' is null");
99                         }
100                         addService(aContext, _provided.get(name), svc);
101                 }
102         }
103
104         private void parseConfigFiles() {
105                 // Parse spring config files
106
107                 _context = new ClassPathXmlApplicationContext((String[]) _configFiles,
108                                 _parentContext);
109         }
110
111         private void registerRequiredServices(Service[] aRequiredServices) {
112                 // Register required services in a parent context
113
114                 // Register the Hibernate mapping files as a bean.
115                 _parentContext = new GenericApplicationContext();
116                 
117                 for (Service svc: aRequiredServices) { 
118                         String id = svc.getId();
119                         ServiceDescriptor descriptor = svc.getDescriptor();
120                         String beanName = _required.get(descriptor);
121                         ConstructorArgumentValues cargs = new ConstructorArgumentValues();
122                         cargs.addGenericArgumentValue(id); 
123                         BeanDefinition definition = new RootBeanDefinition(ProvidedServiceBean.class, cargs,
124                                         new MutablePropertyValues());
125                         _parentContext.registerBeanDefinition(beanName, definition);
126                 }
127                 _parentContext.refresh();
128         }
129
130         @Override
131         protected void doStop() {
132             _context.close();
133         }
134 }