From b522989a5f4a984c2c3ad0780886ac7c7b044bc7 Mon Sep 17 00:00:00 2001 From: erik Date: Fri, 16 May 2008 13:08:27 +0000 Subject: [PATCH] Now basing the implementation on a component graph. --- .../system/adapters/DefaultContainer.java | 2 +- .../CompositeInterfaceRestriction.java | 6 +- .../wamblee/system/container/Container.java | 270 +++++++++++ .../DefaultInterfaceRestriction.java | 6 +- .../InterfaceRestriction.java | 6 +- .../org/wamblee/system/core/Container.java | 448 ------------------ .../org/wamblee/system/graph/DefaultEdge.java | 5 + .../component/ApplyRestrictionsVisitor.java | 55 +++ .../CheckExternallyProvidedVisitor.java | 72 +++ .../CheckExternallyRequiredVisitor.java | 53 +++ ...ckRequiredProvidedMultiplicityVisitor.java | 86 ++++ .../CheckStartupDependenciesVisitor.java | 57 +++ .../graph/component/ComponentGraph.java | 119 +++++ .../system/graph/component/ComponentNode.java | 37 ++ .../ExternalProvidedInterfaceNode.java | 37 ++ .../ExternalRequiredInterfaceNode.java | 42 ++ .../system/graph/component/LinkVisitor.java | 68 +++ .../component/ProvidedInterfaceNode.java | 49 ++ .../component/RequiredInterfaceNode.java | 49 ++ .../RequiredProvidedEdgeFactory.java | 68 +++ .../system/adapters/ClassAdapterTest.java | 2 +- .../system/adapters/ObjectAdapterTest.java | 2 +- .../components/DatabaseComponentFactory.java | 2 +- .../{core => container}/Application.java | 9 +- .../CompositeInterfaceRestrictionTest.java | 14 +- .../{core => container}/ContainerTest.java | 66 +-- .../DefaultInterfaceRestrictionTest.java | 7 +- 27 files changed, 1114 insertions(+), 523 deletions(-) rename system/general/src/main/java/org/wamblee/system/{core => container}/CompositeInterfaceRestriction.java (92%) create mode 100644 system/general/src/main/java/org/wamblee/system/container/Container.java rename system/general/src/main/java/org/wamblee/system/{core => container}/DefaultInterfaceRestriction.java (94%) rename system/general/src/main/java/org/wamblee/system/{core => container}/InterfaceRestriction.java (87%) delete mode 100644 system/general/src/main/java/org/wamblee/system/core/Container.java create mode 100644 system/general/src/main/java/org/wamblee/system/graph/component/ApplyRestrictionsVisitor.java create mode 100644 system/general/src/main/java/org/wamblee/system/graph/component/CheckExternallyProvidedVisitor.java create mode 100644 system/general/src/main/java/org/wamblee/system/graph/component/CheckExternallyRequiredVisitor.java create mode 100644 system/general/src/main/java/org/wamblee/system/graph/component/CheckRequiredProvidedMultiplicityVisitor.java create mode 100644 system/general/src/main/java/org/wamblee/system/graph/component/CheckStartupDependenciesVisitor.java create mode 100644 system/general/src/main/java/org/wamblee/system/graph/component/ComponentGraph.java create mode 100644 system/general/src/main/java/org/wamblee/system/graph/component/ComponentNode.java create mode 100644 system/general/src/main/java/org/wamblee/system/graph/component/ExternalProvidedInterfaceNode.java create mode 100644 system/general/src/main/java/org/wamblee/system/graph/component/ExternalRequiredInterfaceNode.java create mode 100644 system/general/src/main/java/org/wamblee/system/graph/component/LinkVisitor.java create mode 100644 system/general/src/main/java/org/wamblee/system/graph/component/ProvidedInterfaceNode.java create mode 100644 system/general/src/main/java/org/wamblee/system/graph/component/RequiredInterfaceNode.java create mode 100644 system/general/src/main/java/org/wamblee/system/graph/component/RequiredProvidedEdgeFactory.java rename system/general/src/test/java/org/wamblee/system/{core => container}/Application.java (94%) rename system/general/src/test/java/org/wamblee/system/{core => container}/CompositeInterfaceRestrictionTest.java (93%) rename system/general/src/test/java/org/wamblee/system/{core => container}/ContainerTest.java (89%) rename system/general/src/test/java/org/wamblee/system/{core => container}/DefaultInterfaceRestrictionTest.java (98%) diff --git a/system/general/src/main/java/org/wamblee/system/adapters/DefaultContainer.java b/system/general/src/main/java/org/wamblee/system/adapters/DefaultContainer.java index c8e8f58a..7f630105 100644 --- a/system/general/src/main/java/org/wamblee/system/adapters/DefaultContainer.java +++ b/system/general/src/main/java/org/wamblee/system/adapters/DefaultContainer.java @@ -15,8 +15,8 @@ */ package org.wamblee.system.adapters; +import org.wamblee.system.container.Container; import org.wamblee.system.core.Component; -import org.wamblee.system.core.Container; import org.wamblee.system.core.ProvidedInterface; import org.wamblee.system.core.RequiredInterface; diff --git a/system/general/src/main/java/org/wamblee/system/core/CompositeInterfaceRestriction.java b/system/general/src/main/java/org/wamblee/system/container/CompositeInterfaceRestriction.java similarity index 92% rename from system/general/src/main/java/org/wamblee/system/core/CompositeInterfaceRestriction.java rename to system/general/src/main/java/org/wamblee/system/container/CompositeInterfaceRestriction.java index 29448c16..041c75d2 100644 --- a/system/general/src/main/java/org/wamblee/system/core/CompositeInterfaceRestriction.java +++ b/system/general/src/main/java/org/wamblee/system/container/CompositeInterfaceRestriction.java @@ -13,12 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.wamblee.system.core; +package org.wamblee.system.container; import java.util.ArrayList; import java.util.Collection; import java.util.List; +import org.wamblee.system.core.Component; +import org.wamblee.system.core.ProvidedInterface; +import org.wamblee.system.core.RequiredInterface; + /** * Composite restriction that wraps a number of other restrictions. The restriction is diff --git a/system/general/src/main/java/org/wamblee/system/container/Container.java b/system/general/src/main/java/org/wamblee/system/container/Container.java new file mode 100644 index 00000000..349e5978 --- /dev/null +++ b/system/general/src/main/java/org/wamblee/system/container/Container.java @@ -0,0 +1,270 @@ +/* + * Copyright 2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.wamblee.system.container; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wamblee.general.Pair; +import org.wamblee.system.core.AbstractComponent; +import org.wamblee.system.core.Component; +import org.wamblee.system.core.DefaultScope; +import org.wamblee.system.core.ProvidedInterface; +import org.wamblee.system.core.RequiredInterface; +import org.wamblee.system.core.Scope; +import org.wamblee.system.core.SystemAssemblyException; +import org.wamblee.system.graph.component.ComponentGraph; + +/** + * Container consisting of multiple components. + * + * @author Erik Brakkee + */ +public class Container extends AbstractComponent { + + private static final Log LOG = LogFactory.getLog(Container.class); + + private List _components; + private Set _componentNames; + private CompositeInterfaceRestriction _restriction; + private boolean _sealed; + + /** + * Constructs the container + * + * @param aName + * Name of the container + * @param aComponents + * Components. + * @param aProvided + * Provided services of the container + * @param aRequired + * Required services by the container. + */ + public Container(String aName, Component[] aComponents, + ProvidedInterface[] aProvided, RequiredInterface[] aRequired) { + super(aName, aProvided, aRequired); + _components = new ArrayList(); + + _componentNames = new HashSet(); + _restriction = new CompositeInterfaceRestriction(); + _sealed = false; + for (Component component : aComponents) { + addComponent(component); + } + } + + public Container(String aName) { + this(aName, new Component[0], new ProvidedInterface[0], + new RequiredInterface[0]); + } + + public Container addComponent(Component aComponent) { + checkSealed(); + if (aComponent.getContext() != null) { + throw new SystemAssemblyException( + "Inconsistent hierarchy, component '" + + aComponent.getName() + + "' is already part of another hierarchy"); + } + if (_componentNames.contains(aComponent.getName())) { + throw new SystemAssemblyException("Duplicate component '" + + aComponent.getName() + "'"); + } + _components.add(aComponent); + _componentNames.add(aComponent.getName()); + 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) { + checkSealed(); + super.addProvidedInterface(aProvided); + return this; + } + + @Override + public Container addRequiredInterface(RequiredInterface aRequired) { + checkSealed(); + 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 + * services not in the required list and no services in the provided list + * that cannot be provided. Also logs a warning in case of superfluous + * requirements. + * + * @throws SystemAssemblyException + * in case of any validation problems. + */ + public void validate() { + doStartOptionalDryRun(null, true); + } + + /** + * Seal the container, meaning that no further components or interfaces may + * be added. + */ + public void seal() { + _sealed = true; + } + + /** + * Checks if the container is sealed. + * + * @return True iff the container is sealed. + */ + 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) { + checkSealed(); + validate(); + Scope scope = new DefaultScope(getProvidedInterfaces(), aExternalScope); + ComponentGraph graph = doStartOptionalDryRun(scope, false); + exposeProvidedInterfaces(graph, aExternalScope, scope); + seal(); + return scope; + } + + private void exposeProvidedInterfaces(ComponentGraph aGraph, Scope aExternalScope, + Scope aInternalScope) { + for (Pair mapping: + aGraph.findExternalProvidedInterfaceMapping()) { + Object svc = aInternalScope.getInterfaceImplementation(mapping.getSecond(), Object.class); + addInterface(mapping.getFirst(), svc, aExternalScope); + } + } + + private ComponentGraph doStartOptionalDryRun(Scope aScope, boolean aDryRun) { + ComponentGraph graph = createComponentGraph(); + graph.validate(); + if (!aDryRun) { + graph.link(); + } + + LOG.info("Starting '" + getQualifiedName() + "'"); + + List started = new ArrayList(); + for (Component component : _components) { + try { + // Start the service. + if (!aDryRun) { + Object runtime = component.start(aScope); + aScope.addRuntime(component, runtime); + started.add(component); + } + } catch (SystemAssemblyException e) { + throw e; + } catch (RuntimeException e) { + LOG.error(getQualifiedName() + ": could not start '" + + component.getQualifiedName() + "'", e); + stopAlreadyStartedComponents(started, aScope); + throw e; + } + } + return graph; + } + + private ComponentGraph createComponentGraph() { + ComponentGraph graph = new ComponentGraph(); + for (RequiredInterface req : getRequiredInterfaces()) { + graph.addRequiredInterface(req); + } + for (Component comp : _components) { + graph.addComponent(comp); + } + for (ProvidedInterface prov: getProvidedInterfaces()) { + graph.addProvidedInterface(prov); + } + + graph.addRestriction(_restriction); + return graph; + } + + private void stopAlreadyStartedComponents(List aStarted, + Scope aScope) { + // an exception occurred, stop the successfully started + // components + for (int i = aStarted.size() - 1; i >= 0; i--) { + try { + Component component = aStarted.get(i); + aStarted.get(i).stop(aScope.getRuntime(component)); + } catch (Throwable t) { + LOG.error(getQualifiedName() + ": error stopping " + + aStarted.get(i).getQualifiedName()); + } + } + } + + @Override + protected void doStop(Scope aScope) { + for (int i = _components.size() - 1; i >= 0; i--) { + Component component = _components.get(i); + Object runtime = aScope.getRuntime(component); + component.stop(runtime); + } + } + + private void checkSealed() { + if (_sealed) { + throw new SystemAssemblyException("Container is sealed"); + } + } +} diff --git a/system/general/src/main/java/org/wamblee/system/core/DefaultInterfaceRestriction.java b/system/general/src/main/java/org/wamblee/system/container/DefaultInterfaceRestriction.java similarity index 94% rename from system/general/src/main/java/org/wamblee/system/core/DefaultInterfaceRestriction.java rename to system/general/src/main/java/org/wamblee/system/container/DefaultInterfaceRestriction.java index ece7b289..27066844 100644 --- a/system/general/src/main/java/org/wamblee/system/core/DefaultInterfaceRestriction.java +++ b/system/general/src/main/java/org/wamblee/system/container/DefaultInterfaceRestriction.java @@ -13,7 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.wamblee.system.core; +package org.wamblee.system.container; + +import org.wamblee.system.core.Component; +import org.wamblee.system.core.ProvidedInterface; +import org.wamblee.system.core.RequiredInterface; /** * An interface restriction on the required interface of a component. diff --git a/system/general/src/main/java/org/wamblee/system/core/InterfaceRestriction.java b/system/general/src/main/java/org/wamblee/system/container/InterfaceRestriction.java similarity index 87% rename from system/general/src/main/java/org/wamblee/system/core/InterfaceRestriction.java rename to system/general/src/main/java/org/wamblee/system/container/InterfaceRestriction.java index d84af1cf..5f9749b4 100644 --- a/system/general/src/main/java/org/wamblee/system/core/InterfaceRestriction.java +++ b/system/general/src/main/java/org/wamblee/system/container/InterfaceRestriction.java @@ -13,7 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.wamblee.system.core; +package org.wamblee.system.container; + +import org.wamblee.system.core.Component; +import org.wamblee.system.core.ProvidedInterface; +import org.wamblee.system.core.RequiredInterface; /** * Interface restriction for restricting the linking of provided and required 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 deleted file mode 100644 index 1a3e9911..00000000 --- a/system/general/src/main/java/org/wamblee/system/core/Container.java +++ /dev/null @@ -1,448 +0,0 @@ -/* - * Copyright 2007 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.wamblee.system.core; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -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. - * - * @author Erik Brakkee - */ -public class Container extends AbstractComponent { - - private static final Log LOG = LogFactory.getLog(Container.class); - - private List _components; - private Set _componentNames; - private CompositeInterfaceRestriction _restriction; - private boolean _sealed; - - 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 result.toArray(new ProvidedInterface[0]); - } - - /** - * Constructs the container - * - * @param aName - * Name of the container - * @param aComponents - * Components. - * @param aProvided - * Provided services of the container - * @param aRequired - * Required services by the container. - */ - public Container(String aName, Component[] aComponents, - ProvidedInterface[] aProvided, RequiredInterface[] aRequired) { - super(aName, aProvided, aRequired); - _components = new ArrayList(); - - _componentNames = new HashSet(); - _restriction = new CompositeInterfaceRestriction(); - _sealed = false; - for (Component component : aComponents) { - addComponent(component); - } - } - - public Container(String aName) { - this(aName, new Component[0], new ProvidedInterface[0], - new RequiredInterface[0]); - } - - public Container addComponent(Component aComponent) { - checkSealed(); - if (aComponent.getContext() != null) { - throw new SystemAssemblyException( - "Inconsistent hierarchy, component '" - + aComponent.getName() - + "' is already part of another hierarchy"); - } - if (_componentNames.contains(aComponent.getName())) { - throw new SystemAssemblyException("Duplicate component '" - + aComponent.getName() + "'"); - } - _components.add(aComponent); - _componentNames.add(aComponent.getName()); - 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) { - checkSealed(); - super.addProvidedInterface(aProvided); - return this; - } - - @Override - public Container addRequiredInterface(RequiredInterface aRequired) { - checkSealed(); - 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 - * services not in the required list and no services in the provided list - * that cannot be provided. Also logs a warning in case of superfluous - * requirements. - * - * @throws SystemAssemblyException - * in case of any validation problems. - */ - public void validate() { - validateProvidedInterfacesArePresent(); - - validateRequiredInterfaces(); - - doStartOptionalDryRun(null, true); - } - - private void validateRequiredInterfaces() { - List required = new ArrayList(); - for (Component component : _components) { - required.addAll(Arrays.asList(component.getRequiredInterfaces())); - } - - for (RequiredInterface service : getRequiredInterfaces()) { - // TODO required interfaces by the component could be - // subclasses or implementations of the requirements - // of the contained components. 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 components"); - } - // 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()) { - warn("Required service '" - + service - + "' indicated as optional is mandatory by one of its components (" - + getClients(intf) - + ", " - + intf - + "), this can lead to problems when the container is started and the interface is not provided to the container."); - } - } - } - } - } - - private void validateProvidedInterfacesArePresent() { - for (ProvidedInterface service : getProvidedInterfaces()) { - findProvidedInterface(service); - } - } - - /** - * 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. - */ - 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. - */ - public void seal() { - _sealed = true; - } - - /** - * Checks if the container is sealed. - * - * @return True iff the container is sealed. - */ - 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) { - checkSealed(); - validate(); - Scope scope = new DefaultScope(getProvidedInterfaces(), aExternalScope); - doStartOptionalDryRun(scope, false); - exposeProvidedInterfaces(aExternalScope, scope); - seal(); - return scope; - } - - 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); - } - } - - private void doStartOptionalDryRun(Scope aScope, boolean aDryRun) { - LOG.info("Starting '" + getQualifiedName() + "'"); - - List> allProvided = new ArrayList>(); - - addProvidersOfRequiredInterfaces(allProvided); - - List started = new ArrayList(); - for (Component component : _components) { - try { - initializeProvidersForRequiredInterfaces(allProvided, - component, aDryRun); - - // Start the service. - if (!aDryRun) { - Object runtime = component.start(aScope); - aScope.addRuntime(component, runtime); - started.add(component); - } - - // add all provided services - ProvidedInterface[] provided = component - .getProvidedInterfaces(); - 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, aScope); - throw e; - } - } - } - - private void addProvidersOfRequiredInterfaces( - 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(new Pair(provider, null)); - } else { - if (!intf.isOptional()) { - throw new SystemAssemblyException(getQualifiedName() - + ": required interface '" + intf - + "' is not provided"); - } - } - } - } - - private void stopAlreadyStartedComponents(List aStarted, - Scope aScope) { - // an exception occurred, stop the successfully started - // components - for (int i = aStarted.size() - 1; i >= 0; i--) { - try { - Component component = aStarted.get(i); - aStarted.get(i).stop(aScope.getRuntime(component)); - } catch (Throwable t) { - LOG.error(getQualifiedName() + ": error stopping " - + aStarted.get(i).getQualifiedName()); - } - } - } - - /** - * Sets the provided interface or a component. - * - * @param aAllProvided - * All available provided interfaces. - * @param aComponent - * Component whose required interfaces we are looking at. - * @param aValidateOnly - * If true then the provider will not be set for required - * interfaces. - */ - private void initializeProvidersForRequiredInterfaces( - 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(aComponent, descriptor, - aAllProvided, _restriction); - if (filtered.length == 1) { - if (!aValidateOnly) { - descriptor.setProvider(filtered[0]); - } - } else if (filtered.length > 1) { - throw new SystemAssemblyException( - "Service '" - + descriptor - + "' required by system '" - + aComponent - + "' matches multiple services provided by other systems: " - + getServers(filtered)); - } else { - // filtered.length == 0 - if (!descriptor.isOptional()) { - throw new SystemAssemblyException( - "Service '" - + descriptor - + "' required by system '" - + aComponent - + "' is not provided by systems that are started earlier"); - } - } - } - } - - @Override - protected void doStop(Scope aScope) { - for (int i = _components.size() - 1; i >= 0; i--) { - Component component = _components.get(i); - Object runtime = aScope.getRuntime(component); - component.stop(runtime); - } - } - - private void info(String aMsg) { - LOG.info(getQualifiedName() + ": " + aMsg); - } - - private void warn(String aMsg) { - LOG.warn(getQualifiedName() + ": " + aMsg); - } - - private String getServers(ProvidedInterface[] aProvidedList) { - String result = ""; - for (ProvidedInterface provided : aProvidedList) { - result += "(components "; - for (Component component : _components) { - for (ProvidedInterface provided2 : component - .getProvidedInterfaces()) { - if (provided.equals(provided2)) { - result += component + " "; - } - } - } - result += ", interface " + provided + ")"; - } - return result; - } - - private List getClients(RequiredInterface aRequirement) { - List clients = new ArrayList(); - for (Component component : _components) { - for (RequiredInterface required : component.getRequiredInterfaces()) { - if (required.equals(aRequirement) - && required.isOptional() == aRequirement.isOptional()) { - clients.add(component); - } - } - } - return clients; - } - - private void checkSealed() { - if (_sealed) { - throw new SystemAssemblyException("Container is sealed"); - } - } -} diff --git a/system/general/src/main/java/org/wamblee/system/graph/DefaultEdge.java b/system/general/src/main/java/org/wamblee/system/graph/DefaultEdge.java index 886186fd..24a2c7eb 100644 --- a/system/general/src/main/java/org/wamblee/system/graph/DefaultEdge.java +++ b/system/general/src/main/java/org/wamblee/system/graph/DefaultEdge.java @@ -34,5 +34,10 @@ public class DefaultEdge implements Edge { public Node getTo() { return _to; } + + @Override + public String toString() { + return "Edge(" + _from.getName() + ", " + _to.getName() + ")"; + } } diff --git a/system/general/src/main/java/org/wamblee/system/graph/component/ApplyRestrictionsVisitor.java b/system/general/src/main/java/org/wamblee/system/graph/component/ApplyRestrictionsVisitor.java new file mode 100644 index 00000000..8213bb8b --- /dev/null +++ b/system/general/src/main/java/org/wamblee/system/graph/component/ApplyRestrictionsVisitor.java @@ -0,0 +1,55 @@ +/* + * Copyright 2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.wamblee.system.graph.component; + +import java.util.List; + +import org.wamblee.system.container.InterfaceRestriction; +import org.wamblee.system.core.Component; +import org.wamblee.system.graph.Edge; +import org.wamblee.system.graph.Graph; +import org.wamblee.system.graph.Node; +import org.wamblee.system.graph.Visitor; + +public class ApplyRestrictionsVisitor implements Visitor { + + private Graph _graph; + private InterfaceRestriction _restriction; + + public ApplyRestrictionsVisitor(Graph aGraph, InterfaceRestriction aRestriction) { + _graph = aGraph; + _restriction = aRestriction; + } + + @Override + public void visitEdge(Edge aEdge) { + if ( aEdge.getFrom() instanceof RequiredInterfaceNode && + aEdge.getTo() instanceof ProvidedInterfaceNode) { + RequiredInterfaceNode from = (RequiredInterfaceNode)aEdge.getFrom(); + ProvidedInterfaceNode to = (ProvidedInterfaceNode) aEdge.getTo(); + + if ( _restriction.isViolated(from.getComponent(), from.getRequired(), to.getComponent(), to.getProvided())) { + _graph.removeEdge(aEdge); + } + } + } + + @Override + public void visitNode(Node aNode) { + // Empty + } + +} diff --git a/system/general/src/main/java/org/wamblee/system/graph/component/CheckExternallyProvidedVisitor.java b/system/general/src/main/java/org/wamblee/system/graph/component/CheckExternallyProvidedVisitor.java new file mode 100644 index 00000000..ef5ea4fb --- /dev/null +++ b/system/general/src/main/java/org/wamblee/system/graph/component/CheckExternallyProvidedVisitor.java @@ -0,0 +1,72 @@ +/* + * Copyright 2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.wamblee.system.graph.component; + +import java.util.List; + +import org.wamblee.system.core.SystemAssemblyException; +import org.wamblee.system.graph.Edge; +import org.wamblee.system.graph.Graph; +import org.wamblee.system.graph.Node; +import org.wamblee.system.graph.Visitor; + +/** + * Visitor that checks externally provided + *
    + *
  • + *
+ * + * @author Erik Brakkee + * + */ +public class CheckExternallyProvidedVisitor implements Visitor { + + private Graph _graph; + + public CheckExternallyProvidedVisitor(Graph aGraph) { + _graph = aGraph; + } + + @Override + public void visitEdge(Edge aEdge) { + // Empty. + } + + @Override + public void visitNode(Node aNode) { + if ( aNode instanceof ExternalProvidedInterfaceNode) { + ExternalProvidedInterfaceNode provided = (ExternalProvidedInterfaceNode) aNode; + List edges = _graph.findOutgoing(provided); + if ( edges.size() > 2 ) { + createDuplicateException("External provided interfaces has multiple internal matches", aNode, edges); + } + if ( edges.size() == 0 ) { + throw new SystemAssemblyException(aNode + ": external provded interface is not provided by any of the internal components"); + } + } + } + + private void createDuplicateException(String aMsg, Node aNode, List edges) { + StringBuffer buf = new StringBuffer(); + buf.append(aNode + + ": " + aMsg + ": "); + for (Edge edge : edges) { + buf.append(edge.getTo() + "/ "); + } + throw new SystemAssemblyException(buf.toString()); + } + +} diff --git a/system/general/src/main/java/org/wamblee/system/graph/component/CheckExternallyRequiredVisitor.java b/system/general/src/main/java/org/wamblee/system/graph/component/CheckExternallyRequiredVisitor.java new file mode 100644 index 00000000..025608a9 --- /dev/null +++ b/system/general/src/main/java/org/wamblee/system/graph/component/CheckExternallyRequiredVisitor.java @@ -0,0 +1,53 @@ +/* + * Copyright 2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.wamblee.system.graph.component; + +import org.wamblee.system.core.SystemAssemblyException; +import org.wamblee.system.graph.Edge; +import org.wamblee.system.graph.Node; +import org.wamblee.system.graph.Visitor; + +/** + * Visitor that checks externally required services: + *
    + *
  • + *
+ * + * @author Erik Brakkee + * + */ +public class CheckExternallyRequiredVisitor implements Visitor { + + public CheckExternallyRequiredVisitor() { + // Empty. + } + + @Override + public void visitEdge(Edge aEdge) { + // Empty. + } + + @Override + public void visitNode(Node aNode) { + if ( aNode instanceof ExternalRequiredInterfaceNode) { + ExternalRequiredInterfaceNode required = (ExternalRequiredInterfaceNode) aNode; + if ( !required.getRequired().isOptional() && required.getRequired().getProvider() == null) { + throw new SystemAssemblyException(aNode + ": External required interface is not provided"); + } + } + } + +} diff --git a/system/general/src/main/java/org/wamblee/system/graph/component/CheckRequiredProvidedMultiplicityVisitor.java b/system/general/src/main/java/org/wamblee/system/graph/component/CheckRequiredProvidedMultiplicityVisitor.java new file mode 100644 index 00000000..526b8923 --- /dev/null +++ b/system/general/src/main/java/org/wamblee/system/graph/component/CheckRequiredProvidedMultiplicityVisitor.java @@ -0,0 +1,86 @@ +/* + * Copyright 2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.wamblee.system.graph.component; + +import java.util.List; + +import org.wamblee.system.core.SystemAssemblyException; +import org.wamblee.system.graph.Edge; +import org.wamblee.system.graph.Graph; +import org.wamblee.system.graph.Node; +import org.wamblee.system.graph.Visitor; + +/** + * Visitor that checks whether required and provided interfaces are matched + * appropriately: + *
    + *
  • Each required interface is connected to at most one provided interface + *
  • + *
  • Required interfaces that are not optional must be connected to precisely + * one provided interface
  • + *
+ * + * @author Erik Brakkee + * + */ +public class CheckRequiredProvidedMultiplicityVisitor implements Visitor { + + private Graph _graph; + + public CheckRequiredProvidedMultiplicityVisitor(Graph aGraph) { + _graph = aGraph; + } + + @Override + public void visitEdge(Edge aEdge) { + // Empty + } + + @Override + public void visitNode(Node aNode) { + if (aNode instanceof RequiredInterfaceNode) { + RequiredInterfaceNode required = (RequiredInterfaceNode) aNode; + List edges = _graph.findOutgoing(aNode); + if (edges.size() > 1) { + createDuplicateException("Multiple internal providers of interface found", aNode, edges); + } + if (edges.size() == 0 && !required.getRequired().isOptional()) { + throw new SystemAssemblyException( + aNode + + ": mandatpory required interface not provided by other components started earlier"); + } + } else if ( aNode instanceof ProvidedInterfaceNode) { + List edges = _graph.findOutgoing(aNode); + if ( edges.size() > 1) { + createDuplicateException("multiple internal matches for externally provided interface", aNode, edges); + } + if ( edges.size() == 0 ) { + throw new SystemAssemblyException(aNode + ": external provided interface is not provided internally"); + } + } + } + + private void createDuplicateException(String aMsg, Node aNode, List edges) { + StringBuffer buf = new StringBuffer(); + buf.append(aNode + + ": " + aMsg + ": "); + for (Edge edge : edges) { + buf.append(edge.getTo() + "/ "); + } + throw new SystemAssemblyException(buf.toString()); + } + +} diff --git a/system/general/src/main/java/org/wamblee/system/graph/component/CheckStartupDependenciesVisitor.java b/system/general/src/main/java/org/wamblee/system/graph/component/CheckStartupDependenciesVisitor.java new file mode 100644 index 00000000..5b50e092 --- /dev/null +++ b/system/general/src/main/java/org/wamblee/system/graph/component/CheckStartupDependenciesVisitor.java @@ -0,0 +1,57 @@ +/* + * Copyright 2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.wamblee.system.graph.component; + +import java.util.ArrayList; +import java.util.List; + +import org.wamblee.system.core.SystemAssemblyException; +import org.wamblee.system.graph.Edge; +import org.wamblee.system.graph.Graph; +import org.wamblee.system.graph.Node; +import org.wamblee.system.graph.Visitor; + +public class CheckStartupDependenciesVisitor implements Visitor { + + private Graph _graph; + private List _available; + + public CheckStartupDependenciesVisitor(Graph aGraph) { + _graph = aGraph; + _available = new ArrayList(); + } + + @Override + public void visitEdge(Edge aEdge) { + // Empty + } + + @Override + public void visitNode(Node aNode) { + List edges = _graph.findOutgoing(aNode); + + // check dependencies. + for (Edge edge: edges) { + Node dep = edge.getTo(); + if ( !_available.contains(dep)) { + throw new SystemAssemblyException(aNode + ": required dependency '" + dep + "' was not started"); + } + } + + _available.add(aNode); + } + +} diff --git a/system/general/src/main/java/org/wamblee/system/graph/component/ComponentGraph.java b/system/general/src/main/java/org/wamblee/system/graph/component/ComponentGraph.java new file mode 100644 index 00000000..20313b59 --- /dev/null +++ b/system/general/src/main/java/org/wamblee/system/graph/component/ComponentGraph.java @@ -0,0 +1,119 @@ +/* + * Copyright 2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.wamblee.system.graph.component; + +import java.util.ArrayList; +import java.util.List; + +import org.wamblee.general.Pair; +import org.wamblee.system.container.CompositeInterfaceRestriction; +import org.wamblee.system.container.InterfaceRestriction; +import org.wamblee.system.core.Component; +import org.wamblee.system.core.ProvidedInterface; +import org.wamblee.system.core.RequiredInterface; +import org.wamblee.system.graph.DefaultEdge; +import org.wamblee.system.graph.Edge; +import org.wamblee.system.graph.Graph; +import org.wamblee.system.graph.Node; + + +// TODO info superfluous required interfaces +// TODO check optional external required but mandatory internal. + +public class ComponentGraph extends Graph { + + private CompositeInterfaceRestriction _restriction; + + public ComponentGraph() { + _restriction = new CompositeInterfaceRestriction(); + } + + public void addRestriction(InterfaceRestriction aRestriction) { + _restriction.add(aRestriction); + } + + public void addRequiredInterface(RequiredInterface aInterface) { + addNode(new ExternalRequiredInterfaceNode(aInterface)); + } + + public void addProvidedInterface(ProvidedInterface aInterface) { + addNode(new ExternalProvidedInterfaceNode(aInterface)); + } + + public void validate() { + extend(new RequiredProvidedEdgeFactory()); + accept(new ApplyRestrictionsVisitor(this, _restriction)); + accept(new CheckRequiredProvidedMultiplicityVisitor(this)); + accept(new CheckExternallyRequiredVisitor()); + accept(new CheckExternallyProvidedVisitor(this)); + accept(new CheckStartupDependenciesVisitor(this)); + } + + public void link() { + accept(new LinkVisitor()); + } + + /** + * Finds a list of mappings of external provided interface to internal provided interface. + * + * @return List of pairs of external to internal interface. + */ + public List> findExternalProvidedInterfaceMapping() { + List> result = + new ArrayList>(); + for (Edge edge: getEdges()) { + if ( edge.getFrom() instanceof ExternalProvidedInterfaceNode && + edge.getTo() instanceof ProvidedInterfaceNode ) { + result.add(new Pair( + ((ExternalProvidedInterfaceNode)edge.getFrom()).getProvided(), + ((ProvidedInterfaceNode)edge.getTo()).getProvided() + )); + } + } + return result; + } + + public void addComponent(Component aComponent) { + // Add required interfaces. + Node compNode = new ComponentNode(aComponent); + List requiredNodes = new ArrayList(); + for (RequiredInterface required: aComponent.getRequiredInterfaces()) { + Node reqNode = new RequiredInterfaceNode(aComponent, required); + addNode(reqNode); + requiredNodes.add(reqNode); + } + // Add the component + addNode(compNode); + + // Edges from component to required interface. + for (Node reqNode: requiredNodes) { + addEdge(new DefaultEdge(compNode, reqNode)); + } + + // Add provided interfaces + List providedNodes = new ArrayList(); + for (ProvidedInterface provided: aComponent.getProvidedInterfaces()) { + Node provNode = new ProvidedInterfaceNode(aComponent, provided); + addNode(provNode); + providedNodes.add(provNode); + } + + // Edges from provided interface to component + for (Node provNode: providedNodes) { + addEdge(new DefaultEdge(provNode, compNode)); + } + } +} diff --git a/system/general/src/main/java/org/wamblee/system/graph/component/ComponentNode.java b/system/general/src/main/java/org/wamblee/system/graph/component/ComponentNode.java new file mode 100644 index 00000000..72e71106 --- /dev/null +++ b/system/general/src/main/java/org/wamblee/system/graph/component/ComponentNode.java @@ -0,0 +1,37 @@ +/* + * Copyright 2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.wamblee.system.graph.component; + +import org.wamblee.system.core.Component; +import org.wamblee.system.graph.Node; + +public class ComponentNode implements Node { + + private Component _component; + + public ComponentNode(Component aComponent) { + _component = aComponent; + } + + @Override + public String getName() { + return _component.getQualifiedName(); + } + + public Component getComponent() { + return _component; + } +} diff --git a/system/general/src/main/java/org/wamblee/system/graph/component/ExternalProvidedInterfaceNode.java b/system/general/src/main/java/org/wamblee/system/graph/component/ExternalProvidedInterfaceNode.java new file mode 100644 index 00000000..c2c7f212 --- /dev/null +++ b/system/general/src/main/java/org/wamblee/system/graph/component/ExternalProvidedInterfaceNode.java @@ -0,0 +1,37 @@ +/* + * Copyright 2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.wamblee.system.graph.component; + +import org.wamblee.system.core.ProvidedInterface; +import org.wamblee.system.graph.Node; + +public class ExternalProvidedInterfaceNode implements Node { + + private ProvidedInterface _provided; + + public ExternalProvidedInterfaceNode(ProvidedInterface aProvided) { + _provided = aProvided; + } + + @Override + public String getName() { + return _provided.getName(); + } + + public ProvidedInterface getProvided() { + return _provided; + } +} diff --git a/system/general/src/main/java/org/wamblee/system/graph/component/ExternalRequiredInterfaceNode.java b/system/general/src/main/java/org/wamblee/system/graph/component/ExternalRequiredInterfaceNode.java new file mode 100644 index 00000000..580bc719 --- /dev/null +++ b/system/general/src/main/java/org/wamblee/system/graph/component/ExternalRequiredInterfaceNode.java @@ -0,0 +1,42 @@ +/* + * Copyright 2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.wamblee.system.graph.component; + +import org.wamblee.system.core.RequiredInterface; +import org.wamblee.system.graph.Node; + +public class ExternalRequiredInterfaceNode implements Node { + + private RequiredInterface _required; + + public ExternalRequiredInterfaceNode(RequiredInterface aRequired) { + _required = aRequired; + } + + @Override + public String getName() { + return _required.getName(); + } + + public RequiredInterface getRequired() { + return _required; + } + + @Override + public String toString() { + return "EXTERNAL" + ":" + _required; + } +} diff --git a/system/general/src/main/java/org/wamblee/system/graph/component/LinkVisitor.java b/system/general/src/main/java/org/wamblee/system/graph/component/LinkVisitor.java new file mode 100644 index 00000000..088f0585 --- /dev/null +++ b/system/general/src/main/java/org/wamblee/system/graph/component/LinkVisitor.java @@ -0,0 +1,68 @@ +/* + * Copyright 2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.wamblee.system.graph.component; + +import org.wamblee.system.core.ProvidedInterface; +import org.wamblee.system.core.SystemAssemblyException; +import org.wamblee.system.graph.DefaultEdge; +import org.wamblee.system.graph.Edge; +import org.wamblee.system.graph.Node; +import org.wamblee.system.graph.Visitor; + +/** + * Visitor that creates links between required and provided interfaces as + * described by the edges in the graph. + * + * @author Erik Brakkee + * + */ +public class LinkVisitor implements Visitor { + + @Override + public void visitEdge(Edge aEdge) { + Node from = aEdge.getFrom(); + Node to = aEdge.getTo(); + if (from instanceof RequiredInterfaceNode) { + RequiredInterfaceNode required = (RequiredInterfaceNode) from; + if (to instanceof ProvidedInterfaceNode) { + + ProvidedInterfaceNode provided = (ProvidedInterfaceNode) to; + required.getRequired().setProvider(provided.getProvided()); + + } else if (to instanceof ExternalRequiredInterfaceNode) { + ExternalRequiredInterfaceNode external = (ExternalRequiredInterfaceNode) to; + ProvidedInterface provider = external.getRequired() + .getProvider(); + if (provider == null && !required.getRequired().isOptional()) { + throw new SystemAssemblyException("Mandatory interface '" + + from + "' is not provided."); + } + if ( provider != null ) { + required.getRequired().setProvider(provider); + } + } + } else if (from instanceof ProvidedInterfaceNode) { + // Provided interfaces can only be published after the system has + // started. + } + } + + @Override + public void visitNode(Node aNode) { + // Empty. + } + +} diff --git a/system/general/src/main/java/org/wamblee/system/graph/component/ProvidedInterfaceNode.java b/system/general/src/main/java/org/wamblee/system/graph/component/ProvidedInterfaceNode.java new file mode 100644 index 00000000..e2782b09 --- /dev/null +++ b/system/general/src/main/java/org/wamblee/system/graph/component/ProvidedInterfaceNode.java @@ -0,0 +1,49 @@ +/* + * Copyright 2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.wamblee.system.graph.component; + +import org.wamblee.system.core.Component; +import org.wamblee.system.core.ProvidedInterface; +import org.wamblee.system.graph.Node; + +public class ProvidedInterfaceNode implements Node { + + private Component _component; + private ProvidedInterface _provided; + + public ProvidedInterfaceNode(Component aComponent, ProvidedInterface aProvided) { + _component = aComponent; + _provided = aProvided; + } + + @Override + public String getName() { + return _component.getQualifiedName() + ":" + _provided.getName(); + } + + public ProvidedInterface getProvided() { + return _provided; + } + + public Component getComponent() { + return _component; + } + + @Override + public String toString() { + return _component.getQualifiedName() + ":" + _provided; + } +} diff --git a/system/general/src/main/java/org/wamblee/system/graph/component/RequiredInterfaceNode.java b/system/general/src/main/java/org/wamblee/system/graph/component/RequiredInterfaceNode.java new file mode 100644 index 00000000..1b053b18 --- /dev/null +++ b/system/general/src/main/java/org/wamblee/system/graph/component/RequiredInterfaceNode.java @@ -0,0 +1,49 @@ +/* + * Copyright 2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.wamblee.system.graph.component; + +import org.wamblee.system.core.Component; +import org.wamblee.system.core.RequiredInterface; +import org.wamblee.system.graph.Node; + +public class RequiredInterfaceNode implements Node { + + private Component _component; + private RequiredInterface _required; + + public RequiredInterfaceNode(Component aComponent, RequiredInterface aRequired) { + _component = aComponent; + _required = aRequired; + } + + @Override + public String getName() { + return _component.getQualifiedName() + ":" + _required.getName(); + } + + public RequiredInterface getRequired() { + return _required; + } + + public Component getComponent() { + return _component; + } + + @Override + public String toString() { + return _component.getQualifiedName() + ":" + _required; + } +} diff --git a/system/general/src/main/java/org/wamblee/system/graph/component/RequiredProvidedEdgeFactory.java b/system/general/src/main/java/org/wamblee/system/graph/component/RequiredProvidedEdgeFactory.java new file mode 100644 index 00000000..983acba1 --- /dev/null +++ b/system/general/src/main/java/org/wamblee/system/graph/component/RequiredProvidedEdgeFactory.java @@ -0,0 +1,68 @@ +/* + * Copyright 2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.wamblee.system.graph.component; + +import java.util.ArrayList; +import java.util.List; + +import org.wamblee.system.graph.DefaultEdge; +import org.wamblee.system.graph.Edge; +import org.wamblee.system.graph.EdgeFactory; +import org.wamblee.system.graph.Node; + +/** + * Factory that creates links between required and provided interfaces. + * + * @author Erik Brakkee + * + */ +public class RequiredProvidedEdgeFactory implements EdgeFactory { + + public RequiredProvidedEdgeFactory() { + // Empty. + } + + @Override + public List create(Node aFrom, Node aTo) { + List result = new ArrayList(); + if (aFrom instanceof RequiredInterfaceNode) { + RequiredInterfaceNode required = (RequiredInterfaceNode) aFrom; + if (aTo instanceof ProvidedInterfaceNode) { + + ProvidedInterfaceNode provided = (ProvidedInterfaceNode) aTo; + if (required.getRequired() + .implementedBy(provided.getProvided())) { + result.add(new DefaultEdge(required, provided)); + } + } else if (aTo instanceof ExternalRequiredInterfaceNode) { + ExternalRequiredInterfaceNode external = (ExternalRequiredInterfaceNode) aTo; + if ( required.getRequired().equals(external.getRequired())) { + result.add(new DefaultEdge(required, external)); + } + } + } else if ( aFrom instanceof ProvidedInterfaceNode) { + ProvidedInterfaceNode provided = (ProvidedInterfaceNode)aFrom; + if ( aTo instanceof ExternalProvidedInterfaceNode) { + ExternalProvidedInterfaceNode external = (ExternalProvidedInterfaceNode)aTo; + if ( provided.getProvided().equals(external.getProvided())) { + result.add(new DefaultEdge(external, provided)); + } + } + } + return result; + } + +} diff --git a/system/general/src/test/java/org/wamblee/system/adapters/ClassAdapterTest.java b/system/general/src/test/java/org/wamblee/system/adapters/ClassAdapterTest.java index f25d3459..113fe74e 100644 --- a/system/general/src/test/java/org/wamblee/system/adapters/ClassAdapterTest.java +++ b/system/general/src/test/java/org/wamblee/system/adapters/ClassAdapterTest.java @@ -18,8 +18,8 @@ package org.wamblee.system.adapters; import java.util.Collections; import java.util.List; +import org.wamblee.system.container.Container; import org.wamblee.system.core.Component; -import org.wamblee.system.core.Container; import org.wamblee.system.core.DefaultProvidedInterface; import org.wamblee.system.core.ProvidedInterface; import org.wamblee.system.core.RequiredInterface; diff --git a/system/general/src/test/java/org/wamblee/system/adapters/ObjectAdapterTest.java b/system/general/src/test/java/org/wamblee/system/adapters/ObjectAdapterTest.java index 2a06375e..6d5a3359 100644 --- a/system/general/src/test/java/org/wamblee/system/adapters/ObjectAdapterTest.java +++ b/system/general/src/test/java/org/wamblee/system/adapters/ObjectAdapterTest.java @@ -18,8 +18,8 @@ package org.wamblee.system.adapters; import java.util.Collections; import java.util.List; +import org.wamblee.system.container.Container; import org.wamblee.system.core.Component; -import org.wamblee.system.core.Container; import org.wamblee.system.core.DefaultProvidedInterface; import org.wamblee.system.core.ProvidedInterface; import org.wamblee.system.core.RequiredInterface; diff --git a/system/general/src/test/java/org/wamblee/system/components/DatabaseComponentFactory.java b/system/general/src/test/java/org/wamblee/system/components/DatabaseComponentFactory.java index 540c09ae..522b14b8 100644 --- a/system/general/src/test/java/org/wamblee/system/components/DatabaseComponentFactory.java +++ b/system/general/src/test/java/org/wamblee/system/components/DatabaseComponentFactory.java @@ -17,7 +17,7 @@ package org.wamblee.system.components; import org.wamblee.io.ClassPathResource; import org.wamblee.persistence.DerbyDatabase; -import org.wamblee.system.core.Container; +import org.wamblee.system.container.Container; public class DatabaseComponentFactory { diff --git a/system/general/src/test/java/org/wamblee/system/core/Application.java b/system/general/src/test/java/org/wamblee/system/container/Application.java similarity index 94% rename from system/general/src/test/java/org/wamblee/system/core/Application.java rename to system/general/src/test/java/org/wamblee/system/container/Application.java index 5e771b32..94603767 100644 --- a/system/general/src/test/java/org/wamblee/system/core/Application.java +++ b/system/general/src/test/java/org/wamblee/system/container/Application.java @@ -13,14 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.wamblee.system.core; - -import javax.sql.DataSource; +package org.wamblee.system.container; import org.wamblee.system.core.AbstractComponent; import org.wamblee.system.core.DefaultRequiredInterface; import org.wamblee.system.core.ProvidedInterface; import org.wamblee.system.core.RequiredInterface; +import org.wamblee.system.core.Scope; import org.wamblee.test.EventTracker; public class Application extends AbstractComponent { @@ -65,7 +64,7 @@ public class Application extends AbstractComponent { } @Override - protected Object doStart(Scope aScope) { + public Object doStart(Scope aScope) { track("start." + getName()); _string = aScope.getInterfaceImplementation(getRequiredInterfaces()[0].getProvider(), String.class); _integer = aScope.getInterfaceImplementation(getRequiredInterfaces()[1].getProvider(), Integer.class); @@ -81,7 +80,7 @@ public class Application extends AbstractComponent { } @Override - protected void doStop(Object aRuntime) { + public void doStop(Object aRuntime) { track("stop." + getName()); if ( _random != (Double)aRuntime) { throw new IllegalArgumentException("Wrong runtime: expected " + _random + " but got " + diff --git a/system/general/src/test/java/org/wamblee/system/core/CompositeInterfaceRestrictionTest.java b/system/general/src/test/java/org/wamblee/system/container/CompositeInterfaceRestrictionTest.java similarity index 93% rename from system/general/src/test/java/org/wamblee/system/core/CompositeInterfaceRestrictionTest.java rename to system/general/src/test/java/org/wamblee/system/container/CompositeInterfaceRestrictionTest.java index 98f95aa4..e724178e 100644 --- a/system/general/src/test/java/org/wamblee/system/core/CompositeInterfaceRestrictionTest.java +++ b/system/general/src/test/java/org/wamblee/system/container/CompositeInterfaceRestrictionTest.java @@ -13,16 +13,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.wamblee.system.core; +package org.wamblee.system.container; + +import static org.easymock.EasyMock.anyObject; import java.util.Arrays; +import junit.framework.TestCase; + import org.easymock.classextension.EasyMock; import org.easymock.classextension.IMocksControl; -import org.wamblee.test.EasyMockMatchers; -import static org.easymock.classextension.EasyMock.*; - -import junit.framework.TestCase; +import org.wamblee.system.core.Component; +import org.wamblee.system.core.Environment; +import org.wamblee.system.core.ProvidedInterface; +import org.wamblee.system.core.RequiredInterface; public class CompositeInterfaceRestrictionTest extends TestCase { diff --git a/system/general/src/test/java/org/wamblee/system/core/ContainerTest.java b/system/general/src/test/java/org/wamblee/system/container/ContainerTest.java similarity index 89% rename from system/general/src/test/java/org/wamblee/system/core/ContainerTest.java rename to system/general/src/test/java/org/wamblee/system/container/ContainerTest.java index da8f8350..95d7e312 100644 --- a/system/general/src/test/java/org/wamblee/system/core/ContainerTest.java +++ b/system/general/src/test/java/org/wamblee/system/container/ContainerTest.java @@ -13,12 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.wamblee.system.core; +package org.wamblee.system.container; -import java.io.IOException; import java.io.Serializable; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import junit.framework.TestCase; @@ -27,6 +25,16 @@ import org.easymock.classextension.ConstructorArgs; import org.easymock.classextension.EasyMock; import org.easymock.classextension.IMocksControl; import org.wamblee.general.Pair; +import org.wamblee.system.core.Component; +import org.wamblee.system.core.DefaultProvidedInterface; +import org.wamblee.system.core.DefaultRequiredInterface; +import org.wamblee.system.core.DefaultScope; +import org.wamblee.system.core.Environment; +import org.wamblee.system.core.ProvidedInterface; +import org.wamblee.system.core.RequiredInterface; +import org.wamblee.system.core.Scope; +import org.wamblee.system.core.StringComponent; +import org.wamblee.system.core.SystemAssemblyException; import org.wamblee.test.AssertionUtils; import org.wamblee.test.EasyMockMatchers; import org.wamblee.test.EventTracker; @@ -58,58 +66,6 @@ public class ContainerTest extends TestCase { return result; } - public void testFilterProvided() { - RequiredInterface req1 = new DefaultRequiredInterface("name", - Runnable.class); - RequiredInterface req2 = new DefaultRequiredInterface("name", - Serializable.class); - ProvidedInterface prov1 = new DefaultProvidedInterface("name", - Runnable.class); - ProvidedInterface prov2 = new DefaultProvidedInterface("name", - Serializable.class); - ProvidedInterface prov3 = new DefaultProvidedInterface("name", - MyMultiple.class); - - Component client = new Application("client"); - Component dummy = new Application("dummy"); - - InterfaceRestriction noRestriction = new InterfaceRestriction() { - @Override - public boolean isViolated(Component aClient, - RequiredInterface aRequired, Component aServer, - ProvidedInterface aProvided) { - return false; - } - }; - - AssertionUtils.assertEquals(new ProvidedInterface[] { prov1 }, - Container.filterProvidedServices(client, req1, - createProvidedInput(new ProvidedInterface[] { prov1 }, - dummy), noRestriction)); - AssertionUtils.assertEquals(new ProvidedInterface[] { prov1 }, - Container.filterProvidedServices(client, req1, - createProvidedInput(new ProvidedInterface[] { prov1, - prov2 }, dummy), noRestriction)); - AssertionUtils.assertEquals(new ProvidedInterface[] { prov1, prov3 }, - Container.filterProvidedServices(client, req1, - createProvidedInput(new ProvidedInterface[] { prov1, - prov3 }, dummy), noRestriction)); - - InterfaceRestriction everything = new InterfaceRestriction() { - @Override - public boolean isViolated(Component aClient, - RequiredInterface aRequired, Component aServer, - ProvidedInterface aProvided) { - return true; - } - }; - AssertionUtils.assertEquals(new ProvidedInterface[0], Container - .filterProvidedServices(client, req1, createProvidedInput( - new ProvidedInterface[] { prov1, prov3 }, dummy), - everything)); - - } - public void testEnvironmentApplication() { Environment environment = new Environment(_tracker); Application application = new Application(_tracker); diff --git a/system/general/src/test/java/org/wamblee/system/core/DefaultInterfaceRestrictionTest.java b/system/general/src/test/java/org/wamblee/system/container/DefaultInterfaceRestrictionTest.java similarity index 98% rename from system/general/src/test/java/org/wamblee/system/core/DefaultInterfaceRestrictionTest.java rename to system/general/src/test/java/org/wamblee/system/container/DefaultInterfaceRestrictionTest.java index 81d47512..cf6c3a82 100644 --- a/system/general/src/test/java/org/wamblee/system/core/DefaultInterfaceRestrictionTest.java +++ b/system/general/src/test/java/org/wamblee/system/container/DefaultInterfaceRestrictionTest.java @@ -13,16 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.wamblee.system.core; +package org.wamblee.system.container; import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import org.wamblee.test.AssertionUtils; - import junit.framework.TestCase; +import org.wamblee.system.core.Environment; +import org.wamblee.test.AssertionUtils; + public class DefaultInterfaceRestrictionTest extends TestCase { private Application _app1 = new Application("app1", "pf1."); private Application _app2 = new Application("app2", "pf2."); -- 2.31.1