(no commit message)
[utils] / system / general / src / main / java / org / wamblee / system / SystemAssembler.java
1 /*
2  * Copyright 2007 the original author or authors.
3  * 
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  * 
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  * 
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */ 
16 package org.wamblee.system;
17
18 import java.util.ArrayList;
19 import java.util.Arrays;
20 import java.util.Collection;
21 import java.util.HashMap;
22 import java.util.HashSet;
23 import java.util.List;
24 import java.util.Map;
25 import java.util.Set;
26
27 import org.apache.commons.logging.Log;
28 import org.apache.commons.logging.LogFactory;
29
30 /**
31  * Assembler to control multiple subsystems. It makes sure that all dependencies
32  * are met and controls the order in which systems are initialized.
33  *
34  * @author Erik Brakkee
35  */
36 public class SystemAssembler {
37
38         private static final Log LOG = LogFactory.getLog(SystemAssembler.class);
39
40         private static final String ROOT_CONTEXT_NAME = "root";
41         private String _context;
42         private Component[] _systems;
43         private ProvidedInterface[] _required; 
44
45         public static RequiredInterface[] filterRequiredServices(
46                         ProvidedInterface aProvided,
47                         Collection<RequiredInterface> aDescriptors) {
48                 List<RequiredInterface> required = new ArrayList<RequiredInterface>();
49                 for (RequiredInterface descriptor : aDescriptors) {
50                         if (descriptor.implementedBy(aProvided)) {
51                                 required.add(descriptor);
52                         }
53                 }
54                 return required.toArray(new RequiredInterface[0]);
55         }
56
57         public static ProvidedInterface[] filterProvidedServices(
58                         RequiredInterface aRequired,
59                         Collection<ProvidedInterface> aProvided) {
60                 List<ProvidedInterface> provided = new ArrayList<ProvidedInterface>();
61                 for (ProvidedInterface descriptor : aProvided) {
62                         if (aRequired.implementedBy(descriptor)) {
63                                 provided.add(descriptor);
64                         }
65                 }
66                 return provided.toArray(new ProvidedInterface[0]);
67         }
68
69         /**
70          * Constructs the assembler.
71          * 
72          * @param aSystems
73          *            Systems that must be assembled.
74          * @param aAvailableServices
75          *            Available services from other systems outside of the systems
76          *            that this assembler manages.
77          */
78         public SystemAssembler(Component[] aSystems,
79                         ProvidedInterface[] aAvailableServices) {
80                 this(ROOT_CONTEXT_NAME, aSystems, aAvailableServices);
81         }
82
83         /**
84          * Constructs the assembler.
85          * 
86          * @param aContext
87          *            Context (unique name) of the assembler.
88          * @param aSystems
89          *            Systems that must be assembled.
90          * @param aAvailableServices
91          *            Available services from other systems outside of the systems
92          *            that this assembler manages.
93          */
94         public SystemAssembler(String aContext, Component[] aSystems,
95                         ProvidedInterface[] aAvailableServices) {
96                 _context = aContext;
97                 _systems = aSystems;
98                 _required = aAvailableServices; 
99                 validate();
100         }
101
102         /**
103          * Determines if the systems are ordered appropriately so that all
104          * dependencies are met.
105          */
106         private void validate()
107                         throws SystemAssemblyException {
108
109                 List<ProvidedInterface> allProvided = new ArrayList<ProvidedInterface>();
110                 for (ProvidedInterface descriptor : _required) {
111                         allProvided.add(descriptor);
112                 }
113                 for (Component system : _systems) {
114                         // Check if all required services are already provided by earlier
115                         // systems.
116                         RequiredInterface[] required = system.getRequiredServices();
117
118                         for (RequiredInterface descriptor : required) {
119                                 ProvidedInterface[] filtered = filterProvidedServices(
120                                                 descriptor, allProvided);
121
122                                 if (filtered.length == 0) {
123                                         throw new SystemAssemblyException(
124                                                         "Service '"
125                                                                         + descriptor
126                                                                         + "' required by system '"
127                                                                         + system
128                                                                         + "' is not provided by systems that are started earlier");
129                                 }
130                                 if (filtered.length > 1) {
131                                         throw new SystemAssemblyException(
132                                                         "Service '"
133                                                                         + descriptor
134                                                                         + "' required by system '"
135                                                                         + system
136                                                                         + "' matches multiple services provided by other systems: " + 
137                                                                         Arrays.asList(filtered));
138                                 }
139                         }
140
141                         // add all provided services
142                         ProvidedInterface[] provided = system.getProvidedServices();
143                         allProvided.addAll(Arrays.asList(provided));
144                 }
145         }
146
147         /**
148          * Starts the subsystems.
149          * 
150          * @param aRequiredServices
151          *            Services that are available from other systems that have been
152          *            started before.
153          */
154         public void start() {
155                 LOG.info("Starting '" + _context + "'");
156                 Set<ProvidedInterface> allProvided = new HashSet<ProvidedInterface>();
157                 allProvided.addAll(Arrays.asList(_required));
158                 
159                 for (Component system : _systems) {
160                         
161                         // Compose a list of the required services required for the subsystem.
162                         
163                         RequiredInterface[] descriptors = system
164                                         .getRequiredServices();
165                         List<ProvidedInterface> services = new ArrayList<ProvidedInterface>();
166                         for (RequiredInterface required : descriptors) {
167                                 ProvidedInterface[] provided = filterProvidedServices(
168                                                 required, allProvided);
169                                 assert provided.length == 1;    
170                                 services.add(provided[0]);
171                                 required.setProvider(provided[0]);
172                         }
173                         
174                         // Start the service. 
175                         system.start(_context);
176                         
177                         // Add started services to the set of started services.
178                         for (ProvidedInterface service : system.getProvidedServices()) {
179                                 allProvided.add(service);
180                         }
181
182                 }
183         }
184
185 }