X-Git-Url: http://wamblee.org/gitweb/?a=blobdiff_plain;f=system%2Fgeneral%2Fsrc%2Fmain%2Fjava%2Forg%2Fwamblee%2Fsystem%2Fcore%2FContainer.java;h=1a3e9911cccc80b0ae47a3b3890711bb5bcaeda5;hb=652b3a33aeb0d362784c1926c1931ca865aaef9e;hp=1eaebf432a0e1c239189e621e3d0a35b9275f1ed;hpb=96e3c00dcb2e4297b54359e7c5ea109d9b9dd3f1;p=utils 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 1eaebf43..1a3e9911 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 @@ -25,6 +25,9 @@ import java.util.Set; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.wamblee.collections.CollectionFilter; +import org.wamblee.conditions.Condition; +import org.wamblee.general.Pair; /** * Container consisting of multiple components. @@ -37,29 +40,22 @@ public class Container extends AbstractComponent { private List _components; private Set _componentNames; + private CompositeInterfaceRestriction _restriction; private boolean _sealed; - - public static RequiredInterface[] filterRequiredServices( - ProvidedInterface aProvided, - Collection aDescriptors) { - List required = new ArrayList(); - for (RequiredInterface descriptor : aDescriptors) { - if (descriptor.implementedBy(aProvided)) { - required.add(descriptor); - } - } - return required.toArray(new RequiredInterface[0]); - } - - public static ProvidedInterface[] filterProvidedServices( - RequiredInterface aRequired, Collection aProvided) { - List provided = new ArrayList(); - for (ProvidedInterface descriptor : aProvided) { - if (aRequired.implementedBy(descriptor)) { - provided.add(descriptor); + + static ProvidedInterface[] filterProvidedServices( + Component aClient, RequiredInterface aRequired, Collection> aProvided, + InterfaceRestriction aRestriction) { + List result = new ArrayList(); + for (Pair descriptor : aProvided) { + ProvidedInterface provided = descriptor.getFirst(); + Component server = descriptor.getSecond(); + if (aRequired.implementedBy(provided) && + !aRestriction.isViolated(aClient, aRequired, server, provided)) { + result.add(provided); } } - return provided.toArray(new ProvidedInterface[0]); + return result.toArray(new ProvidedInterface[0]); } /** @@ -80,6 +76,7 @@ public class Container extends AbstractComponent { _components = new ArrayList(); _componentNames = new HashSet(); + _restriction = new CompositeInterfaceRestriction(); _sealed = false; for (Component component : aComponents) { addComponent(component); @@ -108,6 +105,18 @@ public class Container extends AbstractComponent { aComponent.addContext(getQualifiedName()); return this; } + + /** + * Adds an interface restriction for explicitly configuring the + * relations between components. + * @param aRestriction Restriction to add. + * @return Reference to this to allow call chaining. + */ + public Container addRestriction(InterfaceRestriction aRestriction) { + checkSealed(); + _restriction.add(aRestriction); + return this; + } @Override public Container addProvidedInterface(ProvidedInterface aProvided) { @@ -122,6 +131,14 @@ public class Container extends AbstractComponent { super.addRequiredInterface(aRequired); return this; } + + @Override + public void addContext(String aContext) { + super.addContext(aContext); + for (Component component: _components) { + component.addContext(aContext); + } + } /** * Validates the components together to check that there are no required @@ -176,38 +193,42 @@ public class Container extends AbstractComponent { } private void validateProvidedInterfacesArePresent() { - List provided = new ArrayList(); - for (Component component : _components) { - provided.addAll(Arrays.asList(component.getProvidedInterfaces())); - } for (ProvidedInterface service : getProvidedInterfaces()) { - // TODO provided interfaces by components 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 its components"); - } + findProvidedInterface(service); } } - + /** - * Starts the container. After the container is started, the container - * becomes sealed meaning that no further components, required or provided - * interfaces may be added. - * - * @return Scope. + * Finds the component and provided interface that matches a provided interface of this + * container. + * @param aProvided Interface to provide externally. + * @return Pair of component and provided interface + * @throws SystemAssemblyException In case there are multiple matches or no match at all. */ - public Scope start() { - checkSealed(); - validate(); - Scope scope = super.start(new DefaultScope(new ProvidedInterface[0])); - seal(); - return scope; + private Pair findProvidedInterface(ProvidedInterface aProvided) { + List> result = + new ArrayList>(); + for (Component component: _components) { + for (ProvidedInterface provided: component.getProvidedInterfaces()) { + if ( aProvided.equals(provided) ) { + result.add(new Pair(component, provided)); + } + } + } + if ( result.size() == 0) { + throw new SystemAssemblyException(getQualifiedName() + ": Service '" + + aProvided + + "' is not provided by any of its components"); + } + if ( result.size() > 1) { + throw new SystemAssemblyException(getQualifiedName() + ": Service '" + + aProvided + + "' is provided by multiple components: " + result); + } + return result.get(0); } + /** * Seal the container, meaning that no further components or interfaces may * be added. @@ -224,19 +245,41 @@ public class Container extends AbstractComponent { public boolean isSealed() { return _sealed; } + + /** + * Utility method to start with an empty external scope. This is useful for + * top-level containers which are not part of another container. + * @return Scope. + */ + public Scope start() { + Scope scope = new DefaultScope(getProvidedInterfaces()); + return super.start(scope); + } @Override protected Scope doStart(Scope aExternalScope) { - return doStartOptionalDryRun(aExternalScope, false); + checkSealed(); + validate(); + Scope scope = new DefaultScope(getProvidedInterfaces(), aExternalScope); + doStartOptionalDryRun(scope, false); + exposeProvidedInterfaces(aExternalScope, scope); + seal(); + return scope; } - private Scope doStartOptionalDryRun(Scope aExternalScope, boolean aDryRun) { - LOG.info("Starting '" + getQualifiedName() + "'"); - - Scope scope = new DefaultScope(getProvidedInterfaces(), aExternalScope); + private void exposeProvidedInterfaces(Scope aExternalScope, Scope aInternalScope) { + for (ProvidedInterface intf: getProvidedInterfaces()) { + Pair found = findProvidedInterface(intf); + Object svc = aInternalScope.getInterfaceImplementation(found.getSecond(), Object.class); + addInterface(intf, svc, aExternalScope); + } + } - List allProvided = new ArrayList(); + private void doStartOptionalDryRun(Scope aScope, boolean aDryRun) { + LOG.info("Starting '" + getQualifiedName() + "'"); + List> allProvided = new ArrayList>(); + addProvidersOfRequiredInterfaces(allProvided); List started = new ArrayList(); @@ -247,36 +290,37 @@ public class Container extends AbstractComponent { // Start the service. if (!aDryRun) { - Object runtime = component.start(scope); - scope.addRuntime(component, runtime); + Object runtime = component.start(aScope); + aScope.addRuntime(component, runtime); started.add(component); } // add all provided services ProvidedInterface[] provided = component .getProvidedInterfaces(); - allProvided.addAll(Arrays.asList(provided)); + for (ProvidedInterface prov: provided) { + allProvided.add(new Pair(prov, component)); + } } catch (SystemAssemblyException e) { throw e; } catch (RuntimeException e) { LOG.error(getQualifiedName() + ": could not start '" + component.getQualifiedName() + "'", e); - stopAlreadyStartedComponents(started, scope); + stopAlreadyStartedComponents(started, aScope); throw e; } } - return scope; } private void addProvidersOfRequiredInterfaces( - List allProvided) { + List> allProvided) { // 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) { - allProvided.add(provider); + allProvided.add(new Pair(provider, null)); } else { if (!intf.isOptional()) { throw new SystemAssemblyException(getQualifiedName() @@ -314,15 +358,15 @@ public class Container extends AbstractComponent { * interfaces. */ private void initializeProvidersForRequiredInterfaces( - List aAllProvided, Component aComponent, + List> aAllProvided, Component aComponent, boolean aValidateOnly) { // Check if all required services are already provided by // earlier // systems. for (RequiredInterface descriptor : aComponent.getRequiredInterfaces()) { - ProvidedInterface[] filtered = filterProvidedServices(descriptor, - aAllProvided); + ProvidedInterface[] filtered = filterProvidedServices(aComponent, descriptor, + aAllProvided, _restriction); if (filtered.length == 1) { if (!aValidateOnly) { descriptor.setProvider(filtered[0]);