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