Implemented optinality of interfaces.
[utils] / system / general / src / main / java / org / wamblee / system / core / Container.java
index 8436ebca4174ba7090966756a5b6b2e5b8923ea5..81dcb6a2c95bc106ce2195e384eb7c521d07b5c6 100644 (file)
@@ -18,6 +18,7 @@ package org.wamblee.system.core;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Iterator;
 import java.util.List;
 
 import org.apache.commons.logging.Log;
@@ -78,7 +79,7 @@ public class Container extends AbstractComponent {
                for (Component component : aSystems) {
                        component.addContext(getQualifiedName());
                }
-               validate(aRequired);
+               validate();
        }
 
        /**
@@ -87,7 +88,7 @@ public class Container extends AbstractComponent {
         * that cannot be provided. Also logs a warning in case of superfluous
         * requirements.
         */
-       private void validate(RequiredInterface[] aRequired) {
+       private void validate() {
                List<ProvidedInterface> provided = new ArrayList<ProvidedInterface>();
                for (Component system : _systems) {
                        provided.addAll(Arrays.asList(system.getProvidedInterfaces()));
@@ -98,79 +99,105 @@ public class Container extends AbstractComponent {
                        required.addAll(Arrays.asList(system.getRequiredInterfaces()));
                }
 
-               for (ProvidedInterface service : getProvidedInterfaces()) {
-                       if (!(provided.contains(service))) {
-                               throw new SystemAssemblyException(getName() + ": Service '"
-                                               + service
-                                               + "' is not provided by any of the subsystems");
-                       }
-               }
+               validateProvidedInterfaces(provided);
 
-               for (RequiredInterface service : getRequiredInterfaces()) {
-                       if (!(required.contains(service))) {
-                               info("Service '"
-                                               + service
-                                               + "' indicated as required is not actually required by any of the subsystems");
-                       }
+               validateRequiredInterfaces(required);
+
+               List<RequiredInterface> reallyRequired = validateRequiredProvidedMatch(
+                               provided, required);
+               
+               String missingRequired = "";
+               for (RequiredInterface service : reallyRequired) {
+                       missingRequired += service + "\n";
+               }
+               if (missingRequired.length() > 0) {
+                       throw new SystemAssemblyException(getName()
+                                       + ": missing required services\n" + missingRequired);
                }
+       }
 
+       private List<RequiredInterface> validateRequiredProvidedMatch(
+                       List<ProvidedInterface> provided, List<RequiredInterface> required) {
                List<RequiredInterface> reallyRequired = new ArrayList<RequiredInterface>(
                                required);
                // Compute all required interfaces that are not provided
+
                for (ProvidedInterface service : provided) {
                        List<RequiredInterface> fulfilled = Arrays
                                        .asList(filterRequiredServices(service, reallyRequired));
                        reallyRequired.removeAll(fulfilled);
                }
+               // Now remove all optional interfaces from the list.
+               for (Iterator<RequiredInterface> i = 
+                       reallyRequired.iterator(); i.hasNext(); ) { 
+                       RequiredInterface req = i.next();
+                       if ( req.isOptional() ) {
+                               i.remove(); 
+                       }
+               }
                // Now the remaining interfaces should be covered by the required
                // list.
-               reallyRequired.removeAll(Arrays.asList(aRequired));
+               reallyRequired.removeAll(Arrays.asList(getRequiredInterfaces()));
+               return reallyRequired;
+       }
 
-               String missingRequired = "";
-               for (RequiredInterface service : reallyRequired) {
-                       missingRequired += service + "\n";
+       private void validateRequiredInterfaces(List<RequiredInterface> required) {
+               for (RequiredInterface service : getRequiredInterfaces()) {
+                       // TODO required services by the subsystem could be 
+                       //      subclasses or implementations of the requirements
+                       //      of the contained systems. The code below assumes
+                       //      an exact match. 
+                       if (!(required.contains(service))) {
+                               info("Service '"
+                                               + service
+                                               + "' indicated as required is not actually required by any of the subsystems");
+                       }
+                       // Check for the case that the externally required service
+                       // is optional whereas the internally required service is 
+                       // mandatory. 
+                       if ( service.isOptional()) { 
+                               for (RequiredInterface intf: required) { 
+                                       if ( intf.equals(service) && !intf.isOptional()) { 
+                                               // TODO indicate which subsystem this is. 
+                                               warn("Required service '" + service + "' indicated as optional is mandatory by one of its subsystems (" + getClients(intf) + ", " + intf + "), this can lead to problems when the system is started and the service is not there.");
+                                       }
+                               }
+                       }
                }
-               if (missingRequired.length() > 0) {
-                       throw new SystemAssemblyException(getName()
-                                       + ": missing required services\n" + missingRequired);
+       }
+
+       private void validateProvidedInterfaces(List<ProvidedInterface> provided) {
+               for (ProvidedInterface service : getProvidedInterfaces()) {
+                       // TODO provided interfaces by subsystems could be 
+                       //      provide subclasses or implementations of the 
+                       //      provided interfaces of the container.
+                       //      The code below assumes an exact match. 
+                       if (!(provided.contains(service))) {
+                               throw new SystemAssemblyException(getName() + ": Service '"
+                                               + service
+                                               + "' is not provided by any of the subsystems");
+                       }
                }
        }
 
        @Override
        protected void doStart() {
-               List<ProvidedInterface> provided = new ArrayList<ProvidedInterface>();
+               LOG.info("Starting '" + getQualifiedName() + "'");
+               List<ProvidedInterface> allProvided = new ArrayList<ProvidedInterface>();
 
                // all interfaces from the required list of this container are
                // provided to the components inside it.
                RequiredInterface[] required = getRequiredInterfaces();
                for (RequiredInterface intf : required) {
                        ProvidedInterface provider = intf.getProvider();
-                       if (provider == null) {
-                               throw new SystemAssemblyException(getQualifiedName()
-                                               + ": required interface '" + intf + "' is not provided");
+                       if (provider != null ) { 
+                               allProvided.add(provider);
+                       } else { 
+                               if ( !intf.isOptional()) { 
+                                       throw new SystemAssemblyException(getQualifiedName()
+                                                       + ": required interface '" + intf + "' is not provided");               
+                               }
                        }
-                       provided.add(intf.getProvider());
-               }
-
-               startImpl();
-       }
-
-       /**
-        * Starts the subsystems.
-        * 
-        * @param aRequiredServices
-        *            Services that are available from other systems that have been
-        *            started before.
-        */
-       private void startImpl() {
-               LOG.info("Starting '" + getQualifiedName() + "'");
-               List<ProvidedInterface> allProvided = new ArrayList<ProvidedInterface>();
-
-               // Add the provides of all externally required interfaces to the list of
-               // available
-               // interfaces
-               for (RequiredInterface required : getRequiredInterfaces()) {
-                       allProvided.add(required.getProvider());
                }
 
                List<Component> started = new ArrayList<Component>();
@@ -179,21 +206,13 @@ public class Container extends AbstractComponent {
                                // Check if all required services are already provided by
                                // earlier
                                // systems.
-                               RequiredInterface[] required = system.getRequiredInterfaces();
 
-                               for (RequiredInterface descriptor : required) {
+                               for (RequiredInterface descriptor : system.getRequiredInterfaces()) {
                                        ProvidedInterface[] filtered = filterProvidedServices(
                                                        descriptor, allProvided);
-
-                                       if (filtered.length == 0) {
-                                               throw new SystemAssemblyException(
-                                                               "Service '"
-                                                                               + descriptor
-                                                                               + "' required by system '"
-                                                                               + system
-                                                                               + "' is not provided by systems that are started earlier");
-                                       }
-                                       if (filtered.length > 1) {
+                                       if ( filtered.length == 1 ) { 
+                                               descriptor.setProvider(filtered[0]);
+                                       } else if ( filtered.length > 1 ) { 
                                                throw new SystemAssemblyException(
                                                                "Service '"
                                                                                + descriptor
@@ -201,8 +220,17 @@ public class Container extends AbstractComponent {
                                                                                + system
                                                                                + "' matches multiple services provided by other systems: "
                                                                                + Arrays.asList(filtered));
+                                       } else { 
+                                               // filtered.length == 0
+                                               if ( !descriptor.isOptional()) { 
+                                                       throw new SystemAssemblyException(
+                                                                       "Service '"
+                                                                                       + descriptor
+                                                                                       + "' required by system '"
+                                                                                       + system
+                                                                                       + "' is not provided by systems that are started earlier");     
+                                               }
                                        }
-                                       descriptor.setProvider(filtered[0]);
                                }
 
                                // Start the service.
@@ -241,7 +269,22 @@ public class Container extends AbstractComponent {
        }
 
        private void info(String aMsg) {
-               LOG.info(getName() + ": " + aMsg);
+               LOG.info(getQualifiedName() + ": " + aMsg);
+       }
+       
+       private void warn(String aMsg) {
+               LOG.warn(getQualifiedName() + ": " + aMsg);
        }
 
+       private List<Component> getClients(RequiredInterface aRequirement) {
+               List<Component> clients = new ArrayList<Component>();
+               for (Component component: _systems) { 
+                       for (RequiredInterface required: component.getRequiredInterfaces()) { 
+                               if ( required.equals(aRequirement) && required.isOptional() == aRequirement.isOptional()) { 
+                                       clients.add(component);
+                               }
+                       }
+               }
+               return clients; 
+       }
 }