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.HashSet;
22 import java.util.Iterator;
23 import java.util.List;
26 import org.apache.commons.logging.Log;
27 import org.apache.commons.logging.LogFactory;
28 import org.wamblee.general.Pair;
31 * Container consisting of multiple components.
33 * @author Erik Brakkee
35 public class Container extends AbstractComponent<Scope> {
37 private static final Log LOG = LogFactory.getLog(Container.class);
39 private List<Component> _components;
40 private Set<String> _componentNames;
41 private CompositeInterfaceRestriction _restriction;
42 private boolean _sealed;
45 public static ProvidedInterface[] filterProvidedServices(
46 Component aClient, RequiredInterface aRequired, Collection<Pair<ProvidedInterface,Component>> aProvided,
47 InterfaceRestriction aRestriction) {
48 List<ProvidedInterface> result = new ArrayList<ProvidedInterface>();
49 for (Pair<ProvidedInterface,Component> descriptor : aProvided) {
50 ProvidedInterface provided = descriptor.getFirst();
51 Component server = descriptor.getSecond();
52 if (aRequired.implementedBy(provided) &&
53 !aRestriction.isViolated(aClient, aRequired, server, provided)) {
57 return result.toArray(new ProvidedInterface[0]);
61 * Constructs the container
64 * Name of the container
68 * Provided services of the container
70 * Required services by the container.
72 public Container(String aName, Component[] aComponents,
73 ProvidedInterface[] aProvided, RequiredInterface[] aRequired) {
74 super(aName, aProvided, aRequired);
75 _components = new ArrayList<Component>();
77 _componentNames = new HashSet<String>();
78 _restriction = new CompositeInterfaceRestriction();
80 for (Component component : aComponents) {
81 addComponent(component);
85 public Container(String aName) {
86 this(aName, new Component[0], new ProvidedInterface[0],
87 new RequiredInterface[0]);
90 public Container addComponent(Component aComponent) {
92 if (aComponent.getContext() != null) {
93 throw new SystemAssemblyException(
94 "Inconsistent hierarchy, component '"
95 + aComponent.getName()
96 + "' is already part of another hierarchy");
98 if (_componentNames.contains(aComponent.getName())) {
99 throw new SystemAssemblyException("Duplicate component '"
100 + aComponent.getName() + "'");
102 _components.add(aComponent);
103 _componentNames.add(aComponent.getName());
104 aComponent.addContext(getQualifiedName());
109 * Adds an interface restriction for explicitly configuring the
110 * relations between components.
111 * @param aRestriction Restriction to add.
112 * @return Reference to this to allow call chaining.
114 public Container addRestriction(InterfaceRestriction aRestriction) {
116 _restriction.add(aRestriction);
121 public Container addProvidedInterface(ProvidedInterface aProvided) {
123 super.addProvidedInterface(aProvided);
128 public Container addRequiredInterface(RequiredInterface aRequired) {
130 super.addRequiredInterface(aRequired);
135 * Validates the components together to check that there are no required
136 * services not in the required list and no services in the provided list
137 * that cannot be provided. Also logs a warning in case of superfluous
140 * @throws SystemAssemblyException
141 * in case of any validation problems.
143 public void validate() {
144 validateProvidedInterfacesArePresent();
146 validateRequiredInterfaces();
148 doStartOptionalDryRun(null, true);
151 private void validateRequiredInterfaces() {
152 List<RequiredInterface> required = new ArrayList<RequiredInterface>();
153 for (Component component : _components) {
154 required.addAll(Arrays.asList(component.getRequiredInterfaces()));
157 for (RequiredInterface service : getRequiredInterfaces()) {
158 // TODO required interfaces by the component could be
159 // subclasses or implementations of the requirements
160 // of the contained components. The code below assumes
162 if (!(required.contains(service))) {
165 + "' indicated as required is not actually required by any of the components");
167 // Check for the case that the externally required service
168 // is optional whereas the internally required service is
170 if (service.isOptional()) {
171 for (RequiredInterface intf : required) {
172 if (intf.equals(service) && !intf.isOptional()) {
173 warn("Required service '"
175 + "' indicated as optional is mandatory by one of its components ("
179 + "), this can lead to problems when the container is started and the interface is not provided to the container.");
186 private void validateProvidedInterfacesArePresent() {
187 List<ProvidedInterface> provided = new ArrayList<ProvidedInterface>();
188 for (Component component : _components) {
189 provided.addAll(Arrays.asList(component.getProvidedInterfaces()));
191 for (ProvidedInterface service : getProvidedInterfaces()) {
192 // TODO provided interfaces by components could be
193 // provide subclasses or implementations of the
194 // provided interfaces of the container.
195 // The code below assumes an exact match.
196 if (!(provided.contains(service))) {
197 throw new SystemAssemblyException(getName() + ": Service '"
199 + "' is not provided by any of its components");
205 * Starts the container. After the container is started, the container
206 * becomes sealed meaning that no further components, required or provided
207 * interfaces may be added.
211 public Scope start() {
214 Scope scope = super.start(new DefaultScope(new ProvidedInterface[0]));
220 * Seal the container, meaning that no further components or interfaces may
228 * Checks if the container is sealed.
230 * @return True iff the container is sealed.
232 public boolean isSealed() {
237 protected Scope doStart(Scope aExternalScope) {
238 return doStartOptionalDryRun(aExternalScope, false);
241 private Scope doStartOptionalDryRun(Scope aExternalScope, boolean aDryRun) {
242 LOG.info("Starting '" + getQualifiedName() + "'");
244 Scope scope = new DefaultScope(getProvidedInterfaces(), aExternalScope);
246 List<Pair<ProvidedInterface,Component>> allProvided = new ArrayList<Pair<ProvidedInterface,Component>>();
248 addProvidersOfRequiredInterfaces(allProvided);
250 List<Component> started = new ArrayList<Component>();
251 for (Component component : _components) {
253 initializeProvidersForRequiredInterfaces(allProvided,
256 // Start the service.
258 Object runtime = component.start(scope);
259 scope.addRuntime(component, runtime);
260 started.add(component);
263 // add all provided services
264 ProvidedInterface[] provided = component
265 .getProvidedInterfaces();
266 for (ProvidedInterface prov: provided) {
267 allProvided.add(new Pair<ProvidedInterface,Component>(prov, component));
269 } catch (SystemAssemblyException e) {
271 } catch (RuntimeException e) {
272 LOG.error(getQualifiedName() + ": could not start '"
273 + component.getQualifiedName() + "'", e);
274 stopAlreadyStartedComponents(started, scope);
281 private void addProvidersOfRequiredInterfaces(
282 List<Pair<ProvidedInterface,Component>> allProvided) {
283 // all interfaces from the required list of this container are
284 // provided to the components inside it.
285 RequiredInterface[] required = getRequiredInterfaces();
286 for (RequiredInterface intf : required) {
287 ProvidedInterface provider = intf.getProvider();
288 if (provider != null) {
289 allProvided.add(new Pair<ProvidedInterface,Component>(provider, null));
291 if (!intf.isOptional()) {
292 throw new SystemAssemblyException(getQualifiedName()
293 + ": required interface '" + intf
294 + "' is not provided");
300 private void stopAlreadyStartedComponents(List<Component> aStarted,
302 // an exception occurred, stop the successfully started
304 for (int i = aStarted.size() - 1; i >= 0; i--) {
306 Component component = aStarted.get(i);
307 aStarted.get(i).stop(aScope.getRuntime(component));
308 } catch (Throwable t) {
309 LOG.error(getQualifiedName() + ": error stopping "
310 + aStarted.get(i).getQualifiedName());
316 * Sets the provided interface or a component.
318 * @param aAllProvided
319 * All available provided interfaces.
321 * Component whose required interfaces we are looking at.
322 * @param aValidateOnly
323 * If true then the provider will not be set for required
326 private void initializeProvidersForRequiredInterfaces(
327 List<Pair<ProvidedInterface,Component>> aAllProvided, Component aComponent,
328 boolean aValidateOnly) {
329 // Check if all required services are already provided by
333 for (RequiredInterface descriptor : aComponent.getRequiredInterfaces()) {
334 ProvidedInterface[] filtered = filterProvidedServices(aComponent, descriptor,
335 aAllProvided, _restriction);
336 if (filtered.length == 1) {
337 if (!aValidateOnly) {
338 descriptor.setProvider(filtered[0]);
340 } else if (filtered.length > 1) {
341 throw new SystemAssemblyException(
344 + "' required by system '"
346 + "' matches multiple services provided by other systems: "
347 + getServers(filtered));
349 // filtered.length == 0
350 if (!descriptor.isOptional()) {
351 throw new SystemAssemblyException(
354 + "' required by system '"
356 + "' is not provided by systems that are started earlier");
363 protected void doStop(Scope aScope) {
364 for (int i = _components.size() - 1; i >= 0; i--) {
365 Component component = _components.get(i);
366 Object runtime = aScope.getRuntime(component);
367 component.stop(runtime);
371 private void info(String aMsg) {
372 LOG.info(getQualifiedName() + ": " + aMsg);
375 private void warn(String aMsg) {
376 LOG.warn(getQualifiedName() + ": " + aMsg);
379 private String getServers(ProvidedInterface[] aProvidedList) {
381 for (ProvidedInterface provided : aProvidedList) {
382 result += "(components ";
383 for (Component component : _components) {
384 for (ProvidedInterface provided2 : component
385 .getProvidedInterfaces()) {
386 if (provided.equals(provided2)) {
387 result += component + " ";
391 result += ", interface " + provided + ")";
396 private List<Component> getClients(RequiredInterface aRequirement) {
397 List<Component> clients = new ArrayList<Component>();
398 for (Component component : _components) {
399 for (RequiredInterface required : component.getRequiredInterfaces()) {
400 if (required.equals(aRequirement)
401 && required.isOptional() == aRequirement.isOptional()) {
402 clients.add(component);
409 private void checkSealed() {
411 throw new SystemAssemblyException("Container is sealed");