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