import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.Iterator;
import java.util.List;
import org.apache.commons.logging.Log;
for (Component component : aSystems) {
component.addContext(getQualifiedName());
}
- validate(aRequired);
+ validate();
}
/**
* that cannot be provided. Also logs a warning in case of superfluous
* requirements.
*/
- private void validate(RequiredInterface[] aRequired) {
+ private void validate() {
List<ProvidedInterface> provided = new ArrayList<ProvidedInterface>();
for (Component system : _systems) {
provided.addAll(Arrays.asList(system.getProvidedInterfaces()));
required.addAll(Arrays.asList(system.getRequiredInterfaces()));
}
- for (ProvidedInterface service : getProvidedInterfaces()) {
- if (!(provided.contains(service))) {
- throw new SystemAssemblyException(getName() + ": Service '"
- + service
- + "' is not provided by any of the subsystems");
- }
- }
+ validateProvidedInterfaces(provided);
- for (RequiredInterface service : getRequiredInterfaces()) {
- if (!(required.contains(service))) {
- info("Service '"
- + service
- + "' indicated as required is not actually required by any of the subsystems");
- }
+ validateRequiredInterfaces(required);
+
+ List<RequiredInterface> reallyRequired = validateRequiredProvidedMatch(
+ provided, required);
+
+ String missingRequired = "";
+ for (RequiredInterface service : reallyRequired) {
+ missingRequired += service + "\n";
+ }
+ if (missingRequired.length() > 0) {
+ throw new SystemAssemblyException(getName()
+ + ": missing required services\n" + missingRequired);
}
+ }
+ private List<RequiredInterface> validateRequiredProvidedMatch(
+ List<ProvidedInterface> provided, List<RequiredInterface> required) {
List<RequiredInterface> reallyRequired = new ArrayList<RequiredInterface>(
required);
// Compute all required interfaces that are not provided
+
for (ProvidedInterface service : provided) {
List<RequiredInterface> fulfilled = Arrays
.asList(filterRequiredServices(service, reallyRequired));
reallyRequired.removeAll(fulfilled);
}
+ // Now remove all optional interfaces from the list.
+ for (Iterator<RequiredInterface> i =
+ reallyRequired.iterator(); i.hasNext(); ) {
+ RequiredInterface req = i.next();
+ if ( req.isOptional() ) {
+ i.remove();
+ }
+ }
// Now the remaining interfaces should be covered by the required
// list.
- reallyRequired.removeAll(Arrays.asList(aRequired));
+ reallyRequired.removeAll(Arrays.asList(getRequiredInterfaces()));
+ return reallyRequired;
+ }
- String missingRequired = "";
- for (RequiredInterface service : reallyRequired) {
- missingRequired += service + "\n";
+ private void validateRequiredInterfaces(List<RequiredInterface> required) {
+ for (RequiredInterface service : getRequiredInterfaces()) {
+ // TODO required services by the subsystem could be
+ // subclasses or implementations of the requirements
+ // of the contained systems. The code below assumes
+ // an exact match.
+ if (!(required.contains(service))) {
+ info("Service '"
+ + service
+ + "' indicated as required is not actually required by any of the subsystems");
+ }
+ // Check for the case that the externally required service
+ // is optional whereas the internally required service is
+ // mandatory.
+ if ( service.isOptional()) {
+ for (RequiredInterface intf: required) {
+ if ( intf.equals(service) && !intf.isOptional()) {
+ // TODO indicate which subsystem this is.
+ 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.");
+ }
+ }
+ }
}
- if (missingRequired.length() > 0) {
- throw new SystemAssemblyException(getName()
- + ": missing required services\n" + missingRequired);
+ }
+
+ private void validateProvidedInterfaces(List<ProvidedInterface> provided) {
+ for (ProvidedInterface service : getProvidedInterfaces()) {
+ // TODO provided interfaces by subsystems could be
+ // provide subclasses or implementations of the
+ // provided interfaces of the container.
+ // The code below assumes an exact match.
+ if (!(provided.contains(service))) {
+ throw new SystemAssemblyException(getName() + ": Service '"
+ + service
+ + "' is not provided by any of the subsystems");
+ }
}
}
@Override
protected void doStart() {
- List<ProvidedInterface> provided = new ArrayList<ProvidedInterface>();
+ LOG.info("Starting '" + getQualifiedName() + "'");
+ List<ProvidedInterface> allProvided = new ArrayList<ProvidedInterface>();
// all interfaces from the required list of this container are
// provided to the components inside it.
RequiredInterface[] required = getRequiredInterfaces();
for (RequiredInterface intf : required) {
ProvidedInterface provider = intf.getProvider();
- if (provider == null) {
- throw new SystemAssemblyException(getQualifiedName()
- + ": required interface '" + intf + "' is not provided");
+ if (provider != null ) {
+ allProvided.add(provider);
+ } else {
+ if ( !intf.isOptional()) {
+ throw new SystemAssemblyException(getQualifiedName()
+ + ": required interface '" + intf + "' is not provided");
+ }
}
- provided.add(intf.getProvider());
- }
-
- startImpl();
- }
-
- /**
- * Starts the subsystems.
- *
- * @param aRequiredServices
- * Services that are available from other systems that have been
- * started before.
- */
- private void startImpl() {
- LOG.info("Starting '" + getQualifiedName() + "'");
- List<ProvidedInterface> allProvided = new ArrayList<ProvidedInterface>();
-
- // Add the provides of all externally required interfaces to the list of
- // available
- // interfaces
- for (RequiredInterface required : getRequiredInterfaces()) {
- allProvided.add(required.getProvider());
}
List<Component> started = new ArrayList<Component>();
// Check if all required services are already provided by
// earlier
// systems.
- RequiredInterface[] required = system.getRequiredInterfaces();
- for (RequiredInterface descriptor : required) {
+ for (RequiredInterface descriptor : system.getRequiredInterfaces()) {
ProvidedInterface[] filtered = filterProvidedServices(
descriptor, allProvided);
-
- if (filtered.length == 0) {
- throw new SystemAssemblyException(
- "Service '"
- + descriptor
- + "' required by system '"
- + system
- + "' is not provided by systems that are started earlier");
- }
- if (filtered.length > 1) {
+ if ( filtered.length == 1 ) {
+ descriptor.setProvider(filtered[0]);
+ } else if ( filtered.length > 1 ) {
throw new SystemAssemblyException(
"Service '"
+ descriptor
+ system
+ "' matches multiple services provided by other systems: "
+ Arrays.asList(filtered));
+ } else {
+ // filtered.length == 0
+ if ( !descriptor.isOptional()) {
+ throw new SystemAssemblyException(
+ "Service '"
+ + descriptor
+ + "' required by system '"
+ + system
+ + "' is not provided by systems that are started earlier");
+ }
}
- descriptor.setProvider(filtered[0]);
}
// Start the service.
}
private void info(String aMsg) {
- LOG.info(getName() + ": " + aMsg);
+ LOG.info(getQualifiedName() + ": " + aMsg);
+ }
+
+ private void warn(String aMsg) {
+ LOG.warn(getQualifiedName() + ": " + aMsg);
}
+ private List<Component> getClients(RequiredInterface aRequirement) {
+ List<Component> clients = new ArrayList<Component>();
+ for (Component component: _systems) {
+ for (RequiredInterface required: component.getRequiredInterfaces()) {
+ if ( required.equals(aRequirement) && required.isOptional() == aRequirement.isOptional()) {
+ clients.add(component);
+ }
+ }
+ }
+ return clients;
+ }
}
}
fail();
}
+
+ public void testOptionalRequiredInterfaceProvidedOptionalInternal() {
+ Application application = new Application(true);
+ Container container = new Container("top", new Component[] { application },
+ new ProvidedInterface[0], Application.required(true));
+ Environment env = new Environment();
+ container.getRequiredInterfaces()[0].setProvider(
+ env.getProvidedInterfaces()[0]);
+ container.getRequiredInterfaces()[1].setProvider(
+ env.getProvidedInterfaces()[1]);
+ container.start();
+ assertSame(env.getProvidedInterfaces()[0], container.getRequiredInterfaces()[0].getProvider());
+ assertSame(env.getProvidedInterfaces()[1], container.getRequiredInterfaces()[1].getProvider());
+ assertSame(env.getProvidedInterfaces()[0], application.getRequiredInterfaces()[0].getProvider());
+ assertSame(env.getProvidedInterfaces()[1], application.getRequiredInterfaces()[1].getProvider());
+ }
+
+ public void testOptionalRequiredInterfaceNotProvidedOptionalInternal() {
+ Application application = new Application(true);
+ Container container = new Container("top", new Component[] { application },
+ new ProvidedInterface[0], Application.required(true));
+ Environment env = new Environment();
+ container.getRequiredInterfaces()[0].setProvider(
+ env.getProvidedInterfaces()[0]);
+ container.start();
+ assertSame(env.getProvidedInterfaces()[0], container.getRequiredInterfaces()[0].getProvider());
+ assertNull(container.getRequiredInterfaces()[1].getProvider());
+ assertSame(env.getProvidedInterfaces()[0], application.getRequiredInterfaces()[0].getProvider());
+ assertNull(application.getRequiredInterfaces()[1].getProvider());
+ }
+
+ public void testOptionalRequiredInterfaceProvidedMandatoryInternal() {
+ Application application = new Application();
+ Container container = new Container("top", new Component[] { application },
+ new ProvidedInterface[0], Application.required(true));
+ Environment env = new Environment();
+ container.getRequiredInterfaces()[0].setProvider(
+ env.getProvidedInterfaces()[0]);
+ container.getRequiredInterfaces()[1].setProvider(
+ env.getProvidedInterfaces()[1]);
+ container.start();
+ assertSame(env.getProvidedInterfaces()[0], container.getRequiredInterfaces()[0].getProvider());
+ assertSame(env.getProvidedInterfaces()[1], container.getRequiredInterfaces()[1].getProvider());
+ assertSame(env.getProvidedInterfaces()[0], application.getRequiredInterfaces()[0].getProvider());
+ assertSame(env.getProvidedInterfaces()[1], application.getRequiredInterfaces()[1].getProvider());
+ }
}