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.List;
25 import org.apache.commons.logging.Log;
26 import org.apache.commons.logging.LogFactory;
29 * Composite system consisting of multiple subsystems.
31 * @author Erik Brakkee
33 public class Container extends AbstractComponent {
35 private static final Log LOG = LogFactory.getLog(Container.class);
37 private Component[] _systems;
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);
48 return required.toArray(new RequiredInterface[0]);
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);
59 return provided.toArray(new ProvidedInterface[0]);
63 * Construcst the composite system.
72 * Provided services of the system.
74 * Required services by the system.
76 public Container(String aName, Component[] aSystems,
77 ProvidedInterface[] aProvided, RequiredInterface[] aRequired) {
78 super(aName, aProvided, aRequired);
80 for (Component component : aSystems) {
81 component.addContext(getQualifiedName());
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
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()));
98 List<RequiredInterface> required = new ArrayList<RequiredInterface>();
99 for (Component system : _systems) {
100 required.addAll(Arrays.asList(system.getRequiredInterfaces()));
103 for (ProvidedInterface service : getProvidedInterfaces()) {
104 if (!(provided.contains(service))) {
105 throw new SystemAssemblyException(getName() + ": Service '"
107 + "' is not provided by any of the subsystems");
111 for (RequiredInterface service : getRequiredInterfaces()) {
112 if (!(required.contains(service))) {
115 + "' indicated as required is not actually required by any of the subsystems");
119 List<RequiredInterface> reallyRequired = new ArrayList<RequiredInterface>(
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);
127 // Now the remaining interfaces should be covered by the required
129 reallyRequired.removeAll(Arrays.asList(aRequired));
131 String missingRequired = "";
132 for (RequiredInterface service : reallyRequired) {
133 missingRequired += service + "\n";
135 if (missingRequired.length() > 0) {
136 throw new SystemAssemblyException(getName()
137 + ": missing required services\n" + missingRequired);
142 protected void doStart() {
143 List<ProvidedInterface> provided = new ArrayList<ProvidedInterface>();
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");
154 provided.add(intf.getProvider());
161 * Starts the subsystems.
163 * @param aRequiredServices
164 * Services that are available from other systems that have been
167 private void startImpl() {
168 LOG.info("Starting '" + getQualifiedName() + "'");
169 List<ProvidedInterface> allProvided = new ArrayList<ProvidedInterface>();
171 // Add the provides of all externally required interfaces to the list of
174 for (RequiredInterface required : getRequiredInterfaces()) {
175 allProvided.add(required.getProvider());
178 List<Component> started = new ArrayList<Component>();
179 for (Component system : _systems) {
181 // Check if all required services are already provided by
184 RequiredInterface[] required = system.getRequiredInterfaces();
186 for (RequiredInterface descriptor : required) {
187 ProvidedInterface[] filtered = filterProvidedServices(
188 descriptor, allProvided);
190 if (filtered.length == 0) {
191 throw new SystemAssemblyException(
194 + "' required by system '"
196 + "' is not provided by systems that are started earlier");
198 if (filtered.length > 1) {
199 throw new SystemAssemblyException(
202 + "' required by system '"
204 + "' matches multiple services provided by other systems: "
205 + Arrays.asList(filtered));
207 descriptor.setProvider(filtered[0]);
210 // Start the service.
214 // add all provided services
215 ProvidedInterface[] provided = system.getProvidedInterfaces();
216 allProvided.addAll(Arrays.asList(provided));
217 } catch (SystemAssemblyException e) {
219 } catch (RuntimeException e) {
220 LOG.error(getQualifiedName() + ": could not start '"
221 + system.getQualifiedName() + "'", e);
222 // an exception occurred, stop the successfully started
224 for (int i = started.size() - 1; i >= 0; i--) {
226 started.get(i).stop();
227 } catch (Throwable t) {
228 LOG.error(getQualifiedName() + ": error stopping "
229 + started.get(i).getQualifiedName());
239 protected void doStop() {
240 for (int i = _systems.length - 1; i >= 0; i--) {
245 private void info(String aMsg) {
246 LOG.info(getName() + ": " + aMsg);