(no commit message)
[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 {
33
34         private static final Log LOG = LogFactory.getLog(Container.class);
35
36         private Component[] _components;
37
38         public static RequiredInterface[] filterRequiredServices(
39                         ProvidedInterface aProvided,
40                         Collection<RequiredInterface> aDescriptors) {
41                 List<RequiredInterface> required = new ArrayList<RequiredInterface>();
42                 for (RequiredInterface descriptor : aDescriptors) {
43                         if (descriptor.implementedBy(aProvided)) {
44                                 required.add(descriptor);
45                         }
46                 }
47                 return required.toArray(new RequiredInterface[0]);
48         }
49
50         public static ProvidedInterface[] filterProvidedServices(
51                         RequiredInterface aRequired, Collection<ProvidedInterface> aProvided) {
52                 List<ProvidedInterface> provided = new ArrayList<ProvidedInterface>();
53                 for (ProvidedInterface descriptor : aProvided) {
54                         if (aRequired.implementedBy(descriptor)) {
55                                 provided.add(descriptor);
56                         }
57                 }
58                 return provided.toArray(new ProvidedInterface[0]);
59         }
60
61         /**
62          * Construcst the container
63          * 
64          * @param aName
65          *            Name of the container
66          * @param aComponents
67          *            Components.
68          * @param aProvided
69          *            Provided services of the container
70          * @param aRequired
71          *            Required services by the container.
72          */
73         public Container(String aName, Component[] aComponents,
74                         ProvidedInterface[] aProvided, RequiredInterface[] aRequired) {
75                 super(aName, aProvided, aRequired);
76                 _components = aComponents;
77                 for (Component component : aComponents) {
78                         component.addContext(getQualifiedName());
79                 }
80                 validate();
81         }
82
83         /**
84          * Validates the components together to check that there are no required
85          * services not in the required list and no services in the provided list
86          * that cannot be provided. Also logs a warning in case of superfluous
87          * requirements.
88          */
89         private void validate() {
90                 List<ProvidedInterface> provided = new ArrayList<ProvidedInterface>();
91                 for (Component component : _components) {
92                         provided.addAll(Arrays.asList(component.getProvidedInterfaces()));
93                 }
94
95                 List<RequiredInterface> required = new ArrayList<RequiredInterface>();
96                 for (Component component : _components) {
97                         required.addAll(Arrays.asList(component.getRequiredInterfaces()));
98                 }
99
100                 validateProvidedInterfaces(provided);
101
102                 validateRequiredInterfaces(required);
103
104                 List<RequiredInterface> reallyRequired = validateRequiredProvidedMatch(
105                                 provided, required);
106                 
107                 String missingRequired = "";
108                 for (RequiredInterface service : reallyRequired) {
109                         missingRequired += service + "\n";
110                 }
111                 if (missingRequired.length() > 0) {
112                         throw new SystemAssemblyException(getName()
113                                         + ": missing required services\n" + missingRequired);
114                 }
115         }
116
117         private List<RequiredInterface> validateRequiredProvidedMatch(
118                         List<ProvidedInterface> aProvided, List<RequiredInterface> aRequired) {
119                 List<RequiredInterface> reallyRequired = new ArrayList<RequiredInterface>(
120                                 aRequired);
121                 // Compute all required interfaces that are not provided
122
123                 for (ProvidedInterface service : aProvided) {
124                         List<RequiredInterface> fulfilled = Arrays
125                                         .asList(filterRequiredServices(service, reallyRequired));
126                         reallyRequired.removeAll(fulfilled);
127                 }
128                 // Now remove all optional interfaces from the list.
129                 for (Iterator<RequiredInterface> i = 
130                         reallyRequired.iterator(); i.hasNext(); ) { 
131                         RequiredInterface req = i.next();
132                         if ( req.isOptional() ) {
133                                 i.remove(); 
134                         }
135                 }
136                 // Now the remaining interfaces should be covered by the required
137                 // list.
138                 reallyRequired.removeAll(Arrays.asList(getRequiredInterfaces()));
139                 return reallyRequired;
140         }
141
142         private void validateRequiredInterfaces(List<RequiredInterface> aRequired) {
143                 for (RequiredInterface service : getRequiredInterfaces()) {
144                         // TODO required interfaces by the component could be 
145                         //      subclasses or implementations of the requirements
146                         //      of the contained components. The code below assumes
147                         //      an exact match. 
148                         if (!(aRequired.contains(service))) {
149                                 info("Service '"
150                                                 + service
151                                                 + "' indicated as required is not actually required by any of the components");
152                         }
153                         // Check for the case that the externally required service
154                         // is optional whereas the internally required service is 
155                         // mandatory. 
156                         if ( service.isOptional()) { 
157                                 for (RequiredInterface intf: aRequired) { 
158                                         if ( intf.equals(service) && !intf.isOptional()) {  
159                                                 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.");
160                                         }
161                                 }
162                         }
163                 }
164         }
165
166         private void validateProvidedInterfaces(List<ProvidedInterface> aProvided) {
167                 for (ProvidedInterface service : getProvidedInterfaces()) {
168                         // TODO provided interfaces by components could be 
169                         //      provide subclasses or implementations of the 
170                         //      provided interfaces of the container.
171                         //      The code below assumes an exact match. 
172                         if (!(aProvided.contains(service))) {
173                                 throw new SystemAssemblyException(getName() + ": Service '"
174                                                 + service
175                                                 + "' is not provided by any of its components");
176                         }
177                 }
178         }
179
180         @Override
181         protected void doStart() {
182                 LOG.info("Starting '" + getQualifiedName() + "'");
183                 List<ProvidedInterface> allProvided = new ArrayList<ProvidedInterface>();
184
185                 // all interfaces from the required list of this container are
186                 // provided to the components inside it.
187                 RequiredInterface[] required = getRequiredInterfaces();
188                 for (RequiredInterface intf : required) {
189                         ProvidedInterface provider = intf.getProvider();
190                         if (provider != null ) { 
191                                 allProvided.add(provider);
192                         } else { 
193                                 if ( !intf.isOptional()) { 
194                                         throw new SystemAssemblyException(getQualifiedName()
195                                                         + ": required interface '" + intf + "' is not provided");               
196                                 }
197                         }
198                 }
199
200                 List<Component> started = new ArrayList<Component>();
201                 for (Component component : _components) {
202                         try {
203                                 checkAllRequiredServicesAlreadyProvided(allProvided, component);
204
205                                 // Start the service.
206                                 component.start();
207                                 started.add(component);
208
209                                 // add all provided services
210                                 ProvidedInterface[] provided = component.getProvidedInterfaces();
211                                 allProvided.addAll(Arrays.asList(provided));
212                         } catch (SystemAssemblyException e) { 
213                                 throw e; 
214                         } catch (RuntimeException e) {
215                                 LOG.error(getQualifiedName() + ": could not start '"
216                                                 + component.getQualifiedName() + "'", e);
217                                 stopAlreadyStartedComponents(started);
218                                 throw e; 
219                         }
220                 }
221
222         }
223
224         private void stopAlreadyStartedComponents(List<Component> aStarted) {
225                 // an exception occurred, stop the successfully started
226                 // components
227                 for (int i = aStarted.size() - 1; i >= 0; i--) {
228                         try {
229                                 aStarted.get(i).stop();
230                         } catch (Throwable t) {
231                                 LOG.error(getQualifiedName() + ": error stopping "
232                                                 + aStarted.get(i).getQualifiedName());
233                         }
234                 }
235         }
236
237         private void checkAllRequiredServicesAlreadyProvided(
238                         List<ProvidedInterface> aAllProvided, Component aComponent) {
239                 // Check if all required services are already provided by
240                 // earlier
241                 // systems.
242
243                 for (RequiredInterface descriptor : aComponent.getRequiredInterfaces()) {
244                         ProvidedInterface[] filtered = filterProvidedServices(
245                                         descriptor, aAllProvided);
246                         if ( filtered.length == 1 ) { 
247                                 descriptor.setProvider(filtered[0]);
248                         } else if ( filtered.length > 1 ) { 
249                                 throw new SystemAssemblyException(
250                                                 "Service '"
251                                                                 + descriptor
252                                                                 + "' required by system '"
253                                                                 + aComponent
254                                                                 + "' matches multiple services provided by other systems: "
255                                                                 + getServers(filtered));
256                         } else { 
257                                 // filtered.length == 0
258                                 if ( !descriptor.isOptional()) { 
259                                         throw new SystemAssemblyException(
260                                                         "Service '"
261                                                                         + descriptor
262                                                                         + "' required by system '"
263                                                                         + aComponent
264                                                                         + "' is not provided by systems that are started earlier");     
265                                 }
266                         }
267                 }
268         }
269
270         @Override
271         protected void doStop() {
272                 for (int i = _components.length - 1; i >= 0; i--) {
273                         _components[i].stop();
274                 }
275         }
276
277         private void info(String aMsg) {
278                 LOG.info(getQualifiedName() + ": " + aMsg);
279         }
280         
281         private void warn(String aMsg) {
282                 LOG.warn(getQualifiedName() + ": " + aMsg);
283         }
284         
285         private String getServers(ProvidedInterface[] aProvidedList ) {
286                 String result = "";
287                 for (ProvidedInterface provided: aProvidedList) {
288                         result += "(components ";
289                         for (Component component: _components) { 
290                                 for (ProvidedInterface provided2: component.getProvidedInterfaces()) { 
291                                         if ( provided.equals(provided2)) { 
292                                                 result += component + " ";
293                                         }
294                                 }
295                         }
296                         result += ", interface " + provided + ")";
297                 }
298                 return result;
299         }
300
301         private List<Component> getClients(RequiredInterface aRequirement) {
302                 List<Component> clients = new ArrayList<Component>();
303                 for (Component component: _components) { 
304                         for (RequiredInterface required: component.getRequiredInterfaces()) { 
305                                 if ( required.equals(aRequirement) && required.isOptional() == aRequirement.isOptional()) { 
306                                         clients.add(component);
307                                 }
308                         }
309                 }
310                 return clients; 
311         }
312 }