8436ebca4174ba7090966756a5b6b2e5b8923ea5
[utils] / system / general / src / main / java / org / wamblee / system / core / Container.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.core;
17
18 import java.util.ArrayList;
19 import java.util.Arrays;
20 import java.util.Collection;
21 import java.util.List;
22
23 import org.apache.commons.logging.Log;
24 import org.apache.commons.logging.LogFactory;
25
26 /**
27  * Composite system consisting of multiple subsystems.
28  * 
29  * @author Erik Brakkee
30  */
31 public class Container extends AbstractComponent {
32
33         private static final Log LOG = LogFactory.getLog(Container.class);
34
35         private Component[] _systems;
36
37         public static RequiredInterface[] filterRequiredServices(
38                         ProvidedInterface aProvided,
39                         Collection<RequiredInterface> aDescriptors) {
40                 List<RequiredInterface> required = new ArrayList<RequiredInterface>();
41                 for (RequiredInterface descriptor : aDescriptors) {
42                         if (descriptor.implementedBy(aProvided)) {
43                                 required.add(descriptor);
44                         }
45                 }
46                 return required.toArray(new RequiredInterface[0]);
47         }
48
49         public static ProvidedInterface[] filterProvidedServices(
50                         RequiredInterface aRequired, Collection<ProvidedInterface> aProvided) {
51                 List<ProvidedInterface> provided = new ArrayList<ProvidedInterface>();
52                 for (ProvidedInterface descriptor : aProvided) {
53                         if (aRequired.implementedBy(descriptor)) {
54                                 provided.add(descriptor);
55                         }
56                 }
57                 return provided.toArray(new ProvidedInterface[0]);
58         }
59
60         /**
61          * Construcst the composite system.
62          * 
63          * @param aName
64          *            Name of the system.
65          * @param aRegistry
66          *            Service registry.
67          * @param aSystems
68          *            Subsystems.
69          * @param aProvided
70          *            Provided services of the system.
71          * @param aRequired
72          *            Required services by the system.
73          */
74         public Container(String aName, Component[] aSystems,
75                         ProvidedInterface[] aProvided, RequiredInterface[] aRequired) {
76                 super(aName, aProvided, aRequired);
77                 _systems = aSystems;
78                 for (Component component : aSystems) {
79                         component.addContext(getQualifiedName());
80                 }
81                 validate(aRequired);
82         }
83
84         /**
85          * Validates the subsystems together to check that there are no required
86          * services not in the required list and no services in the provided list
87          * that cannot be provided. Also logs a warning in case of superfluous
88          * requirements.
89          */
90         private void validate(RequiredInterface[] aRequired) {
91                 List<ProvidedInterface> provided = new ArrayList<ProvidedInterface>();
92                 for (Component system : _systems) {
93                         provided.addAll(Arrays.asList(system.getProvidedInterfaces()));
94                 }
95
96                 List<RequiredInterface> required = new ArrayList<RequiredInterface>();
97                 for (Component system : _systems) {
98                         required.addAll(Arrays.asList(system.getRequiredInterfaces()));
99                 }
100
101                 for (ProvidedInterface service : getProvidedInterfaces()) {
102                         if (!(provided.contains(service))) {
103                                 throw new SystemAssemblyException(getName() + ": Service '"
104                                                 + service
105                                                 + "' is not provided by any of the subsystems");
106                         }
107                 }
108
109                 for (RequiredInterface service : getRequiredInterfaces()) {
110                         if (!(required.contains(service))) {
111                                 info("Service '"
112                                                 + service
113                                                 + "' indicated as required is not actually required by any of the subsystems");
114                         }
115                 }
116
117                 List<RequiredInterface> reallyRequired = new ArrayList<RequiredInterface>(
118                                 required);
119                 // Compute all required interfaces that are not provided
120                 for (ProvidedInterface service : provided) {
121                         List<RequiredInterface> fulfilled = Arrays
122                                         .asList(filterRequiredServices(service, reallyRequired));
123                         reallyRequired.removeAll(fulfilled);
124                 }
125                 // Now the remaining interfaces should be covered by the required
126                 // list.
127                 reallyRequired.removeAll(Arrays.asList(aRequired));
128
129                 String missingRequired = "";
130                 for (RequiredInterface service : reallyRequired) {
131                         missingRequired += service + "\n";
132                 }
133                 if (missingRequired.length() > 0) {
134                         throw new SystemAssemblyException(getName()
135                                         + ": missing required services\n" + missingRequired);
136                 }
137         }
138
139         @Override
140         protected void doStart() {
141                 List<ProvidedInterface> provided = new ArrayList<ProvidedInterface>();
142
143                 // all interfaces from the required list of this container are
144                 // provided to the components inside it.
145                 RequiredInterface[] required = getRequiredInterfaces();
146                 for (RequiredInterface intf : required) {
147                         ProvidedInterface provider = intf.getProvider();
148                         if (provider == null) {
149                                 throw new SystemAssemblyException(getQualifiedName()
150                                                 + ": required interface '" + intf + "' is not provided");
151                         }
152                         provided.add(intf.getProvider());
153                 }
154
155                 startImpl();
156         }
157
158         /**
159          * Starts the subsystems.
160          * 
161          * @param aRequiredServices
162          *            Services that are available from other systems that have been
163          *            started before.
164          */
165         private void startImpl() {
166                 LOG.info("Starting '" + getQualifiedName() + "'");
167                 List<ProvidedInterface> allProvided = new ArrayList<ProvidedInterface>();
168
169                 // Add the provides of all externally required interfaces to the list of
170                 // available
171                 // interfaces
172                 for (RequiredInterface required : getRequiredInterfaces()) {
173                         allProvided.add(required.getProvider());
174                 }
175
176                 List<Component> started = new ArrayList<Component>();
177                 for (Component system : _systems) {
178                         try {
179                                 // Check if all required services are already provided by
180                                 // earlier
181                                 // systems.
182                                 RequiredInterface[] required = system.getRequiredInterfaces();
183
184                                 for (RequiredInterface descriptor : required) {
185                                         ProvidedInterface[] filtered = filterProvidedServices(
186                                                         descriptor, allProvided);
187
188                                         if (filtered.length == 0) {
189                                                 throw new SystemAssemblyException(
190                                                                 "Service '"
191                                                                                 + descriptor
192                                                                                 + "' required by system '"
193                                                                                 + system
194                                                                                 + "' is not provided by systems that are started earlier");
195                                         }
196                                         if (filtered.length > 1) {
197                                                 throw new SystemAssemblyException(
198                                                                 "Service '"
199                                                                                 + descriptor
200                                                                                 + "' required by system '"
201                                                                                 + system
202                                                                                 + "' matches multiple services provided by other systems: "
203                                                                                 + Arrays.asList(filtered));
204                                         }
205                                         descriptor.setProvider(filtered[0]);
206                                 }
207
208                                 // Start the service.
209                                 system.start();
210                                 started.add(system);
211
212                                 // add all provided services
213                                 ProvidedInterface[] provided = system.getProvidedInterfaces();
214                                 allProvided.addAll(Arrays.asList(provided));
215                         } catch (SystemAssemblyException e) { 
216                                 throw e; 
217                         } catch (RuntimeException e) {
218                                 LOG.error(getQualifiedName() + ": could not start '"
219                                                 + system.getQualifiedName() + "'", e);
220                                 // an exception occurred, stop the successfully started
221                                 // systems
222                                 for (int i = started.size() - 1; i >= 0; i--) {
223                                         try {
224                                                 started.get(i).stop();
225                                         } catch (Throwable t) {
226                                                 LOG.error(getQualifiedName() + ": error stopping "
227                                                                 + started.get(i).getQualifiedName());
228                                         }
229                                 }
230                                 throw e; 
231                         }
232                 }
233
234         }
235
236         @Override
237         protected void doStop() {
238                 for (int i = _systems.length - 1; i >= 0; i--) {
239                         _systems[i].stop();
240                 }
241         }
242
243         private void info(String aMsg) {
244                 LOG.info(getName() + ": " + aMsg);
245         }
246
247 }