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