added the basic for a more modular setup of applications and test code.
authorerik <erik@77661180-640e-0410-b3a8-9f9b13e6d0e0>
Sat, 22 Mar 2008 22:47:55 +0000 (22:47 +0000)
committererik <erik@77661180-640e-0410-b3a8-9f9b13e6d0e0>
Sat, 22 Mar 2008 22:47:55 +0000 (22:47 +0000)
A system is now separated in subsystems with clearly defined interfaces.
This makes it easy to substitute different systems for different
environment (such as test) or to construct different behavior.
It will avoid the approach with one big beanRefContext.xml will all
configuration inside.

16 files changed:
trunk/.classpath
trunk/mythtv/war/src/main/resources/org.wamblee.mythtv.properties
trunk/pom.xml
trunk/system/pom.xml [new file with mode: 0644]
trunk/system/src/main/java/org/wamblee/system/AbstractSubSystem.java [new file with mode: 0644]
trunk/system/src/main/java/org/wamblee/system/CompositeSystem.java [new file with mode: 0644]
trunk/system/src/main/java/org/wamblee/system/DefaultService.java [new file with mode: 0644]
trunk/system/src/main/java/org/wamblee/system/DefaultServiceDescriptor.java [new file with mode: 0644]
trunk/system/src/main/java/org/wamblee/system/Service.java [new file with mode: 0644]
trunk/system/src/main/java/org/wamblee/system/ServiceDescriptor.java [new file with mode: 0644]
trunk/system/src/main/java/org/wamblee/system/SubSystem.java [new file with mode: 0644]
trunk/system/src/main/java/org/wamblee/system/SystemAssembler.java [new file with mode: 0644]
trunk/system/src/main/java/org/wamblee/system/SystemAssemblyException.java [new file with mode: 0644]
trunk/system/src/test/java/org/wamblee/system/Application.java [new file with mode: 0644]
trunk/system/src/test/java/org/wamblee/system/Environment.java [new file with mode: 0644]
trunk/system/src/test/java/org/wamblee/system/SystemAssemblerTest.java [new file with mode: 0644]

index 40a31d71ec9b78115817385a51c8267571c76e24..216ffd34a8ab58e26e50d5a0961ffbd392c99f95 100644 (file)
@@ -4,6 +4,8 @@
        <classpathentry kind="src" path="support/src/test/java"/>
        <classpathentry excluding="**" kind="src" output="support/src/main/resources" path="support/src/main/resources"/>
        <classpathentry excluding="**" kind="src" output="support/src/test/resources" path="support/src/test/resources"/>
+       <classpathentry kind="src" path="system/src/main/java"/>
+       <classpathentry kind="src" path="system/src/test/java"/>
        <classpathentry kind="src" path="security/src/main/java"/>
        <classpathentry kind="src" path="security/src/test/java"/>
        <classpathentry excluding="**" kind="src" output="security/src/test/resources" path="security/src/test/resources"/>
index c7bf5062989ae8173414d931a327f41cf0a3fa85..85f9a0e88d6f9af65924cde26669e9d075ae626e 100644 (file)
@@ -1,8 +1,8 @@
 
 org.wamblee.mythtv.datasource=jdbc/mythtv
-org.wamblee.mythtv.pollinterval=2
-#org.wamblee.mythtv.monitordir=/data/vcr
-#org.wamblee.mythtv.linkdir=/data/vcr/links
+org.wamblee.mythtv.pollinterval=120
+org.wamblee.mythtv.monitordir=/data/vcr
+org.wamblee.mythtv.linkdir=/data/vcr/links
 
-org.wamblee.mythtv.monitordir=/ext/home/erik/java/workspace/utils/mythtv/testdata/input
-org.wamblee.mythtv.linkdir=/ext/home/erik/java/workspace/utils/mythtv/testdata/links
+#org.wamblee.mythtv.monitordir=/ext/home/erik/java/workspace/utils/mythtv/testdata/input
+#org.wamblee.mythtv.linkdir=/ext/home/erik/java/workspace/utils/mythtv/testdata/links
index 981a892b0c8fefe1b846c3bb95996c9858b0ed87..f7ec73e884a959bd31dc86a8d31966239d8b4c76 100644 (file)
@@ -10,6 +10,7 @@
     <url>http://wamblee.org</url>
     <modules>
         <module>support</module>
+        <module>system</module>
         <module>hibernate-jpa</module>
         <module>security</module>
         <module>socketproxy</module>
diff --git a/trunk/system/pom.xml b/trunk/system/pom.xml
new file mode 100644 (file)
index 0000000..c70dddc
--- /dev/null
@@ -0,0 +1,24 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+  <parent>
+    <groupId>org.wamblee</groupId>
+    <artifactId>wamblee-utils</artifactId>
+    <version>0.2-SNAPSHOT</version>
+  </parent>
+
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>org.wamblee</groupId>
+  <artifactId>wamblee-system</artifactId>
+  <packaging>jar</packaging>
+  <name>wamblee.org system</name>
+  <url>http://wamblee.org</url>
+  <dependencies>
+    <dependency>
+      <groupId>commons-logging</groupId>
+      <artifactId>commons-logging</artifactId>
+    </dependency>
+  </dependencies>
+
+</project>
diff --git a/trunk/system/src/main/java/org/wamblee/system/AbstractSubSystem.java b/trunk/system/src/main/java/org/wamblee/system/AbstractSubSystem.java
new file mode 100644 (file)
index 0000000..0837d5f
--- /dev/null
@@ -0,0 +1,92 @@
+package org.wamblee.system;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Abstract subsystem class making it easy to implement new 
+ * subsystems.
+ */
+public abstract class AbstractSubSystem implements SubSystem {
+       
+       private static final Log LOG = LogFactory.getLog(AbstractSubSystem.class);
+       
+       private String _name; 
+       private List<ServiceDescriptor> _provided;
+       private List<ServiceDescriptor> _required;
+       private Map<ServiceDescriptor, Service> _running; 
+       
+       /**
+        * Constructs the subsystem. 
+        * @param aName Name of the system. 
+        * @param aProvided Provided services. 
+        * @param aRequired Required services. 
+        */
+       protected AbstractSubSystem(String aName, ServiceDescriptor[] aProvided, 
+                       ServiceDescriptor[] aRequired) { 
+               _name = aName; 
+               _provided = new ArrayList<ServiceDescriptor>(); 
+               _provided.addAll(Arrays.asList(aProvided));
+               _required = new ArrayList<ServiceDescriptor>(); 
+               _required.addAll(Arrays.asList(aRequired));
+               _running = new HashMap<ServiceDescriptor, Service>(); 
+       }
+
+       @Override
+       public final String getName() {
+               return _name; 
+       }
+
+       @Override
+       public final ServiceDescriptor[] getProvidedServices() {
+               return _provided.toArray(new ServiceDescriptor[0]); 
+       }
+
+       @Override
+       public final ServiceDescriptor[] getRequiredServices() {
+               return _required.toArray(new ServiceDescriptor[0]);
+       }
+
+       @Override
+       public final Service[] initialize(String aContext, Service[] aRequiredServices) {
+               LOG.info("Initializing '" + aContext + "." + _name + "' with " + Arrays.asList(aRequiredServices));
+               doInitialize(aContext + "." + getName(), aRequiredServices); 
+               return _running.values().toArray(new Service[0]);
+       }
+       
+       /**
+        * Must be implemented for initializing the subsystem. 
+        * The implementation must call {@link #addService(Service)}
+        * for each service that is started. 
+        * @param aRequiredServices Services that are already running
+        * from other subsystems that may be used. 
+        */
+       protected abstract void doInitialize(String aContext, Service[] aRequiredServices); 
+       
+       /**
+        * Implementations must call this method to indicate that
+        * a new service has been started. 
+        * @param aService Service. 
+        */
+       protected final void addService(String aContext, Service aService) { 
+               LOG.info(aContext + ": service '" + aService + "' started.");
+               _running.put(aService.getDescriptor(), aService);
+       }
+
+       @Override
+       public Service[] getRunningServices() {
+               return _running.values().toArray(new Service[0]);
+       }
+       
+       @Override
+       public String toString() {
+               return _name; 
+       }
+
+}
diff --git a/trunk/system/src/main/java/org/wamblee/system/CompositeSystem.java b/trunk/system/src/main/java/org/wamblee/system/CompositeSystem.java
new file mode 100644 (file)
index 0000000..f3b1bf7
--- /dev/null
@@ -0,0 +1,95 @@
+package org.wamblee.system;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import sun.util.LocaleServiceProviderPool.LocalizedObjectGetter;
+
+/**
+ * Composite system consisting of multiple subsystems. 
+ */
+public class CompositeSystem extends AbstractSubSystem {
+
+       private static final Log LOG = LogFactory.getLog(CompositeSystem.class);
+
+       private SubSystem[] _systems;
+
+       /**
+        * Construcst the composite system. 
+        * @param aName Name of the system. 
+        * @param aSystems Subsystems. 
+        * @param aProvided Provided services of the system. 
+        * @param aRequired Required services by the system. 
+        */
+       public CompositeSystem(String aName, SubSystem[] aSystems,
+                       ServiceDescriptor[] aProvided, ServiceDescriptor[] aRequired) {
+               super(aName, aProvided, aRequired);
+               _systems = aSystems;
+               validate();
+       }
+
+       /**
+        * Validates the subsystems 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.  
+        */
+       private void validate() {
+               List<ServiceDescriptor> provided = new ArrayList<ServiceDescriptor>();
+               for (SubSystem system : _systems) {
+                       provided.addAll(Arrays.asList(system.getProvidedServices()));
+               }
+
+               List<ServiceDescriptor> required = new ArrayList<ServiceDescriptor>();
+               for (SubSystem system : _systems) {
+                       required.addAll(Arrays.asList(system.getRequiredServices()));
+               }
+
+               for (ServiceDescriptor service : getProvidedServices()) {
+                       if (!(provided.contains(service))) {
+                               throw new SystemAssemblyException(getName() + ": Service '" + service
+                                               + "' is not provided by any of the subsystems");
+                       }
+               }
+
+               for (ServiceDescriptor service : getRequiredServices()) {
+                       if (!(required.contains(service))) {
+                               info("Service '"
+                                               + service
+                                               + "' indicated as required is not actually required by any of the subsystems");
+                       }
+               }
+
+               List<ServiceDescriptor> reallyRequired = new ArrayList<ServiceDescriptor>(
+                               required);
+               for (ServiceDescriptor service : provided) {
+                       reallyRequired.remove(service); 
+               }
+               for (ServiceDescriptor service: getRequiredServices()) { 
+                       reallyRequired.remove(service); 
+               }
+               for (ServiceDescriptor service: reallyRequired) { 
+                       throw new SystemAssemblyException(getName() + ": " + "Service '" + service + "' is not provided internally and is not indicated as required for this sub system");
+               }
+       }
+
+       @Override
+       protected void doInitialize(String aContext, Service[] aRequiredServices) {
+               List<ServiceDescriptor> descriptors = new ArrayList<ServiceDescriptor>();
+               for (Service service : aRequiredServices) {
+                       descriptors.add(service.getDescriptor());
+               }
+               SystemAssembler assembler = new SystemAssembler(getName(), _systems,
+                               descriptors.toArray(new ServiceDescriptor[0]));
+               assembler.start(aRequiredServices);
+       }
+
+       private void info(String aMsg) {
+               LOG.info(getName() + ": " + aMsg);
+       }
+
+}
diff --git a/trunk/system/src/main/java/org/wamblee/system/DefaultService.java b/trunk/system/src/main/java/org/wamblee/system/DefaultService.java
new file mode 100644 (file)
index 0000000..fb2128e
--- /dev/null
@@ -0,0 +1,35 @@
+package org.wamblee.system;
+
+/**
+ * Default service implementation.
+ */
+public class DefaultService implements Service {
+       
+       private ServiceDescriptor _descriptor; 
+       private Object _service; 
+       
+       /**
+        * Constructs the service. 
+        * @param aDescriptor Descriptor to use. 
+        * @param aService Service. 
+        */
+       public DefaultService(ServiceDescriptor aDescriptor, Object aService) { 
+               _descriptor = aDescriptor; 
+               _service = aService;
+       }
+
+       @Override
+       public ServiceDescriptor getDescriptor() {
+               return _descriptor; 
+       }
+
+       @Override
+       public <T> T reference(Class<T> aClass) {
+               return (T)_service; 
+       }
+
+       @Override
+       public String toString() {
+               return "(" + _descriptor + ", " + _service + ")"; 
+       }
+}
diff --git a/trunk/system/src/main/java/org/wamblee/system/DefaultServiceDescriptor.java b/trunk/system/src/main/java/org/wamblee/system/DefaultServiceDescriptor.java
new file mode 100644 (file)
index 0000000..82f1456
--- /dev/null
@@ -0,0 +1,41 @@
+package org.wamblee.system;
+
+/**
+ * Default implementation of a service descriptor.
+ */
+public class DefaultServiceDescriptor implements ServiceDescriptor {
+       
+       private Class _class; 
+       
+       /**
+        * Constructs the descriptor. 
+        * @param aClass Type of service. 
+        */
+       public DefaultServiceDescriptor(Class aClass) { 
+               _class = aClass; 
+       }
+
+       @Override
+       public Class getInterfaceType() {
+               return _class;
+       }
+       
+       @Override
+       public boolean equals(Object obj) {
+               if ( !(obj instanceof DefaultServiceDescriptor)) { 
+                       return false; 
+               }
+               DefaultServiceDescriptor descr = (DefaultServiceDescriptor)obj; 
+               return _class.equals(descr._class);
+       }
+
+       @Override
+       public int hashCode() {
+               return _class.hashCode(); 
+       }
+       
+       @Override
+       public String toString() {
+               return _class.getName().toString();
+       }
+}
diff --git a/trunk/system/src/main/java/org/wamblee/system/Service.java b/trunk/system/src/main/java/org/wamblee/system/Service.java
new file mode 100644 (file)
index 0000000..0db4851
--- /dev/null
@@ -0,0 +1,18 @@
+package org.wamblee.system;
+
+/**
+ * Represents a running service.
+ */
+public interface Service {
+       /**
+        * Gets the descriptor of the service. 
+        * @return Descriptor. 
+        */
+       ServiceDescriptor getDescriptor(); 
+       
+       /**
+        * Returns a reference to the running service. 
+        * @return Service. 
+        */
+       <T> T reference(Class<T> aClass);
+}
diff --git a/trunk/system/src/main/java/org/wamblee/system/ServiceDescriptor.java b/trunk/system/src/main/java/org/wamblee/system/ServiceDescriptor.java
new file mode 100644 (file)
index 0000000..793414c
--- /dev/null
@@ -0,0 +1,18 @@
+package org.wamblee.system;
+
+/**
+ * Service descriptor providing a description and characteristics
+ * of the provided service. 
+ * 
+ * NOTE: The current implemention only stores the type of the 
+ * descriptor but his can be extended towards more complex rules
+ * for matching services. 
+ */
+public interface ServiceDescriptor {
+       
+       /**
+        * Returns the service type. 
+        * @return Service type. 
+        */
+       Class getInterfaceType();
+}
diff --git a/trunk/system/src/main/java/org/wamblee/system/SubSystem.java b/trunk/system/src/main/java/org/wamblee/system/SubSystem.java
new file mode 100644 (file)
index 0000000..9d68ee4
--- /dev/null
@@ -0,0 +1,46 @@
+package org.wamblee.system;
+
+/**
+ * A sub system represents a part of a system that required a 
+ * number of services and provides a number of services. 
+ */
+public interface SubSystem {
+       
+       /**
+        * Gets the name of the subsystem.
+        * @return Subsystem name. 
+        */
+       String getName();
+
+       /**
+        * Gets a description of the provided interfaces. 
+        * @return Provided interfaces. 
+        */
+       ServiceDescriptor[] getProvidedServices();
+       
+       /**
+        * Gets a description of the required interfaces. 
+        * @return Required interfaces. 
+        */
+       ServiceDescriptor[] getRequiredServices();
+
+       
+       /**
+        * Initialises the subsytem by starting all the services that
+        * it described as provided. 
+        * @param aContext Unique name for the subsystem. 
+        * @param aRequiredServices Running services from other 
+        * subsystems that are required by this subsystem. 
+        * @return Services that are running in the subsystem. 
+        */
+       Service[] initialize(String aContext, Service[] aRequiredServices);
+       
+       /**
+        * 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
+        */
+       Service[] getRunningServices();
+}
diff --git a/trunk/system/src/main/java/org/wamblee/system/SystemAssembler.java b/trunk/system/src/main/java/org/wamblee/system/SystemAssembler.java
new file mode 100644 (file)
index 0000000..3bff056
--- /dev/null
@@ -0,0 +1,113 @@
+package org.wamblee.system;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Assembler to control multiple subsystems. It makes sure that
+ * all dependencies are met and controls the order in which systems
+ * are initialized. 
+ */
+public class SystemAssembler {
+
+       private static final Log LOG = LogFactory.getLog(SystemAssembler.class);
+
+       private static final String ROOT_CONTEXT_NAME = "root";
+       private String _context;
+       private SubSystem[] _systems;
+
+       /**
+        * Constructs the assembler. 
+        * @param aSystems Systems that must be assembled.
+        * @param aAvailableServices Available services from other systems
+        *  outside of the systems that this assembler manages.
+        */
+       public SystemAssembler(SubSystem[] aSystems, ServiceDescriptor[] aAvailableServices) {
+               this(ROOT_CONTEXT_NAME, aSystems, aAvailableServices);
+       }
+
+       /**
+        * Constructs the assembler. 
+        * @param aContext Context (unique name) of the assembler. 
+        * @param aSystems Systems that must be assembled. 
+        * @param aAvailableServices Available services from other systems
+        *  outside of the systems that this assembler manages.
+        */
+       public SystemAssembler(String aContext, SubSystem[] aSystems, 
+                       ServiceDescriptor[] aAvailableServices) {
+               _context = aContext;
+               _systems = aSystems;
+               validate(aAvailableServices);
+       }
+
+       /**
+        * Determines if the systems are ordered appropriately so that all
+        * dependencies are met.
+        */
+       private void validate(ServiceDescriptor[] aDescriptors) throws SystemAssemblyException {
+
+               List<ServiceDescriptor> allProvided = new ArrayList<ServiceDescriptor>();
+               for (ServiceDescriptor descriptor: aDescriptors) { 
+                       allProvided.add(descriptor);
+               }
+               for (SubSystem system : _systems) {
+                       // Check if all required services are already provided by earlier
+                       // systems.
+                       ServiceDescriptor[] required = system.getRequiredServices();
+                       for (ServiceDescriptor descriptor : required) {
+                               if (!(allProvided.contains(descriptor))) {
+                                       throw new SystemAssemblyException(
+                                                       "Service '"
+                                                                       + descriptor
+                                                                       + "' required by system '"
+                                                                       + system
+                                                                       + "' is not provided by systems that are started earlier");
+                               }
+                       }
+
+                       // add all provided services
+                       ServiceDescriptor[] provided = system.getProvidedServices();
+                       for (ServiceDescriptor descriptor : provided) {
+                               allProvided.add(descriptor);
+                       }
+               }
+       }
+
+       /**
+        * Starts the subsystems.
+        * @param aRequiredServices Services that are available from
+        *  other systems that have been started before. 
+        */
+       public void start(Service[] aRequiredServices) {
+               LOG.info("Starting '" + _context + "'");
+               Map<ServiceDescriptor, Service> allProvided = new HashMap<ServiceDescriptor, Service>();
+
+               for (Service service : aRequiredServices) {
+                       allProvided.put(service.getDescriptor(), service);
+               }
+               for (SubSystem system : _systems) {
+                       ServiceDescriptor[] descriptors = system.getRequiredServices();
+                       List<Service> services = new ArrayList<Service>();
+                       for (ServiceDescriptor descriptor : descriptors) {
+                               Service required = allProvided.get(descriptor);
+                               if (required == null) {
+                                       throw new SystemAssemblyException("Service '" + descriptor
+                                                       + "' required by '" + system + "' is null.");
+                               }
+                               services.add(required);
+                       }
+                       Service[] provided = system.initialize(_context, services
+                                       .toArray(new Service[0]));
+                       for (Service service : provided) {
+                               allProvided.put(service.getDescriptor(), service);
+                       }
+
+               }
+       }
+
+}
diff --git a/trunk/system/src/main/java/org/wamblee/system/SystemAssemblyException.java b/trunk/system/src/main/java/org/wamblee/system/SystemAssemblyException.java
new file mode 100644 (file)
index 0000000..80e7db7
--- /dev/null
@@ -0,0 +1,24 @@
+package org.wamblee.system;
+
+/**
+ * Exception thrown when an error occurs in assembling the systems.
+ */
+public class SystemAssemblyException extends RuntimeException {
+
+       /**
+        * Constructs the exception. 
+        * @param aMsg Message. 
+        */
+       public SystemAssemblyException(String aMsg) { 
+               super(aMsg); 
+       }
+       
+       /**
+        * Constructs the exception. 
+        * @param aMsg Message
+        * @param aCause Cause. 
+        */
+       public SystemAssemblyException(String aMsg, Throwable aCause) { 
+               super(aMsg, aCause);
+       }
+}
diff --git a/trunk/system/src/test/java/org/wamblee/system/Application.java b/trunk/system/src/test/java/org/wamblee/system/Application.java
new file mode 100644 (file)
index 0000000..1a725b3
--- /dev/null
@@ -0,0 +1,21 @@
+package org.wamblee.system;
+
+import javax.sql.DataSource;
+
+public class Application extends AbstractSubSystem {
+       private static final ServiceDescriptor[] REQUIRED = 
+               new ServiceDescriptor[] { 
+                       new DefaultServiceDescriptor(DataSource.class), 
+                       new DefaultServiceDescriptor(Integer.class)
+       };
+       
+       public Application() {
+               super("application", new ServiceDescriptor[0], REQUIRED); 
+       }
+
+       @Override
+       protected void doInitialize(String aContext, Service[] aRequiredServices) {
+               // Empty, no services provided externally. 
+       }
+
+}
diff --git a/trunk/system/src/test/java/org/wamblee/system/Environment.java b/trunk/system/src/test/java/org/wamblee/system/Environment.java
new file mode 100644 (file)
index 0000000..bd014c5
--- /dev/null
@@ -0,0 +1,23 @@
+package org.wamblee.system;
+
+import javax.sql.DataSource;
+
+
+public class Environment extends AbstractSubSystem {
+       
+       private static final ServiceDescriptor[] PROVIDED = 
+               new ServiceDescriptor[] { 
+                       new DefaultServiceDescriptor(DataSource.class), 
+                       new DefaultServiceDescriptor(Integer.class)
+       };
+
+       public Environment() { 
+               super("environment", PROVIDED, new ServiceDescriptor[0]); 
+       }
+       
+       @Override
+       protected void doInitialize(String aContext, Service[] aRequiredServices) {
+           addService(aContext, new DefaultService(PROVIDED[0], new Integer(1)));
+           addService(aContext, new DefaultService(PROVIDED[1], new Integer(2)));
+       }
+}
diff --git a/trunk/system/src/test/java/org/wamblee/system/SystemAssemblerTest.java b/trunk/system/src/test/java/org/wamblee/system/SystemAssemblerTest.java
new file mode 100644 (file)
index 0000000..94bf63c
--- /dev/null
@@ -0,0 +1,105 @@
+package org.wamblee.system;
+
+import junit.framework.TestCase;
+
+public class SystemAssemblerTest extends TestCase {
+
+       public void testEnvironmentApplication() {
+               SubSystem environment = new Environment();
+               SubSystem application = new Application();
+               SystemAssembler assembler = new SystemAssembler(new SubSystem[] {
+                               environment, application }, new ServiceDescriptor[0]);
+               assembler.start(new Service[0]);
+               Service[] envServices = environment.getRunningServices();
+               assertEquals(2, envServices.length);
+               Service[] appServices = environment.getRunningServices();
+               assertEquals(2, appServices.length);
+       }
+
+       public void testApplicationEnvironment() {
+               try {
+                       SubSystem environment = new Environment();
+                       SubSystem application = new Application();
+                       SystemAssembler assembler = new SystemAssembler(new SubSystem[] {
+                                       application, environment }, new ServiceDescriptor[0]);
+                       assembler.start(new Service[0]);
+               } catch (SystemAssemblyException e) {
+                       // e.printStackTrace();
+                       return;
+               }
+               fail();
+       }
+
+       public void testComposite() {
+               SubSystem environment = new Environment();
+               SubSystem application = new Application();
+               CompositeSystem system = new CompositeSystem("all", new SubSystem[] {
+                               environment, application }, new ServiceDescriptor[0],
+                               new ServiceDescriptor[0]);
+               system.initialize("root", new Service[0]);
+               ServiceDescriptor[] required = system.getRequiredServices();
+               assertEquals(0, required.length);
+               ServiceDescriptor[] provided = system.getProvidedServices();
+               assertEquals(0, provided.length);
+       }
+
+       public void testCompositeWithWrongProvidedInfo() {
+               try {
+                       SubSystem environment = new Environment();
+                       SubSystem application = new Application();
+                       CompositeSystem system = new CompositeSystem("all",
+                                       new SubSystem[] { environment, application },
+                                       new ServiceDescriptor[] { new DefaultServiceDescriptor(
+                                                       String.class) }, new ServiceDescriptor[0]);
+               } catch (SystemAssemblyException e) {
+                       return;
+               }
+               fail();
+       }
+
+       public void testCompositeWithSuperfluousRequiredInfo() {
+               SubSystem environment = new Environment();
+               SubSystem application = new Application();
+               CompositeSystem system = new CompositeSystem("all", new SubSystem[] {
+                               environment, application }, new ServiceDescriptor[0],
+                               new ServiceDescriptor[] { new DefaultServiceDescriptor(
+                                               String.class) });
+               system.initialize("root", new Service[0]);
+               ServiceDescriptor[] required = system.getRequiredServices();
+               assertEquals(1, required.length);
+               ServiceDescriptor[] provided = system.getProvidedServices();
+               assertEquals(0, provided.length);
+       }
+
+       public void testCompositeWithExternalDependencesNotProvided() {
+               try {
+                       SubSystem environment = new Environment();
+                       SubSystem application = new Application();
+                       CompositeSystem system = new CompositeSystem("all",
+                                       new SubSystem[] { application }, new ServiceDescriptor[0],
+                                       application.getRequiredServices());
+                       system.initialize("root", new Service[0]);
+               } catch (SystemAssemblyException e) {
+                       return;
+               }
+               fail();
+
+       }
+
+       public void testCompositeWithExternalDependencesProvided() {
+
+               SubSystem environment = new Environment();
+               SubSystem application = new Application();
+               CompositeSystem system = new CompositeSystem("all",
+                               new SubSystem[] { application }, new ServiceDescriptor[0],
+                               application.getRequiredServices());
+               Service[] envServices = environment.initialize("env", new Service[0]);
+               system.initialize("root", envServices);
+               ServiceDescriptor[] required = system.getRequiredServices();
+               assertEquals(2, required.length);
+               ServiceDescriptor[] provided = system.getProvidedServices();
+               assertEquals(0, provided.length);
+
+       }
+
+}