From: Erik Brakkee Date: Fri, 11 Apr 2008 20:22:46 +0000 (+0000) Subject: Implemented optinality of interfaces. X-Git-Tag: wamblee-utils-0.7~787 X-Git-Url: http://wamblee.org/gitweb/?a=commitdiff_plain;h=971e905b3afccdf5b17e9303cee353a33a6db030;p=utils Implemented optinality of interfaces. --- diff --git a/system/general/src/main/java/org/wamblee/system/core/AbstractComponent.java b/system/general/src/main/java/org/wamblee/system/core/AbstractComponent.java index 9bad650d..b202f285 100644 --- a/system/general/src/main/java/org/wamblee/system/core/AbstractComponent.java +++ b/system/general/src/main/java/org/wamblee/system/core/AbstractComponent.java @@ -164,7 +164,7 @@ public abstract class AbstractComponent implements Component { @Override public String toString() { - return _name; + return getQualifiedName(); } } diff --git a/system/general/src/main/java/org/wamblee/system/core/Container.java b/system/general/src/main/java/org/wamblee/system/core/Container.java index 8436ebca..81dcb6a2 100644 --- a/system/general/src/main/java/org/wamblee/system/core/Container.java +++ b/system/general/src/main/java/org/wamblee/system/core/Container.java @@ -18,6 +18,7 @@ package org.wamblee.system.core; 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; @@ -78,7 +79,7 @@ public class Container extends AbstractComponent { for (Component component : aSystems) { component.addContext(getQualifiedName()); } - validate(aRequired); + validate(); } /** @@ -87,7 +88,7 @@ public class Container extends AbstractComponent { * that cannot be provided. Also logs a warning in case of superfluous * requirements. */ - private void validate(RequiredInterface[] aRequired) { + private void validate() { List provided = new ArrayList(); for (Component system : _systems) { provided.addAll(Arrays.asList(system.getProvidedInterfaces())); @@ -98,79 +99,105 @@ public class Container extends AbstractComponent { 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 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 validateRequiredProvidedMatch( + List provided, List required) { List reallyRequired = new ArrayList( required); // Compute all required interfaces that are not provided + for (ProvidedInterface service : provided) { List fulfilled = Arrays .asList(filterRequiredServices(service, reallyRequired)); reallyRequired.removeAll(fulfilled); } + // Now remove all optional interfaces from the list. + for (Iterator 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 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 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 provided = new ArrayList(); + LOG.info("Starting '" + getQualifiedName() + "'"); + List allProvided = new ArrayList(); // 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 allProvided = new ArrayList(); - - // Add the provides of all externally required interfaces to the list of - // available - // interfaces - for (RequiredInterface required : getRequiredInterfaces()) { - allProvided.add(required.getProvider()); } List started = new ArrayList(); @@ -179,21 +206,13 @@ public class Container extends AbstractComponent { // 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 @@ -201,8 +220,17 @@ public class Container extends AbstractComponent { + 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. @@ -241,7 +269,22 @@ public class Container extends AbstractComponent { } private void info(String aMsg) { - LOG.info(getName() + ": " + aMsg); + LOG.info(getQualifiedName() + ": " + aMsg); + } + + private void warn(String aMsg) { + LOG.warn(getQualifiedName() + ": " + aMsg); } + private List getClients(RequiredInterface aRequirement) { + List clients = new ArrayList(); + for (Component component: _systems) { + for (RequiredInterface required: component.getRequiredInterfaces()) { + if ( required.equals(aRequirement) && required.isOptional() == aRequirement.isOptional()) { + clients.add(component); + } + } + } + return clients; + } } diff --git a/system/general/src/main/java/org/wamblee/system/core/DefaultRequiredInterface.java b/system/general/src/main/java/org/wamblee/system/core/DefaultRequiredInterface.java index 32276a25..e028342e 100644 --- a/system/general/src/main/java/org/wamblee/system/core/DefaultRequiredInterface.java +++ b/system/general/src/main/java/org/wamblee/system/core/DefaultRequiredInterface.java @@ -20,6 +20,7 @@ import java.util.Arrays; public class DefaultRequiredInterface implements RequiredInterface { private String _name; + private boolean _optional; private Class[] _required; private ProvidedInterface _provider; @@ -28,7 +29,17 @@ public class DefaultRequiredInterface implements RequiredInterface { } public DefaultRequiredInterface(String aName, Class[] aInterfaces) { + this(aName, aInterfaces, false); + } + + public DefaultRequiredInterface(String aName, Class aInterface, boolean aIsOptional) { + this(aName, new Class[] { aInterface }, aIsOptional ); + } + + + public DefaultRequiredInterface(String aName, Class[] aInterfaces, boolean aIsOptional) { _name = aName; + _optional = aIsOptional; _required = aInterfaces; } @@ -37,6 +48,11 @@ public class DefaultRequiredInterface implements RequiredInterface { return _name; } + @Override + public boolean isOptional() { + return _optional; + } + @Override public boolean implementedBy(ProvidedInterface aDescriptor) { Class[] provided = aDescriptor.getInterfaceTypes(); diff --git a/system/general/src/main/java/org/wamblee/system/core/RequiredInterface.java b/system/general/src/main/java/org/wamblee/system/core/RequiredInterface.java index ce8d084c..bcd2635d 100644 --- a/system/general/src/main/java/org/wamblee/system/core/RequiredInterface.java +++ b/system/general/src/main/java/org/wamblee/system/core/RequiredInterface.java @@ -20,7 +20,13 @@ public interface RequiredInterface { /** * Name for the interface. */ - public String getName(); + String getName(); + + /** + * @return True iff the required interface is optional. + */ + boolean isOptional(); + /** * Checks if the service is provided by a given provided interface. diff --git a/system/general/src/test/java/org/wamblee/system/core/Application.java b/system/general/src/test/java/org/wamblee/system/core/Application.java index 938f2bd1..16d1f36f 100644 --- a/system/general/src/test/java/org/wamblee/system/core/Application.java +++ b/system/general/src/test/java/org/wamblee/system/core/Application.java @@ -24,18 +24,22 @@ import org.wamblee.system.core.RequiredInterface; import org.wamblee.test.EventTracker; public class Application extends AbstractComponent { - private static RequiredInterface[] required() { + public static RequiredInterface[] required(boolean aOptional) { return new RequiredInterface[] { - new DefaultRequiredInterface("datasource", DataSource.class), - new DefaultRequiredInterface("integer", Integer.class) + new DefaultRequiredInterface("datasource", DataSource.class, aOptional), + new DefaultRequiredInterface("integer", Integer.class, aOptional) }; } private EventTracker _tracker; public Application() { - super("application", new ProvidedInterface[0], required()); + super("application", new ProvidedInterface[0], required(false)); + } + + public Application(boolean aIsOptinal) { + super("application", new ProvidedInterface[0], required(true)); } public Application(EventTracker aTracker) { diff --git a/system/general/src/test/java/org/wamblee/system/core/ContainerTest.java b/system/general/src/test/java/org/wamblee/system/core/ContainerTest.java index 5a7fa90b..27a1937f 100644 --- a/system/general/src/test/java/org/wamblee/system/core/ContainerTest.java +++ b/system/general/src/test/java/org/wamblee/system/core/ContainerTest.java @@ -338,4 +338,50 @@ public class ContainerTest extends TestCase { } 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()); + } }