Added initial support for externally querying the status of services managed by the dependency manager. Added initial support for supplying your own default implementation. Made null object creation more robust. Added support for specifying the name of the property in case you're injecting dependencies so you can inject multiple instances of a service to specific properties.

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@696805 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/dependencymanager/src/main/java/org/apache/felix/dependencymanager/ConfigurationDependency.java b/dependencymanager/src/main/java/org/apache/felix/dependencymanager/ConfigurationDependency.java
index 4f27cb3..0c24e25 100644
--- a/dependencymanager/src/main/java/org/apache/felix/dependencymanager/ConfigurationDependency.java
+++ b/dependencymanager/src/main/java/org/apache/felix/dependencymanager/ConfigurationDependency.java
@@ -47,7 +47,7 @@
  * 
  * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
  */
-public class ConfigurationDependency implements Dependency, ManagedService {
+public class ConfigurationDependency implements Dependency, ManagedService, ServiceComponentDependency {
 	private BundleContext m_context;
 	private String m_pid;
 	private ServiceRegistration m_registration;
@@ -171,4 +171,16 @@
     public String toString() {
     	return "ConfigurationDependency[" + m_pid + "]";
     }
+
+    public String getName() {
+        return m_pid;
+    }
+
+    public int getState() {
+        return (isAvailable() ? 1 : 0) + (isRequired() ? 2 : 0);
+    }
+
+    public String getType() {
+        return "configuration";
+    }
 }
diff --git a/dependencymanager/src/main/java/org/apache/felix/dependencymanager/DependencyActivatorBase.java b/dependencymanager/src/main/java/org/apache/felix/dependencymanager/DependencyActivatorBase.java
index 1d92150..2fa8b13 100644
--- a/dependencymanager/src/main/java/org/apache/felix/dependencymanager/DependencyActivatorBase.java
+++ b/dependencymanager/src/main/java/org/apache/felix/dependencymanager/DependencyActivatorBase.java
@@ -95,7 +95,7 @@
      * @return the new service
      */
     public Service createService() {
-        return new ServiceImpl(m_context, m_logger);
+        return new ServiceImpl(m_context, m_manager, m_logger);
     }
     
     /**
diff --git a/dependencymanager/src/main/java/org/apache/felix/dependencymanager/DependencyManager.java b/dependencymanager/src/main/java/org/apache/felix/dependencymanager/DependencyManager.java
index 30f3cde..5c99c7b 100644
--- a/dependencymanager/src/main/java/org/apache/felix/dependencymanager/DependencyManager.java
+++ b/dependencymanager/src/main/java/org/apache/felix/dependencymanager/DependencyManager.java
@@ -73,7 +73,7 @@
      * @return the new service
      */
     public Service createService() {
-        return new ServiceImpl(m_context, m_logger);
+        return new ServiceImpl(m_context, this, m_logger);
     }
     
     /**
diff --git a/dependencymanager/src/main/java/org/apache/felix/dependencymanager/ServiceComponent.java b/dependencymanager/src/main/java/org/apache/felix/dependencymanager/ServiceComponent.java
new file mode 100644
index 0000000..fc14de7
--- /dev/null
+++ b/dependencymanager/src/main/java/org/apache/felix/dependencymanager/ServiceComponent.java
@@ -0,0 +1,10 @@
+package org.apache.felix.dependencymanager;
+
+public interface ServiceComponent {
+    public static final String[] STATE_NAMES = { "unregistered", "registered" };
+    public static final int STATE_UNREGISTERED = 0;
+    public static final int STATE_REGISTERED = 1;
+    public ServiceComponentDependency[] getComponentDependencies();
+    public String getName();
+    public int getState();
+}
diff --git a/dependencymanager/src/main/java/org/apache/felix/dependencymanager/ServiceComponentDependency.java b/dependencymanager/src/main/java/org/apache/felix/dependencymanager/ServiceComponentDependency.java
new file mode 100644
index 0000000..ee29aac
--- /dev/null
+++ b/dependencymanager/src/main/java/org/apache/felix/dependencymanager/ServiceComponentDependency.java
@@ -0,0 +1,12 @@
+package org.apache.felix.dependencymanager;
+
+public interface ServiceComponentDependency {
+    public static final String[] STATE_NAMES = { "unavailable optional", "available optional", "unavailable required", "available required" };
+    public static final int STATE_UNAVAILABLE_OPTIONAL = 0;
+    public static final int STATE_AVAILABLE_OPTIONAL = 1;
+    public static final int STATE_UNAVAILABLE_REQUIRED = 2;
+    public static final int STATE_AVAILABLE_REQUIRED = 3;
+    public String getName();
+    public String getType();
+    public int getState();
+}
diff --git a/dependencymanager/src/main/java/org/apache/felix/dependencymanager/ServiceDependency.java b/dependencymanager/src/main/java/org/apache/felix/dependencymanager/ServiceDependency.java
index f76ac67..9a53bdf 100644
--- a/dependencymanager/src/main/java/org/apache/felix/dependencymanager/ServiceDependency.java
+++ b/dependencymanager/src/main/java/org/apache/felix/dependencymanager/ServiceDependency.java
@@ -32,7 +32,7 @@
  * 
  * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
  */
-public class ServiceDependency implements Dependency, ServiceTrackerCustomizer {
+public class ServiceDependency implements Dependency, ServiceTrackerCustomizer, ServiceComponentDependency {
     private boolean m_isRequired;
     private Service m_service;
     private ServiceTracker m_tracker;
@@ -51,6 +51,9 @@
     private ServiceReference m_reference;
     private Object m_serviceInstance;
     private final Logger m_logger;
+    private String m_autoConfigInstance;
+    private Object m_defaultImplementation;
+    private Object m_defaultImplementationInstance;
     
     /**
      * Creates a new service dependency.
@@ -82,7 +85,10 @@
             service = m_tracker.getService();
         }
         if (service == null) {
-            service = getNullObject(); 
+            service = getDefaultImplementation();
+            if (service == null) {
+                service = getNullObject();
+            }
         }
         return service;
     }
@@ -93,11 +99,33 @@
             synchronized (this) {
                 trackedServiceName = m_trackedServiceName;
             }
-            m_nullObject = Proxy.newProxyInstance(trackedServiceName.getClassLoader(), new Class[] {trackedServiceName}, new DefaultNullObject()); 
+            try {
+                m_nullObject = Proxy.newProxyInstance(trackedServiceName.getClassLoader(), new Class[] {trackedServiceName}, new DefaultNullObject()); 
+            }
+            catch (Exception e) {
+                m_logger.log(Logger.LOG_ERROR, "Could not create null object for " + trackedServiceName + ".", e);
+            }
         }
         return m_nullObject;
     }
     
+    private Object getDefaultImplementation() {
+        if (m_defaultImplementation != null) {
+            if (m_defaultImplementation instanceof Class) {
+                try {
+                    m_defaultImplementationInstance = ((Class) m_defaultImplementation).newInstance();
+                }
+                catch (Exception e) {
+                    m_logger.log(Logger.LOG_ERROR, "Could not create default implementation instance of class " + m_defaultImplementation + ".", e);
+                }
+            }
+            else {
+                m_defaultImplementationInstance = m_defaultImplementation;
+            }
+        }
+        return m_defaultImplementationInstance;
+    }
+    
     public synchronized Class getInterface() {
         return m_trackedServiceName;
     }
@@ -373,6 +401,22 @@
         m_trackedServiceFilter = null;
         return this;
     }
+    
+    /**
+     * Sets the default implementation for this service dependency. You can use this to supply
+     * your own implementation that will be used instead of a Null Object when the dependency is
+     * not available. This is also convenient if the service dependency is not an interface
+     * (which would cause the Null Object creation to fail) but a class.
+     * 
+     * @param implementation the instance to use or the class to instantiate if you want to lazily
+     *     instantiate this implementation
+     * @return this service dependency
+     */
+    public synchronized ServiceDependency setDefaultImplementation(Object implementation) {
+        ensureNotActive();
+        m_defaultImplementation = implementation;
+        return this;
+    }
 
     /**
      * Sets the required flag which determines if this service is required or not.
@@ -401,6 +445,21 @@
     }
     
     /**
+     * Sets auto configuration for this service. Auto configuration allows the
+     * dependency to fill in the attribute in the service implementation that
+     * has the same type and instance name.
+     * 
+     * @param instanceName the name of attribute to auto config
+     * @return this service dependency
+     */
+    public synchronized ServiceDependency setAutoConfig(String instanceName) {
+        ensureNotActive();
+        m_autoConfig = (instanceName != null);
+        m_autoConfigInstance = instanceName;
+        return this;
+    }
+    
+    /**
      * Sets the callbacks for this service. These callbacks can be used as hooks whenever
      * a dependency is added or removed. They are called on the service implementation.
      * 
@@ -447,4 +506,20 @@
     public synchronized String toString() {
         return "ServiceDependency[" + m_trackedServiceName + " " + m_trackedServiceFilter + "]";
     }
+
+    public String getAutoConfigName() {
+        return m_autoConfigInstance;
+    }
+
+    public String getName() {
+        return m_trackedServiceName.getName();
+    }
+
+    public int getState() {
+        return (isAvailable() ? 1 : 0) + (isRequired() ? 2 : 0);
+    }
+
+    public String getType() {
+        return "service";
+    }
 }
diff --git a/dependencymanager/src/main/java/org/apache/felix/dependencymanager/ServiceImpl.java b/dependencymanager/src/main/java/org/apache/felix/dependencymanager/ServiceImpl.java
index f29a41e..3f58ec2 100644
--- a/dependencymanager/src/main/java/org/apache/felix/dependencymanager/ServiceImpl.java
+++ b/dependencymanager/src/main/java/org/apache/felix/dependencymanager/ServiceImpl.java
@@ -39,12 +39,13 @@
  *
  * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
  */
-public class ServiceImpl implements Service {
+public class ServiceImpl implements Service, ServiceComponent {
     private static final Class[] VOID = new Class[] {};
 	private static final ServiceRegistration NULL_REGISTRATION;
     private static final ServiceStateListener[] SERVICE_STATE_LISTENER_TYPE = new ServiceStateListener[] {};
 
     private final BundleContext m_context;
+    private final DependencyManager m_manager;
 
     // configuration (static)
     private String m_callbackInit;
@@ -84,11 +85,13 @@
 	
 	// internal logging
     private final Logger m_logger;
+    private ServiceRegistration m_serviceRegistration;
 
-    public ServiceImpl(BundleContext context, Logger logger) {
+    public ServiceImpl(BundleContext context, DependencyManager manager, Logger logger) {
     	m_logger = logger;
         m_state = new State((List) m_dependencies.clone(), false);
         m_context = context;
+        m_manager = manager;
         m_callbackInit = "init";
         m_callbackStart = "start";
         m_callbackStop = "stop";
@@ -237,6 +240,7 @@
     }
 
     public synchronized void start() {
+        m_serviceRegistration = m_context.registerService(ServiceComponent.class.getName(), this, null);
     	State oldState, newState;
         synchronized (m_dependencies) {
         	oldState = m_state;
@@ -254,6 +258,7 @@
             m_state = newState;
         }
         calculateStateChanges(oldState, newState);
+        m_serviceRegistration.unregister();
     }
 
     public synchronized Service setInterface(String serviceName, Dictionary properties) {
@@ -588,6 +593,7 @@
 	        // configure the bundle context
 	        configureImplementation(BundleContext.class, m_context);
 	        configureImplementation(ServiceRegistration.class, NULL_REGISTRATION);
+	        configureImplementation(DependencyManager.class, m_manager);
     	}
     }
 
@@ -676,7 +682,7 @@
             // update the dependency in the service instance (it will use
             // a null object if necessary)
             if (sd.isAutoConfig()) {
-                configureImplementation(sd.getInterface(), sd.getService());
+                configureImplementation(sd.getInterface(), sd.getService(), sd.getAutoConfigName());
             }
         }
         else if (dependency instanceof ConfigurationDependency) {
@@ -696,8 +702,9 @@
      *
      * @param clazz the class to search for
      * @param instance the instance to fill in
+     * @param instanceName the name of the instance to fill in, or <code>null</code> if not used
      */
-    private void configureImplementation(Class clazz, Object instance) {
+    private void configureImplementation(Class clazz, Object instance, String instanceName) {
     	Object[] instances = null;
     	if (m_compositionManagerGetMethod != null) {
 			if (m_compositionManager != null) {
@@ -728,7 +735,7 @@
 		        while (serviceClazz != null) {
 		            Field[] fields = serviceClazz.getDeclaredFields();
 		            for (int j = 0; j < fields.length; j++) {
-		                if (fields[j].getType().equals(clazz)) {
+		                if (fields[j].getType().equals(clazz) && (instanceName == null || fields[j].getName().equals(instanceName))) {
 		                    try {
 		                    	fields[j].setAccessible(true);
 		                        // synchronized makes sure the field is actually written to immediately
@@ -747,6 +754,10 @@
 	    	}
     	}
     }
+    
+    private void configureImplementation(Class clazz, Object instance) {
+        configureImplementation(clazz, instance, null);
+    }
 
     private void configureServices(State state) {
         Iterator i = state.getDependencies().iterator();
@@ -755,7 +766,7 @@
             if (dependency instanceof ServiceDependency) {
                 ServiceDependency sd = (ServiceDependency) dependency;
                 if (sd.isAutoConfig()) {
-                    configureImplementation(sd.getInterface(), sd.getService());
+                    configureImplementation(sd.getInterface(), sd.getService(), sd.getAutoConfigName());
                 }
                 // for required dependencies, we invoke any callbacks here
                 if (sd.isRequired()) {
@@ -796,6 +807,58 @@
         return (state.isTrackingOptional());
     }
 
+    // ServiceComponent interface
+    
+    static class SCDImpl implements ServiceComponentDependency {
+        private final String m_name;
+        private final int m_state;
+        private final String m_type;
+
+        public SCDImpl(String name, int state, String type) {
+            m_name = name;
+            m_state = state;
+            m_type = type;
+        }
+
+        public String getName() {
+            return m_name;
+        }
+
+        public int getState() {
+            return m_state;
+        }
+
+        public String getType() {
+            return m_type;
+        }
+    }
+    
+    public ServiceComponentDependency[] getComponentDependencies() {
+        List deps = getDependencies();
+        if (deps != null) {
+            ServiceComponentDependency[] result = new ServiceComponentDependency[deps.size()];
+            for (int i = 0; i < result.length; i++) {
+                Dependency dep = (Dependency) deps.get(i);
+                if (dep instanceof ServiceComponentDependency) {
+                    result[i] = (ServiceComponentDependency) dep;
+                }
+                else {
+                    result[i] = new SCDImpl(dep.toString(), (dep.isAvailable() ? 1 : 0) + (dep.isRequired() ? 2 : 0), dep.getClass().getName());
+                }
+            }
+            return result;
+        }
+        return null;
+    }
+
+    public String getName() {
+        return (String) (m_serviceName != null ? m_serviceName : m_serviceInstance);
+    }
+
+    public int getState() {
+        return (isRegistered() ? 1 : 0);
+    }
+    
     static {
         NULL_REGISTRATION = (ServiceRegistration) Proxy.newProxyInstance(ServiceImpl.class.getClassLoader(), new Class[] {ServiceRegistration.class}, new DefaultNullObject());
     }