a4b0620fccc589ab5fd48796aa3ca8ba34d17768
[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.Iterator;
22 import java.util.List;
23
24 import org.apache.commons.logging.Log;
25 import org.apache.commons.logging.LogFactory;
26
27 /**
28  * Container consisting of multiple components. 
29  * 
30  * @author Erik Brakkee
31  */
32 public class Container extends AbstractComponent<Scope> {
33
34         private static final Log LOG = LogFactory.getLog(Container.class);
35
36         private List<Component> _components;
37         private boolean _sealed; 
38
39         public static RequiredInterface[] filterRequiredServices(
40                         ProvidedInterface aProvided,
41                         Collection<RequiredInterface> aDescriptors) {
42                 List<RequiredInterface> required = new ArrayList<RequiredInterface>();
43                 for (RequiredInterface descriptor : aDescriptors) {
44                         if (descriptor.implementedBy(aProvided)) {
45                                 required.add(descriptor);
46                         }
47                 }
48                 return required.toArray(new RequiredInterface[0]);
49         }
50
51         public static ProvidedInterface[] filterProvidedServices(
52                         RequiredInterface aRequired, Collection<ProvidedInterface> aProvided) {
53                 List<ProvidedInterface> provided = new ArrayList<ProvidedInterface>();
54                 for (ProvidedInterface descriptor : aProvided) {
55                         if (aRequired.implementedBy(descriptor)) {
56                                 provided.add(descriptor);
57                         }
58                 }
59                 return provided.toArray(new ProvidedInterface[0]);
60         }
61
62         /**
63          * Constructs the container
64          * 
65          * @param aName
66          *            Name of the container
67          * @param aComponents
68          *            Components.
69          * @param aProvided
70          *            Provided services of the container
71          * @param aRequired
72          *            Required services by the container.
73          */
74         public Container(String aName, Component[] aComponents,
75                         ProvidedInterface[] aProvided, RequiredInterface[] aRequired) {
76                 super(aName, aProvided, aRequired);
77                 _components = new ArrayList<Component>(Arrays.asList(aComponents));
78                 for (Component component : aComponents) {
79                         component.addContext(getQualifiedName());
80                 }
81                 _sealed = false; 
82                 validate();
83         }
84         
85         public Container(String aName) {
86             this(aName, new Component[0], new ProvidedInterface[0], new RequiredInterface[0]);
87         }
88         
89         public Container addComponent(Component aComponent) {
90                 checkSealed(); 
91                 _components.add(aComponent);
92                 return this; 
93         }
94
95         @Override
96         protected Container addProvidedInterface(ProvidedInterface aProvided) {
97                 checkSealed();
98                 super.addProvidedInterface(aProvided);
99                 return this; 
100         }
101         
102         @Override
103         protected Container addRequiredInterface(RequiredInterface aRequired) {
104                 checkSealed(); 
105                 super.addRequiredInterface(aRequired);
106                 return this; 
107         }
108         
109         /**
110          * Validates the components together to check that there are no required
111          * services not in the required list and no services in the provided list
112          * that cannot be provided. Also logs a warning in case of superfluous
113          * requirements.
114          * @throws SystemAssemblyException in case of any validation problems. 
115          */
116         public void validate() {
117                 List<ProvidedInterface> provided = new ArrayList<ProvidedInterface>();
118                 for (Component component : _components) {
119                         provided.addAll(Arrays.asList(component.getProvidedInterfaces()));
120                 }
121
122                 List<RequiredInterface> required = new ArrayList<RequiredInterface>();
123                 for (Component component : _components) {
124                         required.addAll(Arrays.asList(component.getRequiredInterfaces()));
125                 }
126
127                 validateProvidedInterfaces(provided);
128
129                 validateRequiredInterfaces(required);
130
131                 List<RequiredInterface> reallyRequired = validateRequiredProvidedMatch(
132                                 provided, required);
133                 
134                 String missingRequired = "";
135                 for (RequiredInterface service : reallyRequired) {
136                         missingRequired += service + "\n";
137                 }
138                 if (missingRequired.length() > 0) {
139                         throw new SystemAssemblyException(getName()
140                                         + ": missing required services\n" + missingRequired);
141                 }
142         }
143
144         private List<RequiredInterface> validateRequiredProvidedMatch(
145                         List<ProvidedInterface> aProvided, List<RequiredInterface> aRequired) {
146                 List<RequiredInterface> reallyRequired = new ArrayList<RequiredInterface>(
147                                 aRequired);
148                 // Compute all required interfaces that are not provided
149
150                 for (ProvidedInterface service : aProvided) {
151                         List<RequiredInterface> fulfilled = Arrays
152                                         .asList(filterRequiredServices(service, reallyRequired));
153                         reallyRequired.removeAll(fulfilled);
154                 }
155                 // Now remove all optional interfaces from the list.
156                 for (Iterator<RequiredInterface> i = 
157                         reallyRequired.iterator(); i.hasNext(); ) { 
158                         RequiredInterface req = i.next();
159                         if ( req.isOptional() ) {
160                                 i.remove(); 
161                         }
162                 }
163                 // Now the remaining interfaces should be covered by the required
164                 // list.
165                 reallyRequired.removeAll(Arrays.asList(getRequiredInterfaces()));
166                 return reallyRequired;
167         }
168
169         private void validateRequiredInterfaces(List<RequiredInterface> aRequired) {
170                 for (RequiredInterface service : getRequiredInterfaces()) {
171                         // TODO required interfaces by the component could be 
172                         //      subclasses or implementations of the requirements
173                         //      of the contained components. The code below assumes
174                         //      an exact match. 
175                         if (!(aRequired.contains(service))) {
176                                 info("Service '"
177                                                 + service
178                                                 + "' indicated as required is not actually required by any of the components");
179                         }
180                         // Check for the case that the externally required service
181                         // is optional whereas the internally required service is 
182                         // mandatory. 
183                         if ( service.isOptional()) { 
184                                 for (RequiredInterface intf: aRequired) { 
185                                         if ( intf.equals(service) && !intf.isOptional()) {  
186                                                 warn("Required service '" + service + "' indicated as optional is mandatory by one of its components (" + getClients(intf) + ", " + intf + "), this can lead to problems when the container is started and the interface is not provided to the container.");
187                                         }
188                                 }
189                         }
190                 }
191         }
192
193         private void validateProvidedInterfaces(List<ProvidedInterface> aProvided) {
194                 for (ProvidedInterface service : getProvidedInterfaces()) {
195                         // TODO provided interfaces by components could be 
196                         //      provide subclasses or implementations of the 
197                         //      provided interfaces of the container.
198                         //      The code below assumes an exact match. 
199                         if (!(aProvided.contains(service))) {
200                                 throw new SystemAssemblyException(getName() + ": Service '"
201                                                 + service
202                                                 + "' is not provided by any of its components");
203                         }
204                 }
205         }
206         
207         /**
208          * Starts the container. After the container is started, the container becomes sealed
209          * meaning that no further components, required or provided interfaces may be added. 
210          * @return Scope. 
211          */
212         public Scope start() {
213                 checkSealed();
214                 validate();
215             Scope scope = super.start(new DefaultScope(new ProvidedInterface[0]));
216             seal();
217             return scope; 
218         }
219         
220         /**
221          * Seal the container, meaning that no further components or interfaces may be added. 
222          */
223         public void seal() { 
224                 _sealed = true; 
225         }
226
227         /**
228          * Checks if the container is sealed. 
229          * @return True iff the container is sealed. 
230          */
231         public boolean isSealed() {
232                 return _sealed;
233         }
234
235         @Override
236         protected Scope doStart(Scope aExternalScope) {
237                 LOG.info("Starting '" + getQualifiedName() + "'");
238                 
239                 Scope scope = new DefaultScope(getProvidedInterfaces(), 
240                                 aExternalScope);
241                 
242                 List<ProvidedInterface> allProvided = new ArrayList<ProvidedInterface>();
243
244                 // all interfaces from the required list of this container are
245                 // provided to the components inside it.
246                 RequiredInterface[] required = getRequiredInterfaces();
247                 for (RequiredInterface intf : required) {
248                         ProvidedInterface provider = intf.getProvider();
249                         if (provider != null ) { 
250                                 allProvided.add(provider);
251                         } else { 
252                                 if ( !intf.isOptional()) { 
253                                         throw new SystemAssemblyException(getQualifiedName()
254                                                         + ": required interface '" + intf + "' is not provided");               
255                                 }
256                         }
257                 }
258
259                 List<Component> started = new ArrayList<Component>();
260                 for (Component component : _components) {
261                         try {
262                                 checkAllRequiredServicesAlreadyProvided(allProvided, component);
263
264                                 // Start the service.
265                                 Object runtime = component.start(scope);
266                                 scope.addRuntime(component, runtime); 
267                                 started.add(component);
268
269                                 // add all provided services
270                                 ProvidedInterface[] provided = component.getProvidedInterfaces();
271                                 allProvided.addAll(Arrays.asList(provided));
272                         } catch (SystemAssemblyException e) { 
273                                 throw e; 
274                         } catch (RuntimeException e) {
275                                 LOG.error(getQualifiedName() + ": could not start '"
276                                                 + component.getQualifiedName() + "'", e);
277                                 stopAlreadyStartedComponents(started, scope);
278                                 throw e; 
279                         }
280                 }
281                 return scope; 
282         }
283
284         private void stopAlreadyStartedComponents(List<Component> aStarted, Scope aScope) {
285                 // an exception occurred, stop the successfully started
286                 // components
287                 for (int i = aStarted.size() - 1; i >= 0; i--) {
288                         try {
289                                 aStarted.get(i).stop(aScope);
290                         } catch (Throwable t) {
291                                 LOG.error(getQualifiedName() + ": error stopping "
292                                                 + aStarted.get(i).getQualifiedName());
293                         }
294                 }
295         }
296
297         private void checkAllRequiredServicesAlreadyProvided(
298                         List<ProvidedInterface> aAllProvided, Component aComponent) {
299                 // Check if all required services are already provided by
300                 // earlier
301                 // systems.
302
303                 for (RequiredInterface descriptor : aComponent.getRequiredInterfaces()) {
304                         ProvidedInterface[] filtered = filterProvidedServices(
305                                         descriptor, aAllProvided);
306                         if ( filtered.length == 1 ) { 
307                                 descriptor.setProvider(filtered[0]);
308                         } else if ( filtered.length > 1 ) { 
309                                 throw new SystemAssemblyException(
310                                                 "Service '"
311                                                                 + descriptor
312                                                                 + "' required by system '"
313                                                                 + aComponent
314                                                                 + "' matches multiple services provided by other systems: "
315                                                                 + getServers(filtered));
316                         } else { 
317                                 // filtered.length == 0
318                                 if ( !descriptor.isOptional()) { 
319                                         throw new SystemAssemblyException(
320                                                         "Service '"
321                                                                         + descriptor
322                                                                         + "' required by system '"
323                                                                         + aComponent
324                                                                         + "' is not provided by systems that are started earlier");     
325                                 }
326                         }
327                 }
328         }
329
330         @Override
331         protected void doStop(Scope aScope) {
332                 for (int i = _components.size() - 1; i >= 0; i--) {
333                         Component component = _components.get(i);
334                         Object runtime = aScope.getRuntime(component);
335                         component.stop(runtime);
336                 }
337         }
338
339         private void info(String aMsg) {
340                 LOG.info(getQualifiedName() + ": " + aMsg);
341         }
342         
343         private void warn(String aMsg) {
344                 LOG.warn(getQualifiedName() + ": " + aMsg);
345         }
346         
347         private String getServers(ProvidedInterface[] aProvidedList ) {
348                 String result = "";
349                 for (ProvidedInterface provided: aProvidedList) {
350                         result += "(components ";
351                         for (Component component: _components) { 
352                                 for (ProvidedInterface provided2: component.getProvidedInterfaces()) { 
353                                         if ( provided.equals(provided2)) { 
354                                                 result += component + " ";
355                                         }
356                                 }
357                         }
358                         result += ", interface " + provided + ")";
359                 }
360                 return result;
361         }
362
363         private List<Component> getClients(RequiredInterface aRequirement) {
364                 List<Component> clients = new ArrayList<Component>();
365                 for (Component component: _components) { 
366                         for (RequiredInterface required: component.getRequiredInterfaces()) { 
367                                 if ( required.equals(aRequirement) && required.isOptional() == aRequirement.isOptional()) { 
368                                         clients.add(component);
369                                 }
370                         }
371                 }
372                 return clients; 
373         }
374         
375         private void checkSealed() { 
376                 if ( _sealed ) { 
377                         throw new SystemAssemblyException("Container is sealed");
378                 }
379         }
380 }