Huge refactoring.
authorErik Brakkee <erik@brakkee.org>
Sat, 12 Apr 2008 20:19:09 +0000 (20:19 +0000)
committerErik Brakkee <erik@brakkee.org>
Sat, 12 Apr 2008 20:19:09 +0000 (20:19 +0000)
Separation of the description/metadata part (Component/Container) and
the runtime part (Scope).

16 files changed:
support/general/src/test/java/org/wamblee/test/EasyMockMatchers.java [new file with mode: 0644]
system/general/src/main/java/org/wamblee/system/core/AbstractComponent.java
system/general/src/main/java/org/wamblee/system/core/Component.java
system/general/src/main/java/org/wamblee/system/core/Container.java
system/general/src/main/java/org/wamblee/system/core/DefaultProvidedInterface.java
system/general/src/main/java/org/wamblee/system/core/DefaultRequiredInterface.java
system/general/src/main/java/org/wamblee/system/core/ProvidedInterface.java
system/general/src/main/java/org/wamblee/system/core/RequiredInterface.java
system/general/src/test/java/org/wamblee/system/core/AbstractComponentTest.java
system/general/src/test/java/org/wamblee/system/core/Application.java
system/general/src/test/java/org/wamblee/system/core/ContainerTest.java
system/general/src/test/java/org/wamblee/system/core/DefaultRequiredInterfaceTest.java
system/general/src/test/java/org/wamblee/system/core/Environment.java
system/spring/src/main/java/org/wamblee/system/spring/RequiredServiceBean.java
system/spring/src/main/java/org/wamblee/system/spring/SpringComponent.java
system/spring/src/test/java/org/wamblee/system/spring/SpringComponentTest.java

diff --git a/support/general/src/test/java/org/wamblee/test/EasyMockMatchers.java b/support/general/src/test/java/org/wamblee/test/EasyMockMatchers.java
new file mode 100644 (file)
index 0000000..ef9c5d0
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * 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.test;
+
+import org.easymock.IArgumentMatcher;
+import org.easymock.classextension.EasyMock;
+
+/**
+ * Some general matchers for easy mock. 
+ * 
+ * @author Erik Brakkee
+ */
+public class EasyMockMatchers {
+
+       public static class Any implements IArgumentMatcher {
+               public Any() {
+                       // Empty
+               }
+
+               @Override
+               public boolean matches(Object aArg0) {
+                       return true;
+               }
+
+               @Override
+               public void appendTo(StringBuffer aBuf) {
+                       aBuf.append("anyObject()");
+               }
+       }
+
+       /**
+        * Type-safe matcher to match any object of a given type. 
+        * @param <T>
+        * @param aType Type.
+        * @return Returns null.  
+        */
+       public static <T> T anyObject(Class<T> aType) {
+               EasyMock.reportMatcher(new Any());
+               return null;
+       }
+}
index 01eff741ac362aa13e6b0d3280c9ae9975b001d6..b8e4e6be9b2f5276b041d44e9e61434c1c503ac0 100644 (file)
@@ -12,7 +12,7 @@
  * 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;
@@ -27,19 +27,20 @@ import org.apache.commons.logging.LogFactory;
 /**
  * Abstract subsystem class making it easy to implement new subsystems.
  */
-public abstract class AbstractComponent implements Component {
+public abstract class AbstractComponent<Type> implements Component<Type> {
 
        private static final Log LOG = LogFactory.getLog(AbstractComponent.class);
 
-       private String _context; 
-       private String _name; 
+       private ThreadLocal<List<ProvidedInterface>> _remaining;
+
+       private String _context;
+       private String _name;
        private List<ProvidedInterface> _provided;
        private List<RequiredInterface> _required;
-       private Set<ProvidedInterface> _running;
-       
+
        /**
         * Constructs the subsystem.
-        *
+        * 
         * @param aName
         *            Name of the system.
         * @param aProvided
@@ -49,36 +50,35 @@ public abstract class AbstractComponent implements Component {
         */
        protected AbstractComponent(String aName, ProvidedInterface[] aProvided,
                        RequiredInterface[] aRequired) {
-               _context = null; 
+               _remaining = new ThreadLocal<List<ProvidedInterface>>();
+               _context = null;
                _name = aName;
                _provided = new ArrayList<ProvidedInterface>();
                _provided.addAll(Arrays.asList(aProvided));
                _required = new ArrayList<RequiredInterface>();
                _required.addAll(Arrays.asList(aRequired));
-               _running = new HashSet<ProvidedInterface>();
        }
 
        @Override
        public final String getName() {
                return _name;
        }
-       
+
        @Override
        public void addContext(String aContext) {
-               if (_context == null ) { 
-                       _context = aContext; 
-               }
-               else { 
+               if (_context == null) {
+                       _context = aContext;
+               } else {
                        _context = aContext + "." + _context;
                }
        }
-       
+
        @Override
        public String getQualifiedName() {
-               if ( _context == null ) { 
-                       return getName(); 
+               if (_context == null) {
+                       return getName();
                }
-               return _context + "." + getName(); 
+               return _context + "." + getName();
        }
 
        @Override
@@ -92,67 +92,64 @@ public abstract class AbstractComponent implements Component {
        }
 
        @Override
-       public final void start() {
+       public final Type start(Scope aScope) {
                LOG.info("Initializing '" + getQualifiedName() + "'");
-               doStart();
-               if ( _running.size() != _provided.size()) { 
-                       List<ProvidedInterface> remaining = 
-                               new ArrayList<ProvidedInterface>(_provided);
-                       remaining.removeAll(_running);
-                       throw new SystemAssemblyException(getQualifiedName() + ": not all services were started, missing " + remaining);
+               List<ProvidedInterface> oldRemaining = _remaining.get();
+               _remaining.set(new ArrayList<ProvidedInterface>(Arrays.asList(getProvidedInterfaces())));
+               try {
+                       Type runtime = doStart(aScope);
+                       checkNotStartedInterfaces();
+                       return runtime;
+               } finally {
+                       _remaining.set(oldRemaining);
+               }
+       }
+
+       private void checkNotStartedInterfaces() {
+               if (_remaining.get().size() > 0) {
+                       String notProvided = "";
+                       for (ProvidedInterface provided : _remaining.get()) {
+                               notProvided += "\nComponent " + getQualifiedName()
+                                               + " did not start interface " + provided;
+                       }
+                       throw new SystemAssemblyException(notProvided);
                }
        }
 
        /**
         * Must be implemented for initializing the subsystem. The implementation
         * must call {@link #addService(Service)} for each service that is started.
+        * 
+        * @return Returns the runtime of the component.
         */
-       protected abstract void doStart();
+       protected abstract Type doStart(Scope aScope);
 
        /**
         * Implementations must call this method to indicate that a new service has
         * been started.
         * 
+        * @param aDescriptor
+        *            Provided interface.
         * @param aService
-        *            Service.
+        *            Implementation of the interface.
+        * @param aScope
+        *            scope in which to publish the implementation.
         */
-       protected final void addInterface(
-                       ProvidedInterface aDescriptor, Object aService) {
-               LOG.info("Interface '" + getQualifiedName() + "." + aDescriptor.getName() + "' started.");
-               _running.add(aDescriptor);
-               aDescriptor.publish(aService);
+       protected final void addInterface(ProvidedInterface aDescriptor,
+                       Object aService, Scope aScope) {
+               LOG.info("Interface '" + getQualifiedName() + "."
+                               + aDescriptor.getName() + "' started.");
+               _remaining.get().remove(aDescriptor);
+               aScope.publishInterface(aDescriptor, aService);
        }
 
        @Override
-       public ProvidedInterface[] getRunningInterfaces() {
-               return _running.toArray(new ProvidedInterface[0]);
-       }
-       
-       @Override
-       public void stop() {
-               doStop();
-               if ( _running.size() > 0 ) { 
-                       // programming error.
-                       throw new RuntimeException(getQualifiedName() + ": still services running after the stop call.");
-               }
-       }
-       
-       protected abstract void doStop();
-       
-       /**
-        * Implementations must call this method to indicate that a running service has
-        * been stopped.
-        * 
-        * @param aService
-        *            Service.
-        */
-       protected final void removeInterface(
-                       ProvidedInterface aDescriptor) {
-               LOG.info("Interface '" + getQualifiedName() + "." + aDescriptor.getName() + "' stopped.");
-               _running.remove(aDescriptor);
-               aDescriptor.publish(null);
+       public void stop(Type aRuntime) {
+               doStop(aRuntime);
        }
 
+       protected abstract void doStop(Type aRuntime);
+
        @Override
        public String toString() {
                return getQualifiedName();
index ff80ee693acdcba8f70443cf4bb704ba4821a2d5..d14eb08a2364891205aef8647b47446e8df66f0a 100644 (file)
@@ -17,11 +17,19 @@ package org.wamblee.system.core;
 
 /**
  * A component represents a part of a system that requires a 
- * number of interfaces and provides a number of interfaces. 
+ * number of interfaces and provides a number of interfaces.
+ * 
+ * The component interface provides the meta-data for a component. 
+ * After calling {@link #start(Scope)}, an actual runtime representation of the
+ * component can be created which is independent of this component. 
+ * As a special case, the runtime representation may be identical to the 
+ * component instance but in general it is not. This allows a component to be 
+ * used as a factory for creating objects. 
+ * 
  *
  * @author Erik Brakkee
  */
-public interface Component {
+public interface Component<Type> {
        
        /**
         * Gets the name of the subsystem.
@@ -56,22 +64,18 @@ public interface Component {
 
        
        /**
-        * Initialises the subsytem by starting all the services that
-        * it described as provided. 
-        */
-       void start();
-       
-       /**
-        * Gets the list of running services in the subsystem. 
-        * 
-        * This method may only be called after the
-        * {@link #initialize(String, Service[])} has been called. 
-        * @return
+        * Initialises the subsystem by starting all the services that
+        * it described as provided.
+        * @param aScope Scope with external interface implementations that are available. The component 
+        *  implementation can either oublish itself in this scope or it can decide to
+        *  create a new scope with the scope passed in as a parent. 
+        * @return Gets an object representing the runtime of the component.  
         */
-       ProvidedInterface[] getRunningInterfaces();
+       Type start(Scope aScope);
        
        /**
-        * Stops a subsystem. 
+        * Stops a component. 
+        * @param aRuntime THe runtime part of the component.  
         */
-       void stop(); 
+       void stop(Type aRuntime); 
 }
index df92c0d099dcb4a61e219bb6047a7b5bea672783..f1b8b1ec0380fe4204f7f2021dd97c9bf8e53de9 100644 (file)
@@ -29,7 +29,7 @@ import org.apache.commons.logging.LogFactory;
  * 
  * @author Erik Brakkee
  */
-public class Container extends AbstractComponent {
+public class Container extends AbstractComponent<Scope> {
 
        private static final Log LOG = LogFactory.getLog(Container.class);
 
@@ -176,10 +176,18 @@ public class Container extends AbstractComponent {
                        }
                }
        }
+       
+       public Scope start() { 
+               return super.start(new DefaultScope(new ProvidedInterface[0]));
+       }
 
        @Override
-       protected void doStart() {
+       protected Scope doStart(Scope aExternalScope) {
                LOG.info("Starting '" + getQualifiedName() + "'");
+               
+               Scope scope = new DefaultScope(getProvidedInterfaces(), 
+                               aExternalScope);
+               
                List<ProvidedInterface> allProvided = new ArrayList<ProvidedInterface>();
 
                // all interfaces from the required list of this container are
@@ -203,7 +211,8 @@ public class Container extends AbstractComponent {
                                checkAllRequiredServicesAlreadyProvided(allProvided, component);
 
                                // Start the service.
-                               component.start();
+                               Object runtime = component.start(scope);
+                               scope.addRuntime(component, runtime); 
                                started.add(component);
 
                                // add all provided services
@@ -214,19 +223,19 @@ public class Container extends AbstractComponent {
                        } catch (RuntimeException e) {
                                LOG.error(getQualifiedName() + ": could not start '"
                                                + component.getQualifiedName() + "'", e);
-                               stopAlreadyStartedComponents(started);
+                               stopAlreadyStartedComponents(started, scope);
                                throw e; 
                        }
                }
-
+               return scope; 
        }
 
-       private void stopAlreadyStartedComponents(List<Component> aStarted) {
+       private void stopAlreadyStartedComponents(List<Component> aStarted, Scope aScope) {
                // an exception occurred, stop the successfully started
                // components
                for (int i = aStarted.size() - 1; i >= 0; i--) {
                        try {
-                               aStarted.get(i).stop();
+                               aStarted.get(i).stop(aScope);
                        } catch (Throwable t) {
                                LOG.error(getQualifiedName() + ": error stopping "
                                                + aStarted.get(i).getQualifiedName());
@@ -268,9 +277,11 @@ public class Container extends AbstractComponent {
        }
 
        @Override
-       protected void doStop() {
+       protected void doStop(Scope aScope) {
                for (int i = _components.length - 1; i >= 0; i--) {
-                       _components[i].stop();
+                       Component component = _components[i];
+                       Object runtime = aScope.getRuntime(component);
+                       component.stop(runtime);
                }
        }
 
index 60c111ed1ecfd14c699924b0f2fb10b8476af2ce..90c230dcc9bf23493fcba2dd629f14c7c880e665 100644 (file)
@@ -26,20 +26,20 @@ public class DefaultProvidedInterface implements ProvidedInterface {
        
        private String _name; 
        private Class[] _interfaces;
-       private Object _implementation
+       private String _uniqueId
        
        /**
         * Constructs the descriptor. 
         * @param aInterface Type of service. 
         */
        public DefaultProvidedInterface(String aName, Class aInterface) {
-               _name = aName; 
-               _interfaces = new Class[] { aInterface };  
+               this(aName, new Class[] { aInterface }); 
        }
        
        public DefaultProvidedInterface(String aName, Class[] aInterfaces) {
                _name = aName; 
-               _interfaces = Arrays.copyOf(aInterfaces, aInterfaces.length);  
+               _interfaces = Arrays.copyOf(aInterfaces, aInterfaces.length);
+               _uniqueId = null; 
        }
 
        @Override
@@ -51,40 +51,20 @@ public class DefaultProvidedInterface implements ProvidedInterface {
        public Class[] getInterfaceTypes() {
                return _interfaces;
        }
-       
+
        @Override
-       public void publish(Object aImplementation) {
-               _implementation = aImplementation;      
+       public void setUniqueId(String aId) {
+               _uniqueId = aId;        
        }
        
        @Override
-       public Object getImplementation() {
-               return _implementation; 
+       public String getUniqueId() {
+               return _uniqueId;
        }
        
        @Override
-       public boolean equals(Object obj) {
-               if ( !(obj instanceof DefaultProvidedInterface)) { 
-                       return false; 
-               }
-               DefaultProvidedInterface descr = (DefaultProvidedInterface)obj;
-               if ( _interfaces.length != descr._interfaces.length ) { 
-                       return false; 
-               }
-               String[] interfaces1 = new String[_interfaces.length];
-               String[] interfaces2 = new String[_interfaces.length];
-               for (int i = 0; i < _interfaces.length; i++) {  
-                       interfaces1[i] = _interfaces[i].getName();
-                       interfaces2[i] = descr._interfaces[i].getName();
-               }
-               Arrays.sort(interfaces1);
-               Arrays.sort(interfaces2);
-               return Arrays.equals(interfaces1, interfaces2);
-       }
-
-       @Override
-       public int hashCode() {
-               return _interfaces.hashCode(); 
+       public void publish(Object aImplementation, Scope aScope) {
+               aScope.publishInterface(this, aImplementation);
        }
        
        @Override
index e028342e9d2fa98528ec04e7ccc2dfd90ce59e39..9927b517f9c9accf0618fcfba2e4f065c09889ea 100644 (file)
@@ -92,14 +92,6 @@ public class DefaultRequiredInterface implements RequiredInterface {
        public void setProvider(ProvidedInterface aProvider) {
                _provider = aProvider;  
        }
-
-       @Override
-       public <T> T getImplementation(Class<T> aClass) {
-               if ( _provider == null ) { 
-                       return null; 
-               }
-               return (T)_provider.getImplementation();
-       }
        
        @Override
        public boolean equals(Object obj) {
index 349197d0f2b8a450ee39ea5e21a6c4dfc10f2ffa..fe7f7d26a73483907d0102b29050c86455f8d55c 100644 (file)
@@ -19,6 +19,7 @@ import java.util.Collection;
 
 /**
  * Represents an interface provided by a component.
+ * Different component objects should never share ProvidedInterface instances!
  *  
  * @author Erik Brakkee
  */
@@ -37,14 +38,24 @@ public interface ProvidedInterface {
        Class[] getInterfaceTypes();
        
        /**
-        * Publish an implementation of the interface. 
-        * @param aImplementation
+        * Sets a unique id of the provided interface to identify it within a given scope.
+        * Will be called by the container as part of calling {@link #publish(Object, Scope)}. 
+        * @param aId Unique id. 
         */
-       void publish(Object aImplementation); 
-       
+       void setUniqueId(String aId);
+
+       /**
+        * Gets the unique if of the provided interface. This is set by the container
+        * using {@link #setUniqueId(String)}.  
+        */
+       String getUniqueId();
+
        /**
-        * Gets the implementation. 
-        * @return Implementation or null if not started. 
+        * Publishes an implementation of the interface. The implementation must 
+        * call {@link Scope#publishInterface(ProvidedInterface, Object)} to publish the
+        * interface implementation in a given scope.  
+        * @param aImplementation Implementation to publish. 
+        * @param aScope Scope in which to publish the implementation. 
         */
-       Object getImplementation(); 
+       void publish(Object aImplementation, Scope aScope);
 }
index bcd2635d6cdc6e19742edd2789d9a0c6dcbd0d7b..d3a4e5229483f642be14e8494736f46905f7cd9a 100644 (file)
@@ -46,12 +46,4 @@ public interface RequiredInterface {
         * @return Provider or null if not set. 
         */
        ProvidedInterface getProvider(); 
-       
-       /**
-        * Gets the implementation of the required interface. 
-        * @param <T>
-        * @param aClass Interface type. 
-        * @return Interface implementation or null if not known yet. 
-        */
-       <T> T getImplementation(Class<T> aClass); 
 }
index e65433bef09a70cfb8c93c34b52b05873fc25e6c..ebda473d1b46cabf8e1b90082c0b397f926344e6 100644 (file)
@@ -19,23 +19,25 @@ import junit.framework.TestCase;
 
 public class AbstractComponentTest extends TestCase {
 
-       public void testNotAllComponentsStarted() {
+       public void testNotAllInterfacesStarted() {
                try {
                        Component component = new AbstractComponent("xx",
                                        new ProvidedInterface[] { new DefaultProvidedInterface(
                                                        "xxx", String.class) }, new RequiredInterface[0]) {
                                @Override
-                               protected void doStart() {
+                               protected Object doStart(Scope aScope) {
                                        // Empty, not starting service.
+                                       return null; 
                                }
 
                                @Override
-                               protected void doStop() {
+                               protected void doStop(Object aRuntime) {
                                        // Empty.
                                }
                        };
-                       component.start();
+                       component.start(new DefaultScope(component.getProvidedInterfaces()));
                } catch (SystemAssemblyException e) {
+                       //e.printStackTrace();
                        return;
                }
                fail();
index ca1467cc0fd4afde4ff9327bfcff49668b91035e..07857aee2b7510fa05e87a056676b805b360ace8 100644 (file)
@@ -34,10 +34,12 @@ public class Application extends AbstractComponent {
 
        private EventTracker<String> _tracker;
        private String _string; 
-       private Integer _integer; 
+       private Integer _integer;
+       private double _random; 
        
        public Application() {
-               super("application", new ProvidedInterface[0], required(false)); 
+               super("application", new ProvidedInterface[0], required(false));
+               _random = Math.random();
        }
        
        public Application(boolean aIsOptinal) { 
@@ -50,10 +52,11 @@ public class Application extends AbstractComponent {
        }
 
        @Override
-       protected void doStart() {
+       protected Object doStart(Scope aScope) {
                track("start." + getName());
-               _string = getRequiredInterfaces()[0].getImplementation(String.class);
-           _integer = getRequiredInterfaces()[1].getImplementation(Integer.class);
+               _string = aScope.retrieveInterfaceImplementation(getRequiredInterfaces()[0].getProvider(), String.class);
+           _integer = aScope.retrieveInterfaceImplementation(getRequiredInterfaces()[1].getProvider(), Integer.class);
+           return _random; 
        }
        
        public String getString() {
@@ -65,8 +68,12 @@ public class Application extends AbstractComponent {
        }
        
        @Override
-       protected void doStop() {
-               track("stop." + getName());     
+       protected void doStop(Object aRuntime) {
+               track("stop." + getName());
+               if ( _random != (Double)aRuntime) { 
+                       throw new IllegalArgumentException("Wrong runtime: expected " + _random + " but got " +
+                                       aRuntime);
+               }
        }
        
        private void track(String aString) {
index bcca8cc11bb823c37f01232e81dc78dc2767c76f..74eb43101d0fa461b30e8bd4b31dcf25fd68f479 100644 (file)
@@ -24,6 +24,7 @@ import org.easymock.classextension.ConstructorArgs;
 import org.easymock.classextension.EasyMock;
 import org.easymock.classextension.IMocksControl;
 import org.wamblee.test.AssertionUtils;
+import org.wamblee.test.EasyMockMatchers;
 import org.wamblee.test.EventTracker;
 
 public class ContainerTest extends TestCase {
@@ -83,14 +84,11 @@ public class ContainerTest extends TestCase {
                                environment, application }, new ProvidedInterface[0],
                                new RequiredInterface[0]);
 
-               container.start();
+               Scope scope = container.start();
                AssertionUtils.assertEquals(new String[] { "start.environment",
                                "start.application" }, _tracker.getEvents(
                                Thread.currentThread()).toArray(new String[0]));
-               ProvidedInterface[] envServices = environment.getRunningInterfaces();
-               assertEquals(2, envServices.length);
-               ProvidedInterface[] appServices = application.getRunningInterfaces();
-               assertEquals(0, appServices.length);
+               assertEquals(0, scope.getProvidedInterfaces().length);
                
                assertEquals(environment.getString(), application.getString());
                assertEquals(environment.getInteger(), application.getInteger());
@@ -120,7 +118,7 @@ public class ContainerTest extends TestCase {
                Container system = new Container("all", new Component[] { environment,
                                application }, new ProvidedInterface[0],
                                new RequiredInterface[0]);
-               system.start();
+               Scope runtime = system.start();
                RequiredInterface[] required = system.getRequiredInterfaces();
                assertEquals(0, required.length);
                ProvidedInterface[] provided = system.getProvidedInterfaces();
@@ -131,7 +129,7 @@ public class ContainerTest extends TestCase {
                                Thread.currentThread()).toArray(new String[0]));
                _tracker.clear();
 
-               system.stop();
+               system.stop(runtime);
                AssertionUtils.assertEquals(new String[] { "stop.application",
                                "stop.environment" }, _tracker
                                .getEvents(Thread.currentThread()).toArray(new String[0]));
@@ -206,7 +204,7 @@ public class ContainerTest extends TestCase {
                Container system = new Container("all",
                                new Component[] { application }, new ProvidedInterface[0],
                                application.getRequiredInterfaces());
-               environment.start();
+               environment.start(new DefaultScope(new ProvidedInterface[0]));
                system.getRequiredInterfaces()[0].setProvider(environment
                                .getProvidedInterfaces()[0]);
                system.getRequiredInterfaces()[1].setProvider(environment
@@ -256,9 +254,9 @@ public class ContainerTest extends TestCase {
                Environment environment = new Environment(_tracker);
                Application application = control.createMock(Application.class,
                                new ConstructorArgs(Application.class.getConstructor()),
-                               Application.class.getDeclaredMethod("doStart"));
+                               Application.class.getDeclaredMethod("doStart", Scope.class));
 
-               application.doStart();
+               application.doStart(EasyMockMatchers.anyObject(Scope.class));
                EasyMock.expectLastCall().andThrow(new RuntimeException());
                control.replay();
 
@@ -272,10 +270,6 @@ public class ContainerTest extends TestCase {
                        AssertionUtils.assertEquals(new String[] { "start.environment",
                                        "stop.environment" }, _tracker.getEvents(
                                        Thread.currentThread()).toArray(new String[0]));
-                       ProvidedInterface[] envServices = environment.getRunningInterfaces();
-                       assertEquals(0, envServices.length);
-                       assertNull(environment.getProvidedInterfaces()[0].getImplementation());
-                       assertNull(environment.getProvidedInterfaces()[0].getImplementation());
                        return;
                }
                fail();
@@ -289,17 +283,17 @@ public class ContainerTest extends TestCase {
                // Application 1 will throw an exception while stopping.
                Application application1 = control.createMock(Application.class,
                                new ConstructorArgs(Application.class.getConstructor()),
-                               Application.class.getDeclaredMethod("doStop"));
+                               Application.class.getDeclaredMethod("doStop", Object.class));
 
-               application1.doStop();
+               application1.doStop(EasyMock.anyObject());
                EasyMock.expectLastCall().andThrow(new RuntimeException());
                
                // application 2 will throw an exception while starting
                Application application2 = control.createMock(Application.class,
                                new ConstructorArgs(Application.class.getConstructor()),
-                               Application.class.getDeclaredMethod("doStart"));
+                               Application.class.getDeclaredMethod("doStart", Scope.class));
 
-               application2.doStart();
+               application2.doStart(EasyMockMatchers.anyObject(Scope.class));
                EasyMock.expectLastCall().andThrow(new RuntimeException());
                
                control.replay();
@@ -314,10 +308,6 @@ public class ContainerTest extends TestCase {
                        AssertionUtils.assertEquals(new String[] { "start.environment", 
                                        "stop.environment" }, _tracker.getEvents(
                                        Thread.currentThread()).toArray(new String[0]));
-                       ProvidedInterface[] envServices = environment.getRunningInterfaces();
-                       assertEquals(0, envServices.length);
-                       assertNull(environment.getProvidedInterfaces()[0].getImplementation());
-                       assertNull(environment.getProvidedInterfaces()[0].getImplementation());
                        return;
                }
                fail();
@@ -332,7 +322,10 @@ public class ContainerTest extends TestCase {
                                env.getProvidedInterfaces()[0]); 
                container.getRequiredInterfaces()[1].setProvider(
                                env.getProvidedInterfaces()[1]);
-           container.start();
+               Scope external = new DefaultScope(env.getProvidedInterfaces());
+               env.start(external); 
+               
+           container.start(external);
            assertSame(env.getProvidedInterfaces()[0], container.getRequiredInterfaces()[0].getProvider());
            assertSame(env.getProvidedInterfaces()[1], container.getRequiredInterfaces()[1].getProvider());
            assertSame(env.getProvidedInterfaces()[0], application.getRequiredInterfaces()[0].getProvider());
@@ -345,8 +338,10 @@ public class ContainerTest extends TestCase {
                                new ProvidedInterface[0], Application.required(true));
                Environment env = new Environment();
                container.getRequiredInterfaces()[0].setProvider(
-                               env.getProvidedInterfaces()[0]); 
-           container.start();
+                               env.getProvidedInterfaces()[0]);
+               Scope external = new DefaultScope(new ProvidedInterface[0]); 
+               external.publishInterface(env.getProvidedInterfaces()[0], env.getString());
+           container.start(external);
            assertSame(env.getProvidedInterfaces()[0], container.getRequiredInterfaces()[0].getProvider());
            assertNull(container.getRequiredInterfaces()[1].getProvider());
            assertSame(env.getProvidedInterfaces()[0], application.getRequiredInterfaces()[0].getProvider());
index a7abee5df9639cfb7163fb2de3cb6b6fca9590a9..1c56070caba15c70a0a90a3b91f264b56c3b2714 100644 (file)
@@ -38,9 +38,4 @@ public class DefaultRequiredInterfaceTest extends TestCase {
                                new DefaultRequiredInterface("a", new Class[]{ String.class, Integer.class})));
                
        }
-       
-       public void testGetImplementation() { 
-               RequiredInterface required = new DefaultRequiredInterface("hello", String.class);
-               assertNull(required.getImplementation(String.class));
-       }
 }
index 0f164b625d9bef8c367aeb421da07ee8541bf622..00d947b7504e06948eedb178dc1ef6636cfd7308 100644 (file)
@@ -12,7 +12,7 @@
  * 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 javax.sql.DataSource;
@@ -23,52 +23,55 @@ import org.wamblee.system.core.ProvidedInterface;
 import org.wamblee.system.core.RequiredInterface;
 import org.wamblee.test.EventTracker;
 
-
 public class Environment extends AbstractComponent {
-       
-       private static final ProvidedInterface[] provided() { 
-               return new ProvidedInterface[] { 
-                       new DefaultProvidedInterface("datasource", String.class), 
-                       new DefaultProvidedInterface("integer", Integer.class)
-       };
+
+       private static final ProvidedInterface[] provided() {
+               return new ProvidedInterface[] {
+                               new DefaultProvidedInterface("datasource", String.class),
+                               new DefaultProvidedInterface("integer", Integer.class) };
        }
-       
-       private EventTracker<String> _tracker; 
-       
-       public Environment() { 
+
+       private EventTracker<String> _tracker;
+       private double _random;
+
+       public Environment() {
                super("environment", provided(), new RequiredInterface[0]);
+               _random = Math.random();
        }
-       
-       public Environment(EventTracker aTracker) { 
+
+       public Environment(EventTracker aTracker) {
                this();
-               _tracker = aTracker; 
+               _tracker = aTracker;
        }
-       
-       public Integer getInteger() { 
+
+       public Integer getInteger() {
                return 2;
        }
-       
-       public String getString() { 
+
+       public String getString() {
                return "Hello";
        }
-       
+
        @Override
-       protected void doStart() {
-           addInterface(getProvidedInterfaces()[0], getString());
-           addInterface(getProvidedInterfaces()[1], getInteger());
-           track("start." + getName());
+       protected Object doStart(Scope aScope) {
+               addInterface(getProvidedInterfaces()[0], getString(), aScope);
+               addInterface(getProvidedInterfaces()[1], getInteger(), aScope);
+               track("start." + getName());
+               return _random;
        }
 
        @Override
-       protected void doStop() {
+       protected void doStop(Object aRuntime) {
                track("stop." + getName());
-               removeInterface(getProvidedInterfaces()[0]);
-               removeInterface(getProvidedInterfaces()[1]);
+               if (_random != (Double) aRuntime) {
+                       throw new IllegalArgumentException("Wrong runtime: expected "
+                                       + _random + " but got " + aRuntime);
+               }
        }
-       
+
        private void track(String aString) {
-               if ( _tracker == null ) { 
-                       return; 
+               if (_tracker == null) {
+                       return;
                }
                _tracker.eventOccurred(aString);
        }
index 6c18cc8ce8e776b454eb1a1ea251ecbc3d18ffd1..55433a9c47aac5b793b2da484dafd5298fae7d4b 100644 (file)
@@ -47,7 +47,8 @@ class RequiredServiceBean implements FactoryBean {
 
        @Override
        public Object getObject() throws Exception {
-               return _required.getImplementation(Object.class);
+               return SpringComponent.SCOPE.get().retrieveInterfaceImplementation(
+                               _required.getProvider(), Object.class);
        }
 
        @Override
index 8d50bf280d53b6d09f562519cbecdc20c9526e11..aea9b15aa1a548eacfe9e7c1a39667f684606003 100644 (file)
@@ -26,8 +26,10 @@ import org.springframework.context.support.AbstractApplicationContext;
 import org.springframework.context.support.ClassPathXmlApplicationContext;
 import org.springframework.context.support.GenericApplicationContext;
 import org.wamblee.system.core.AbstractComponent;
+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;
 
 /**
@@ -36,23 +38,17 @@ import org.wamblee.system.core.SystemAssemblyException;
  * 
  * @author Erik Brakkee
  */
-public class SpringComponent extends AbstractComponent {
+public class SpringComponent extends AbstractComponent<Scope> {
+
+       private static final String CONTEXT_KEY = "context";
 
        static final ThreadLocal<SpringComponent> THIS = new ThreadLocal<SpringComponent>();
+       static final ThreadLocal<Scope> SCOPE = new ThreadLocal<Scope>();
 
        private Properties _properties;
        private String[] _configFiles;
        private Map<String, ProvidedInterface> _provided;
        private Map<RequiredInterface, String> _required;
-       /**
-        * Parent application context containing required services.
-        */
-       private GenericApplicationContext _parentContext;
-
-       /**
-        * Application context containing parsed objects.
-        */
-       private AbstractApplicationContext _context;
 
        /**
         * Constructs a spring system.
@@ -98,55 +94,68 @@ public class SpringComponent extends AbstractComponent {
                        setProperty((String) key, aProperties.getProperty((String) key));
                }
        }
+       
+       public Scope start() { 
+               return super.start(new DefaultScope(new ProvidedInterface[0]));
+       }
 
        @Override
-       protected void doStart() {
+       protected Scope doStart(Scope aExternalScope) {
 
                SpringComponent old = THIS.get();
+               Scope oldScope = SCOPE.get();
                THIS.set(this);
+               Scope scope = new DefaultScope(getProvidedInterfaces(), aExternalScope);
+               SCOPE.set(scope);
                try {
-                       _parentContext = new GenericApplicationContext();
+                       GenericApplicationContext parentContext = new GenericApplicationContext();
 
-                       registerRequiredServices();
+                       registerRequiredServices(parentContext);
 
-                       _parentContext.refresh();
+                       parentContext.refresh();
 
-                       parseConfigFiles();
+                       AbstractApplicationContext context = parseConfigFiles(parentContext);
 
-                       _context
+                       context
                                        .addBeanFactoryPostProcessor(new PropertySetter(_properties));
-                       _context.refresh();
+                       context.refresh();
 
-                       exposeProvidedServices();
+                       exposeProvidedServices(context, scope);
+                       
+                       scope.put(CONTEXT_KEY, context);
+                       return scope; 
                } catch (Exception e) {
                        throw new SystemAssemblyException(
                                        "Failed to assemble spring system", e);
                } finally {
                        THIS.set(old);
+                       SCOPE.set(oldScope);
                }
        }
 
-       private void exposeProvidedServices() {
+       private void exposeProvidedServices(AbstractApplicationContext aContext, Scope aScope) {
                // Call addService for each provided service.
 
                for (String name : _provided.keySet()) {
-                       Object svc = _context.getBean(name);
+                       Object svc = aContext.getBean(name);
                        if (svc == null) {
                                throw new IllegalArgumentException(getQualifiedName() + ": service '"
                                                + name + "' is null");
                        }
-                       addInterface(_provided.get(name), svc);
+                       addInterface(_provided.get(name), svc, aScope);
+                       System.out.println("addService " + _provided.get(name) + " " + svc);
+                       aScope.publishInterface(_provided.get(name), svc);
                }
        }
 
-       private void parseConfigFiles() {
+       private AbstractApplicationContext parseConfigFiles(GenericApplicationContext aParentContext) {
                // Parse spring config files
 
-               _context = new ClassPathXmlApplicationContext((String[]) _configFiles,
-                               _parentContext);
+               return new ClassPathXmlApplicationContext((String[]) _configFiles,
+                               aParentContext);
        }
 
-       private void registerRequiredServices() {
+       private void registerRequiredServices(GenericApplicationContext aParentContext) {
                // Register required services in a parent context
                for (RequiredInterface required: getRequiredInterfaces()) { 
                        String beanName = _required.get(required);
@@ -155,15 +164,13 @@ public class SpringComponent extends AbstractComponent {
                        BeanDefinition definition = new RootBeanDefinition(
                                        RequiredServiceBean.class, cargs,
                                        new MutablePropertyValues());
-                       _parentContext.registerBeanDefinition(beanName, definition);
+                       aParentContext.registerBeanDefinition(beanName, definition);
                }
        }
 
        @Override
-       protected void doStop() {
-               _context.close();
-           for (ProvidedInterface provided: getProvidedInterfaces()) { 
-               removeInterface(provided);
-           }
+       protected void doStop(Scope aRuntime) {
+               AbstractApplicationContext context = (AbstractApplicationContext)aRuntime.get(CONTEXT_KEY);
+               context.close();
        }
 }
index 43902d430c12f8130321bebf5eeb2a30d6f808fb..0914c9aa3d5e065c138ebb30387fe5f6ba377221 100644 (file)
@@ -12,7 +12,7 @@
  * 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.spring;
 
 import java.io.IOException;
@@ -25,8 +25,10 @@ import junit.framework.TestCase;
 import org.wamblee.io.ClassPathResource;
 import org.wamblee.system.core.DefaultProvidedInterface;
 import org.wamblee.system.core.DefaultRequiredInterface;
+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;
 
 public class SpringComponentTest extends TestCase {
@@ -34,7 +36,7 @@ public class SpringComponentTest extends TestCase {
        private static final String HELLO_SERVICE_SPRING_XML = "test.org.wamblee.system.spring.xml";
        private static final String HELLO_SERVICE_SPRING_WITH_REQS_XML = "test.org.wamblee.system.springWithRequirements.xml";
        private static final String HELLO_SERVICE_SPRING_WITH_PROPERTIES_XML = "test.org.wamblee.system.springWithProperties.xml";
-    private static final String PROPERTY_FILE = "test.org.wamblee.system.spring.properties";
+       private static final String PROPERTY_FILE = "test.org.wamblee.system.spring.properties";
 
        @Override
        protected void setUp() throws Exception {
@@ -46,46 +48,43 @@ public class SpringComponentTest extends TestCase {
                                new String[] { HELLO_SERVICE_SPRING_XML },
                                new HashMap<String, ProvidedInterface>(),
                                new HashMap<RequiredInterface, String>());
-               system.start();
-               ProvidedInterface[] services = system.getRunningInterfaces();
-               assertEquals(0, services.length);
-               
-               system.stop();
+               Scope runtime = system.start();
+               assertEquals(0, runtime.getProvidedInterfaces().length);
+
+               system.stop(runtime);
        }
 
        public void testOneProvidedService() {
                Map<String, ProvidedInterface> provided = new HashMap<String, ProvidedInterface>();
-               provided.put("helloService", new DefaultProvidedInterface(
-                               "hello", HelloService.class));
+               provided.put("helloService", new DefaultProvidedInterface("hello",
+                               HelloService.class));
 
-               SpringComponent system = new SpringComponent("system", 
+               SpringComponent system = new SpringComponent("system",
                                new String[] { HELLO_SERVICE_SPRING_XML }, provided,
                                new HashMap<RequiredInterface, String>());
-               system.start();
-               ProvidedInterface[] services = system.getRunningInterfaces();
+               Scope runtime = system.start();
+               ProvidedInterface[] services = runtime.getProvidedInterfaces();
                assertEquals(1, services.length);
-               assertTrue(services[0].getImplementation() instanceof HelloService);
-               assertEquals("Hello world!", ((HelloService)services[0].getImplementation())
-                               .say());
-               system.stop();
+               Object service = runtime.retrieveInterfaceImplementation(services[0], Object.class);
+               assertTrue(service instanceof HelloService);
+               assertEquals("Hello world!", ((HelloService) service).say());
+               system.stop(runtime);
        }
-       
+
        public void testWithProperties() throws IOException {
                Map<String, ProvidedInterface> provided = new HashMap<String, ProvidedInterface>();
-               provided.put("helloService", new DefaultProvidedInterface(
-                               "hello", HelloService.class));
-               SpringComponent system = new SpringComponent("system", 
+               provided.put("helloService", new DefaultProvidedInterface("hello",
+                               HelloService.class));
+               SpringComponent system = new SpringComponent("system",
                                new String[] { HELLO_SERVICE_SPRING_WITH_PROPERTIES_XML },
-                               provided,
-                               new HashMap<RequiredInterface, String>());
+                               provided, new HashMap<RequiredInterface, String>());
                Properties props = new Properties();
                props.load(new ClassPathResource(PROPERTY_FILE).getInputStream());
                system.addProperties(props);
-               
-               system.start();
-               ProvidedInterface[] services = system.getRunningInterfaces();
-               assertEquals("Property Value", 
-                               ((HelloService)services[0].getImplementation()).say());
+
+               Scope scope = system.start();
+               ProvidedInterface[] services = scope.getProvidedInterfaces();
+               assertEquals("Property Value", scope.retrieveInterfaceImplementation(services[0], HelloService.class).say());
        }
 
        public void testWithMissingRequirement() {
@@ -96,7 +95,7 @@ public class SpringComponentTest extends TestCase {
                                        new HashMap<RequiredInterface, String>());
                        system.start();
                } catch (SystemAssemblyException e) {
-                       //e.printStackTrace();
+                       // e.printStackTrace();
                        return;
                }
                fail();
@@ -109,41 +108,44 @@ public class SpringComponentTest extends TestCase {
                SpringComponent system = new SpringComponent("system",
                                new String[] { HELLO_SERVICE_SPRING_WITH_REQS_XML },
                                new HashMap<String, ProvidedInterface>(), required);
-               
-               HelloService helloObject = new HelloService("ladida"); 
-               ProvidedInterface helloService = new DefaultProvidedInterface("hello", HelloService.class);
-               helloService.publish(helloObject);
+
+               HelloService helloObject = new HelloService("ladida");
+               ProvidedInterface helloService = new DefaultProvidedInterface("hello",
+                               HelloService.class);
+               Scope scope = new DefaultScope(new ProvidedInterface[]{ helloService });
+               scope.publishInterface(helloService, helloObject);
                system.getRequiredInterfaces()[0].setProvider(helloService);
-               
-               system.start();
-               system.stop();
+
+               Scope runtime = system.start(scope);
+               system.stop(runtime);
        }
-       
+
        public void testWithRequirementAndProvidedService() {
                Map<RequiredInterface, String> required = new HashMap<RequiredInterface, String>();
                required.put(new DefaultRequiredInterface("hello", HelloService.class),
                                "helloService");
-               Map<String,ProvidedInterface> provided = new HashMap<String, ProvidedInterface>();
+               Map<String, ProvidedInterface> provided = new HashMap<String, ProvidedInterface>();
                provided.put("blaService", new DefaultProvidedInterface("bla",
                                BlaService.class));
 
                SpringComponent system = new SpringComponent("system",
-                               new String[] { HELLO_SERVICE_SPRING_WITH_REQS_XML },
-                               provided, required);
-               
-               HelloService helloObject = new HelloService("ladida"); 
-               ProvidedInterface helloService = 
-                       new DefaultProvidedInterface("hello", HelloService.class);
-               helloService.publish(helloObject);
+                               new String[] { HELLO_SERVICE_SPRING_WITH_REQS_XML }, provided,
+                               required);
+
+               HelloService helloObject = new HelloService("ladida");
+               ProvidedInterface helloService = new DefaultProvidedInterface("hello",
+                               HelloService.class);
+               Scope scope = new DefaultScope(new ProvidedInterface[] {  helloService });
+               scope.publishInterface(helloService, helloObject);
                system.getRequiredInterfaces()[0].setProvider(helloService);
-               system.start();
-               ProvidedInterface started = system.getProvidedInterfaces()[0];
-               
-           assertNotNull(started.getImplementation());
-           assertTrue(started.getImplementation() instanceof BlaService);
-               assertEquals("ladida", 
-                               ((BlaService)started.getImplementation()).execute());
-               system.stop();
+               Scope runtime = system.start(scope);
+               ProvidedInterface started = runtime.getProvidedInterfaces()[0];
+
+               Object impl = runtime.retrieveInterfaceImplementation(started, BlaService.class);
+               assertNotNull(impl);
+               assertTrue(impl instanceof BlaService);
+               assertEquals("ladida", ((BlaService)impl).execute());
+               system.stop(runtime);
        }
 
 }