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