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 * Composite system consisting of multiple subsystems.
30 * @author Erik Brakkee
32 public class Container extends AbstractComponent {
34 private static final Log LOG = LogFactory.getLog(Container.class);
36 private Component[] _systems;
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 composite system.
71 * Provided services of the system.
73 * Required services by the system.
75 public Container(String aName, Component[] aSystems,
76 ProvidedInterface[] aProvided, RequiredInterface[] aRequired) {
77 super(aName, aProvided, aRequired);
79 for (Component component : aSystems) {
80 component.addContext(getQualifiedName());
86 * Validates the subsystems together to check that there are no required
87 * services not in the required list and no services in the provided list
88 * that cannot be provided. Also logs a warning in case of superfluous
91 private void validate() {
92 List<ProvidedInterface> provided = new ArrayList<ProvidedInterface>();
93 for (Component system : _systems) {
94 provided.addAll(Arrays.asList(system.getProvidedInterfaces()));
97 List<RequiredInterface> required = new ArrayList<RequiredInterface>();
98 for (Component system : _systems) {
99 required.addAll(Arrays.asList(system.getRequiredInterfaces()));
102 validateProvidedInterfaces(provided);
104 validateRequiredInterfaces(required);
106 List<RequiredInterface> reallyRequired = validateRequiredProvidedMatch(
109 String missingRequired = "";
110 for (RequiredInterface service : reallyRequired) {
111 missingRequired += service + "\n";
113 if (missingRequired.length() > 0) {
114 throw new SystemAssemblyException(getName()
115 + ": missing required services\n" + missingRequired);
119 private List<RequiredInterface> validateRequiredProvidedMatch(
120 List<ProvidedInterface> provided, List<RequiredInterface> required) {
121 List<RequiredInterface> reallyRequired = new ArrayList<RequiredInterface>(
123 // Compute all required interfaces that are not provided
125 for (ProvidedInterface service : provided) {
126 List<RequiredInterface> fulfilled = Arrays
127 .asList(filterRequiredServices(service, reallyRequired));
128 reallyRequired.removeAll(fulfilled);
130 // Now remove all optional interfaces from the list.
131 for (Iterator<RequiredInterface> i =
132 reallyRequired.iterator(); i.hasNext(); ) {
133 RequiredInterface req = i.next();
134 if ( req.isOptional() ) {
138 // Now the remaining interfaces should be covered by the required
140 reallyRequired.removeAll(Arrays.asList(getRequiredInterfaces()));
141 return reallyRequired;
144 private void validateRequiredInterfaces(List<RequiredInterface> required) {
145 for (RequiredInterface service : getRequiredInterfaces()) {
146 // TODO required services by the subsystem could be
147 // subclasses or implementations of the requirements
148 // of the contained systems. The code below assumes
150 if (!(required.contains(service))) {
153 + "' indicated as required is not actually required by any of the subsystems");
155 // Check for the case that the externally required service
156 // is optional whereas the internally required service is
158 if ( service.isOptional()) {
159 for (RequiredInterface intf: required) {
160 if ( intf.equals(service) && !intf.isOptional()) {
161 // TODO indicate which subsystem this is.
162 warn("Required service '" + service + "' indicated as optional is mandatory by one of its subsystems (" + getClients(intf) + ", " + intf + "), this can lead to problems when the system is started and the service is not there.");
169 private void validateProvidedInterfaces(List<ProvidedInterface> provided) {
170 for (ProvidedInterface service : getProvidedInterfaces()) {
171 // TODO provided interfaces by subsystems could be
172 // provide subclasses or implementations of the
173 // provided interfaces of the container.
174 // The code below assumes an exact match.
175 if (!(provided.contains(service))) {
176 throw new SystemAssemblyException(getName() + ": Service '"
178 + "' is not provided by any of the subsystems");
184 protected void doStart() {
185 LOG.info("Starting '" + getQualifiedName() + "'");
186 List<ProvidedInterface> allProvided = new ArrayList<ProvidedInterface>();
188 // all interfaces from the required list of this container are
189 // provided to the components inside it.
190 RequiredInterface[] required = getRequiredInterfaces();
191 for (RequiredInterface intf : required) {
192 ProvidedInterface provider = intf.getProvider();
193 if (provider != null ) {
194 allProvided.add(provider);
196 if ( !intf.isOptional()) {
197 throw new SystemAssemblyException(getQualifiedName()
198 + ": required interface '" + intf + "' is not provided");
203 List<Component> started = new ArrayList<Component>();
204 for (Component system : _systems) {
206 // Check if all required services are already provided by
210 for (RequiredInterface descriptor : system.getRequiredInterfaces()) {
211 ProvidedInterface[] filtered = filterProvidedServices(
212 descriptor, allProvided);
213 if ( filtered.length == 1 ) {
214 descriptor.setProvider(filtered[0]);
215 } else if ( filtered.length > 1 ) {
216 throw new SystemAssemblyException(
219 + "' required by system '"
221 + "' matches multiple services provided by other systems: "
222 + Arrays.asList(filtered));
224 // filtered.length == 0
225 if ( !descriptor.isOptional()) {
226 throw new SystemAssemblyException(
229 + "' required by system '"
231 + "' is not provided by systems that are started earlier");
236 // Start the service.
240 // add all provided services
241 ProvidedInterface[] provided = system.getProvidedInterfaces();
242 allProvided.addAll(Arrays.asList(provided));
243 } catch (SystemAssemblyException e) {
245 } catch (RuntimeException e) {
246 LOG.error(getQualifiedName() + ": could not start '"
247 + system.getQualifiedName() + "'", e);
248 // an exception occurred, stop the successfully started
250 for (int i = started.size() - 1; i >= 0; i--) {
252 started.get(i).stop();
253 } catch (Throwable t) {
254 LOG.error(getQualifiedName() + ": error stopping "
255 + started.get(i).getQualifiedName());
265 protected void doStop() {
266 for (int i = _systems.length - 1; i >= 0; i--) {
271 private void info(String aMsg) {
272 LOG.info(getQualifiedName() + ": " + aMsg);
275 private void warn(String aMsg) {
276 LOG.warn(getQualifiedName() + ": " + aMsg);
279 private List<Component> getClients(RequiredInterface aRequirement) {
280 List<Component> clients = new ArrayList<Component>();
281 for (Component component: _systems) {
282 for (RequiredInterface required: component.getRequiredInterfaces()) {
283 if ( required.equals(aRequirement) && required.isOptional() == aRequirement.isOptional()) {
284 clients.add(component);