80bab167fa1de1bf7c7c071d14f6ad60e93b627a
[utils] / system / general / src / main / java / org / wamblee / system / SystemAssembler.java
1 package org.wamblee.system;
2
3 import java.util.ArrayList;
4 import java.util.Arrays;
5 import java.util.Collection;
6 import java.util.HashMap;
7 import java.util.List;
8 import java.util.Map;
9
10 import org.apache.commons.logging.Log;
11 import org.apache.commons.logging.LogFactory;
12
13 /**
14  * Assembler to control multiple subsystems. It makes sure that all dependencies
15  * are met and controls the order in which systems are initialized.
16  */
17 public class SystemAssembler {
18
19         private static final Log LOG = LogFactory.getLog(SystemAssembler.class);
20
21         private static final String ROOT_CONTEXT_NAME = "root";
22         private String _context;
23         private SubSystem[] _systems;
24
25         public static RequiredServiceDescriptor[] filterRequiredServices(
26                         ProvidedServiceDescriptor aProvided,
27                         Collection<RequiredServiceDescriptor> aDescriptors) {
28                 List<RequiredServiceDescriptor> required = new ArrayList<RequiredServiceDescriptor>();
29                 for (RequiredServiceDescriptor descriptor : aDescriptors) {
30                         if (descriptor.implementedBy(aProvided)) {
31                                 required.add(descriptor);
32                         }
33                 }
34                 return required.toArray(new RequiredServiceDescriptor[0]);
35         }
36
37         public static ProvidedServiceDescriptor[] filterProvidedServices(
38                         RequiredServiceDescriptor aRequired,
39                         Collection<ProvidedServiceDescriptor> aProvided) {
40                 List<ProvidedServiceDescriptor> provided = new ArrayList<ProvidedServiceDescriptor>();
41                 for (ProvidedServiceDescriptor descriptor : aProvided) {
42                         if (aRequired.implementedBy(descriptor)) {
43                                 provided.add(descriptor);
44                         }
45                 }
46                 return provided.toArray(new ProvidedServiceDescriptor[0]);
47         }
48
49         /**
50          * Constructs the assembler.
51          * 
52          * @param aSystems
53          *            Systems that must be assembled.
54          * @param aAvailableServices
55          *            Available services from other systems outside of the systems
56          *            that this assembler manages.
57          */
58         public SystemAssembler(SubSystem[] aSystems,
59                         ProvidedServiceDescriptor[] aAvailableServices) {
60                 this(ROOT_CONTEXT_NAME, aSystems, aAvailableServices);
61         }
62
63         /**
64          * Constructs the assembler.
65          * 
66          * @param aContext
67          *            Context (unique name) of the assembler.
68          * @param aSystems
69          *            Systems that must be assembled.
70          * @param aAvailableServices
71          *            Available services from other systems outside of the systems
72          *            that this assembler manages.
73          */
74         public SystemAssembler(String aContext, SubSystem[] aSystems,
75                         ProvidedServiceDescriptor[] aAvailableServices) {
76                 _context = aContext;
77                 _systems = aSystems;
78                 validate(aAvailableServices);
79         }
80
81         /**
82          * Determines if the systems are ordered appropriately so that all
83          * dependencies are met.
84          */
85         private void validate(ProvidedServiceDescriptor[] aDescriptors)
86                         throws SystemAssemblyException {
87
88                 List<ProvidedServiceDescriptor> allProvided = new ArrayList<ProvidedServiceDescriptor>();
89                 for (ProvidedServiceDescriptor descriptor : aDescriptors) {
90                         allProvided.add(descriptor);
91                 }
92                 for (SubSystem system : _systems) {
93                         // Check if all required services are already provided by earlier
94                         // systems.
95                         RequiredServiceDescriptor[] required = system.getRequiredServices();
96
97                         for (RequiredServiceDescriptor descriptor : required) {
98                                 ProvidedServiceDescriptor[] filtered = filterProvidedServices(
99                                                 descriptor, allProvided);
100
101                                 if (filtered.length == 0) {
102                                         throw new SystemAssemblyException(
103                                                         "Service '"
104                                                                         + descriptor
105                                                                         + "' required by system '"
106                                                                         + system
107                                                                         + "' is not provided by systems that are started earlier");
108                                 }
109                                 if (filtered.length > 1) {
110                                         throw new SystemAssemblyException(
111                                                         "Service '"
112                                                                         + descriptor
113                                                                         + "' required by system '"
114                                                                         + system
115                                                                         + "' matches multiple services provided by other systems: " + 
116                                                                         Arrays.asList(filtered));
117                                 }
118                         }
119
120                         // add all provided services
121                         ProvidedServiceDescriptor[] provided = system.getProvidedServices();
122                         allProvided.addAll(Arrays.asList(provided));
123                 }
124         }
125
126         /**
127          * Starts the subsystems.
128          * 
129          * @param aRegistry
130          *            Service registry to which created services must be registered.
131          * @param aRequiredServices
132          *            Services that are available from other systems that have been
133          *            started before.
134          */
135         public void start(ServiceRegistry aRegistry, Service[] aRequiredServices) {
136                 LOG.info("Starting '" + _context + "'");
137                 Map<ProvidedServiceDescriptor, Service> allProvided = new HashMap<ProvidedServiceDescriptor, Service>();
138
139                 for (Service service : aRequiredServices) {
140                         allProvided.put(service.getDescriptor(), service);
141                 }
142                 for (SubSystem system : _systems) {
143                         
144                         // Compose a list of the required services required for the subsystem.
145                         
146                         RequiredServiceDescriptor[] descriptors = system
147                                         .getRequiredServices();
148                         List<Service> services = new ArrayList<Service>();
149                         for (RequiredServiceDescriptor descriptor : descriptors) {
150                                 ProvidedServiceDescriptor[] provided = filterProvidedServices(
151                                                 descriptor, allProvided.keySet());
152                                 services.add(allProvided.get(provided[0]));
153                         }
154                         
155                         // Start the service. 
156                         Service[] provided = system.start(_context, services
157                                         .toArray(new Service[0]));
158                         
159                         // Add started services to the map of started services.
160                         for (Service service : provided) {
161                                 allProvided.put(service.getDescriptor(), service);
162                         }
163
164                 }
165         }
166
167 }