2 * Copyright 2007 the original author or authors.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 package org.wamblee.system.core;
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;
24 import org.apache.commons.logging.Log;
25 import org.apache.commons.logging.LogFactory;
28 * Container consisting of multiple components.
30 * @author Erik Brakkee
32 public class Container extends AbstractComponent<Scope> {
34 private static final Log LOG = LogFactory.getLog(Container.class);
36 private Component[] _components;
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);
47 return required.toArray(new RequiredInterface[0]);
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);
58 return provided.toArray(new ProvidedInterface[0]);
62 * Construcst the container
65 * Name of the container
69 * Provided services of the container
71 * Required services by the container.
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());
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
89 private void validate() {
90 List<ProvidedInterface> provided = new ArrayList<ProvidedInterface>();
91 for (Component component : _components) {
92 provided.addAll(Arrays.asList(component.getProvidedInterfaces()));
95 List<RequiredInterface> required = new ArrayList<RequiredInterface>();
96 for (Component component : _components) {
97 required.addAll(Arrays.asList(component.getRequiredInterfaces()));
100 validateProvidedInterfaces(provided);
102 validateRequiredInterfaces(required);
104 List<RequiredInterface> reallyRequired = validateRequiredProvidedMatch(
107 String missingRequired = "";
108 for (RequiredInterface service : reallyRequired) {
109 missingRequired += service + "\n";
111 if (missingRequired.length() > 0) {
112 throw new SystemAssemblyException(getName()
113 + ": missing required services\n" + missingRequired);
117 private List<RequiredInterface> validateRequiredProvidedMatch(
118 List<ProvidedInterface> aProvided, List<RequiredInterface> aRequired) {
119 List<RequiredInterface> reallyRequired = new ArrayList<RequiredInterface>(
121 // Compute all required interfaces that are not provided
123 for (ProvidedInterface service : aProvided) {
124 List<RequiredInterface> fulfilled = Arrays
125 .asList(filterRequiredServices(service, reallyRequired));
126 reallyRequired.removeAll(fulfilled);
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() ) {
136 // Now the remaining interfaces should be covered by the required
138 reallyRequired.removeAll(Arrays.asList(getRequiredInterfaces()));
139 return reallyRequired;
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
148 if (!(aRequired.contains(service))) {
151 + "' indicated as required is not actually required by any of the components");
153 // Check for the case that the externally required service
154 // is optional whereas the internally required service is
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.");
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 '"
175 + "' is not provided by any of its components");
180 public Scope start() {
181 return super.start(new DefaultScope(new ProvidedInterface[0]));
185 protected Scope doStart(Scope aExternalScope) {
186 LOG.info("Starting '" + getQualifiedName() + "'");
188 Scope scope = new DefaultScope(getProvidedInterfaces(),
191 List<ProvidedInterface> allProvided = new ArrayList<ProvidedInterface>();
193 // all interfaces from the required list of this container are
194 // provided to the components inside it.
195 RequiredInterface[] required = getRequiredInterfaces();
196 for (RequiredInterface intf : required) {
197 ProvidedInterface provider = intf.getProvider();
198 if (provider != null ) {
199 allProvided.add(provider);
201 if ( !intf.isOptional()) {
202 throw new SystemAssemblyException(getQualifiedName()
203 + ": required interface '" + intf + "' is not provided");
208 List<Component> started = new ArrayList<Component>();
209 for (Component component : _components) {
211 checkAllRequiredServicesAlreadyProvided(allProvided, component);
213 // Start the service.
214 Object runtime = component.start(scope);
215 scope.addRuntime(component, runtime);
216 started.add(component);
218 // add all provided services
219 ProvidedInterface[] provided = component.getProvidedInterfaces();
220 allProvided.addAll(Arrays.asList(provided));
221 } catch (SystemAssemblyException e) {
223 } catch (RuntimeException e) {
224 LOG.error(getQualifiedName() + ": could not start '"
225 + component.getQualifiedName() + "'", e);
226 stopAlreadyStartedComponents(started, scope);
233 private void stopAlreadyStartedComponents(List<Component> aStarted, Scope aScope) {
234 // an exception occurred, stop the successfully started
236 for (int i = aStarted.size() - 1; i >= 0; i--) {
238 aStarted.get(i).stop(aScope);
239 } catch (Throwable t) {
240 LOG.error(getQualifiedName() + ": error stopping "
241 + aStarted.get(i).getQualifiedName());
246 private void checkAllRequiredServicesAlreadyProvided(
247 List<ProvidedInterface> aAllProvided, Component aComponent) {
248 // Check if all required services are already provided by
252 for (RequiredInterface descriptor : aComponent.getRequiredInterfaces()) {
253 ProvidedInterface[] filtered = filterProvidedServices(
254 descriptor, aAllProvided);
255 if ( filtered.length == 1 ) {
256 descriptor.setProvider(filtered[0]);
257 } else if ( filtered.length > 1 ) {
258 throw new SystemAssemblyException(
261 + "' required by system '"
263 + "' matches multiple services provided by other systems: "
264 + getServers(filtered));
266 // filtered.length == 0
267 if ( !descriptor.isOptional()) {
268 throw new SystemAssemblyException(
271 + "' required by system '"
273 + "' is not provided by systems that are started earlier");
280 protected void doStop(Scope aScope) {
281 for (int i = _components.length - 1; i >= 0; i--) {
282 Component component = _components[i];
283 Object runtime = aScope.getRuntime(component);
284 component.stop(runtime);
288 private void info(String aMsg) {
289 LOG.info(getQualifiedName() + ": " + aMsg);
292 private void warn(String aMsg) {
293 LOG.warn(getQualifiedName() + ": " + aMsg);
296 private String getServers(ProvidedInterface[] aProvidedList ) {
298 for (ProvidedInterface provided: aProvidedList) {
299 result += "(components ";
300 for (Component component: _components) {
301 for (ProvidedInterface provided2: component.getProvidedInterfaces()) {
302 if ( provided.equals(provided2)) {
303 result += component + " ";
307 result += ", interface " + provided + ")";
312 private List<Component> getClients(RequiredInterface aRequirement) {
313 List<Component> clients = new ArrayList<Component>();
314 for (Component component: _components) {
315 for (RequiredInterface required: component.getRequiredInterfaces()) {
316 if ( required.equals(aRequirement) && required.isOptional() == aRequirement.isOptional()) {
317 clients.add(component);