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