bd059902d5f2a5995c5bfe6cf283fbe95f315536
[utils] / system / general / src / main / java / org / wamblee / system / 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;
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                 validate(aRequired);
76         }
77
78         /**
79          * Validates the subsystems together to check that there are
80          * no required services not in the required list and 
81          * no services in the provided list that cannot be provided. 
82          * Also logs a warning in case of superfluous requirements.  
83          */
84         private void validate(RequiredInterface[] aRequired) {
85                 List<ProvidedInterface> provided = new ArrayList<ProvidedInterface>();
86                 for (Component system : _systems) {
87                         provided.addAll(Arrays.asList(system.getProvidedServices()));
88                 }
89
90                 List<RequiredInterface> required = new ArrayList<RequiredInterface>();
91                 for (Component system : _systems) {
92                         required.addAll(Arrays.asList(system.getRequiredServices()));
93                 }
94
95                 for (ProvidedInterface service : getProvidedServices()) {
96                         if (!(provided.contains(service))) {
97                                 throw new SystemAssemblyException(getName() + ": Service '" + service
98                                                 + "' is not provided by any of the subsystems");
99                         }
100                 }
101
102                 for (RequiredInterface service : getRequiredServices()) {
103                         if (!(required.contains(service))) {
104                                 info("Service '"
105                                                 + service
106                                                 + "' indicated as required is not actually required by any of the subsystems");
107                         }
108                 }
109
110                 List<RequiredInterface> reallyRequired = new ArrayList<RequiredInterface>(
111                                 required);
112                 // Compute all required interfaces that are not provided
113                 for (ProvidedInterface service : provided) {
114                         List<RequiredInterface> fulfilled = 
115                                 Arrays.asList(filterRequiredServices(service, 
116                                         reallyRequired));
117                         reallyRequired.removeAll(fulfilled); 
118                 }
119                 // Now the remaining interfaces should be covered by the required
120                 // list. 
121                 reallyRequired.removeAll(Arrays.asList(aRequired));
122                 
123                 String missingRequired = "";
124                 for (RequiredInterface service: reallyRequired) {
125                         missingRequired += service + "\n";
126                 }
127                 if ( missingRequired.length() > 0 ) { 
128                         throw new SystemAssemblyException(getName() + ": missing required services\n" + missingRequired);
129                 }
130         }
131
132         @Override
133         protected void doStart(String aContext) {
134                 List<ProvidedInterface> provided = new ArrayList<ProvidedInterface>();
135                 
136                 // all interfaces from the required list of this container are
137                 // provided to the components inside it.
138                 RequiredInterface[] required = getRequiredServices();
139                 for (RequiredInterface intf: required) { 
140                     ProvidedInterface provider = intf.getProvider(); 
141                     if ( provider == null ) { 
142                         throw new SystemAssemblyException(aContext + ": required interface '" + intf +"' is not provided");
143                     }
144                         provided.add(intf.getProvider());
145                 }
146                 
147                 startImpl();
148         }
149         
150         /**
151          * Starts the subsystems.
152          * 
153          * @param aRequiredServices
154          *            Services that are available from other systems that have been
155          *            started before.
156          */
157         private void startImpl() {
158                 LOG.info("Starting '" + "'");
159                 List<ProvidedInterface> allProvided = new ArrayList<ProvidedInterface>();
160                 
161                 // Add the provides of all externally required interfaces to the list of available
162                 // interfaces
163                 for (RequiredInterface required: getRequiredServices()) { 
164                         allProvided.add(required.getProvider());
165                 }
166                 
167                 for (Component system : _systems) {
168                         // Check if all required services are already provided by earlier
169                         // systems.
170                         RequiredInterface[] required = system.getRequiredServices();
171
172                         for (RequiredInterface descriptor : required) {
173                                 ProvidedInterface[] filtered = filterProvidedServices(
174                                                 descriptor, allProvided);
175
176                                 if (filtered.length == 0) {
177                                         throw new SystemAssemblyException(
178                                                         "Service '"
179                                                                         + descriptor
180                                                                         + "' required by system '"
181                                                                         + system
182                                                                         + "' is not provided by systems that are started earlier");
183                                 }
184                                 if (filtered.length > 1) {
185                                         throw new SystemAssemblyException(
186                                                         "Service '"
187                                                                         + descriptor
188                                                                         + "' required by system '"
189                                                                         + system
190                                                                         + "' matches multiple services provided by other systems: " + 
191                                                                         Arrays.asList(filtered));
192                                 }
193                                 descriptor.setProvider(filtered[0]);
194                         }
195                         
196                         // Start the service. 
197                         system.start(getQualifiedName());
198
199                         // add all provided services
200                         ProvidedInterface[] provided = system.getProvidedServices();
201                         allProvided.addAll(Arrays.asList(provided));
202                 }
203         }
204
205         
206         @Override
207         protected void doStop() {
208                 for (int i = _systems.length-1; i >= 0; i--) { 
209                         _systems[i].stop();
210                 }
211         }
212
213         private void info(String aMsg) {
214                 LOG.info(getName() + ": " + aMsg);
215         }
216
217 }