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.List;
23 import org.apache.commons.logging.Log;
24 import org.apache.commons.logging.LogFactory;
27 * Composite system consisting of multiple subsystems.
29 * @author Erik Brakkee
31 public class Container extends AbstractComponent {
33 private static final Log LOG = LogFactory.getLog(Container.class);
35 private Component[] _systems;
37 public static RequiredInterface[] filterRequiredServices(
38 ProvidedInterface aProvided,
39 Collection<RequiredInterface> aDescriptors) {
40 List<RequiredInterface> required = new ArrayList<RequiredInterface>();
41 for (RequiredInterface descriptor : aDescriptors) {
42 if (descriptor.implementedBy(aProvided)) {
43 required.add(descriptor);
46 return required.toArray(new RequiredInterface[0]);
49 public static ProvidedInterface[] filterProvidedServices(
50 RequiredInterface aRequired, Collection<ProvidedInterface> aProvided) {
51 List<ProvidedInterface> provided = new ArrayList<ProvidedInterface>();
52 for (ProvidedInterface descriptor : aProvided) {
53 if (aRequired.implementedBy(descriptor)) {
54 provided.add(descriptor);
57 return provided.toArray(new ProvidedInterface[0]);
61 * Construcst the composite system.
70 * Provided services of the system.
72 * Required services by the system.
74 public Container(String aName, Component[] aSystems,
75 ProvidedInterface[] aProvided, RequiredInterface[] aRequired) {
76 super(aName, aProvided, aRequired);
78 for (Component component : aSystems) {
79 component.addContext(getQualifiedName());
85 * Validates the subsystems together to check that there are no required
86 * services not in the required list and no services in the provided list
87 * that cannot be provided. Also logs a warning in case of superfluous
90 private void validate(RequiredInterface[] aRequired) {
91 List<ProvidedInterface> provided = new ArrayList<ProvidedInterface>();
92 for (Component system : _systems) {
93 provided.addAll(Arrays.asList(system.getProvidedInterfaces()));
96 List<RequiredInterface> required = new ArrayList<RequiredInterface>();
97 for (Component system : _systems) {
98 required.addAll(Arrays.asList(system.getRequiredInterfaces()));
101 for (ProvidedInterface service : getProvidedInterfaces()) {
102 if (!(provided.contains(service))) {
103 throw new SystemAssemblyException(getName() + ": Service '"
105 + "' is not provided by any of the subsystems");
109 for (RequiredInterface service : getRequiredInterfaces()) {
110 if (!(required.contains(service))) {
113 + "' indicated as required is not actually required by any of the subsystems");
117 List<RequiredInterface> reallyRequired = new ArrayList<RequiredInterface>(
119 // Compute all required interfaces that are not provided
120 for (ProvidedInterface service : provided) {
121 List<RequiredInterface> fulfilled = Arrays
122 .asList(filterRequiredServices(service, reallyRequired));
123 reallyRequired.removeAll(fulfilled);
125 // Now the remaining interfaces should be covered by the required
127 reallyRequired.removeAll(Arrays.asList(aRequired));
129 String missingRequired = "";
130 for (RequiredInterface service : reallyRequired) {
131 missingRequired += service + "\n";
133 if (missingRequired.length() > 0) {
134 throw new SystemAssemblyException(getName()
135 + ": missing required services\n" + missingRequired);
140 protected void doStart() {
141 List<ProvidedInterface> provided = new ArrayList<ProvidedInterface>();
143 // all interfaces from the required list of this container are
144 // provided to the components inside it.
145 RequiredInterface[] required = getRequiredInterfaces();
146 for (RequiredInterface intf : required) {
147 ProvidedInterface provider = intf.getProvider();
148 if (provider == null) {
149 throw new SystemAssemblyException(getQualifiedName()
150 + ": required interface '" + intf + "' is not provided");
152 provided.add(intf.getProvider());
159 * Starts the subsystems.
161 * @param aRequiredServices
162 * Services that are available from other systems that have been
165 private void startImpl() {
166 LOG.info("Starting '" + getQualifiedName() + "'");
167 List<ProvidedInterface> allProvided = new ArrayList<ProvidedInterface>();
169 // Add the provides of all externally required interfaces to the list of
172 for (RequiredInterface required : getRequiredInterfaces()) {
173 allProvided.add(required.getProvider());
176 List<Component> started = new ArrayList<Component>();
177 for (Component system : _systems) {
179 // Check if all required services are already provided by
182 RequiredInterface[] required = system.getRequiredInterfaces();
184 for (RequiredInterface descriptor : required) {
185 ProvidedInterface[] filtered = filterProvidedServices(
186 descriptor, allProvided);
188 if (filtered.length == 0) {
189 throw new SystemAssemblyException(
192 + "' required by system '"
194 + "' is not provided by systems that are started earlier");
196 if (filtered.length > 1) {
197 throw new SystemAssemblyException(
200 + "' required by system '"
202 + "' matches multiple services provided by other systems: "
203 + Arrays.asList(filtered));
205 descriptor.setProvider(filtered[0]);
208 // Start the service.
212 // add all provided services
213 ProvidedInterface[] provided = system.getProvidedInterfaces();
214 allProvided.addAll(Arrays.asList(provided));
215 } catch (SystemAssemblyException e) {
217 } catch (RuntimeException e) {
218 LOG.error(getQualifiedName() + ": could not start '"
219 + system.getQualifiedName() + "'", e);
220 // an exception occurred, stop the successfully started
222 for (int i = started.size() - 1; i >= 0; i--) {
224 started.get(i).stop();
225 } catch (Throwable t) {
226 LOG.error(getQualifiedName() + ": error stopping "
227 + started.get(i).getQualifiedName());
237 protected void doStop() {
238 for (int i = _systems.length - 1; i >= 0; i--) {
243 private void info(String aMsg) {
244 LOG.info(getName() + ": " + aMsg);