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