Added a Logger similar to the one the core framework uses, that either logs to standard out, or to an available LogService. Went through the codebase and used this logger to provide better diagnostics messages.

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@661243 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 83b3364..4f27cb3 100644
--- a/dependencymanager/src/main/java/org/apache/felix/dependencymanager/ConfigurationDependency.java
+++ b/dependencymanager/src/main/java/org/apache/felix/dependencymanager/ConfigurationDependency.java
@@ -54,19 +54,30 @@
 	private volatile Service m_service;
 	private Dictionary m_settings;
 	private boolean m_propagate;
+    private final Logger m_logger;
 	
-	public ConfigurationDependency(BundleContext context) {
+	public ConfigurationDependency(BundleContext context, Logger logger) {
 		m_context = context;
+        m_logger = logger;
 	}
 	
 	public synchronized boolean isAvailable() {
 		return m_settings != null;
 	}
 
+	/**
+	 * Will always return <code>true</code> as optional configuration dependencies
+	 * do not make sense. You might as well just implement <code>ManagedService</code>
+	 * yourself in those cases.
+	 */
 	public boolean isRequired() {
 		return true;
 	}
 	
+	/**
+	 * Returns <code>true</code> when configuration properties should be propagated
+	 * as service properties.
+	 */
 	public boolean isPropagated() {
 		return m_propagate;
 	}
@@ -104,11 +115,13 @@
 				// the "old" configuration to stay in effect
 			}
 			else {
-				throw new IllegalStateException("Could not invoke updated on implementation");
+				m_logger.log(Logger.LOG_ERROR, "Service " + m_service + " with configuration dependency " + this + " does not implement ManagedService.");
+				return;
 			}
 		}
 		else {
-			throw new IllegalStateException("Could not instantiate implementation");
+		    m_logger.log(Logger.LOG_ERROR, "Service " + m_service + " with configuration dependency " + this + " could not be instantiated.");
+		    return;
 		}
 		// if these settings did not cause a configuration exception, we determine
 		// if they have caused the dependency state to change
@@ -128,12 +141,21 @@
 		}
 	}
 
+	/**
+	 * Sets the <code>service.pid</code> of the configuration you
+	 * are depending on.
+	 */
 	public ConfigurationDependency setPid(String pid) {
 		ensureNotActive();
 		m_pid = pid;
 		return this;
 	}
-	
+
+	/**
+	 * Sets propagation of the configuration properties to the service
+	 * properties. Any additional service properties specified directly
+	 * are merged with these.
+	 */
 	public ConfigurationDependency setPropagate(boolean propagate) {
 		ensureNotActive();
 		m_propagate = propagate;
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 f11ea04..1d92150 100644
--- a/dependencymanager/src/main/java/org/apache/felix/dependencymanager/DependencyActivatorBase.java
+++ b/dependencymanager/src/main/java/org/apache/felix/dependencymanager/DependencyActivatorBase.java
@@ -35,6 +35,7 @@
 public abstract class DependencyActivatorBase implements BundleActivator {
     private BundleContext m_context;
     private DependencyManager m_manager;
+    private Logger m_logger;
     
     /**
      * Initialize the dependency manager. Here you can add all services and their dependencies.
@@ -70,7 +71,8 @@
      */
     public void start(BundleContext context) throws Exception {
         m_context = context;
-        m_manager = new DependencyManager(context);
+        m_logger = new Logger(context);
+        m_manager = new DependencyManager(context, m_logger);
         init(m_context, m_manager);
     }
 
@@ -93,7 +95,7 @@
      * @return the new service
      */
     public Service createService() {
-        return new ServiceImpl(m_context);
+        return new ServiceImpl(m_context, m_logger);
     }
     
     /**
@@ -102,7 +104,7 @@
      * @return the service dependency
      */
     public ServiceDependency createServiceDependency() {
-        return new ServiceDependency(m_context);
+        return new ServiceDependency(m_context, m_logger);
     }
     
     /**
@@ -111,7 +113,7 @@
      * @return the configuration dependency
      */
     public ConfigurationDependency createConfigurationDependency() {
-    	return new ConfigurationDependency(m_context);
+    	return new ConfigurationDependency(m_context, 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 40e9e53..30f3cde 100644
--- a/dependencymanager/src/main/java/org/apache/felix/dependencymanager/DependencyManager.java
+++ b/dependencymanager/src/main/java/org/apache/felix/dependencymanager/DependencyManager.java
@@ -30,16 +30,19 @@
  * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
  */
 public class DependencyManager {
-    private BundleContext m_context;
+    private final BundleContext m_context;
+    private final Logger m_logger;
     private List m_services = Collections.synchronizedList(new ArrayList());
 
     /**
      * Creates a new dependency manager.
      * 
      * @param context the bundle context
+     * @param logger 
      */
-    public DependencyManager(BundleContext context) {
+    public DependencyManager(BundleContext context, Logger logger) {
         m_context = context;
+        m_logger = logger;
     }
     
     /**
@@ -70,7 +73,7 @@
      * @return the new service
      */
     public Service createService() {
-        return new ServiceImpl(m_context);
+        return new ServiceImpl(m_context, m_logger);
     }
     
     /**
@@ -79,7 +82,11 @@
      * @return the service dependency
      */
     public ServiceDependency createServiceDependency() {
-        return new ServiceDependency(m_context);
+        return new ServiceDependency(m_context, m_logger);
+    }
+    
+    public ConfigurationDependency createConfigurationDependency() {
+        return new ConfigurationDependency(m_context, m_logger);
     }
     
     /**
diff --git a/dependencymanager/src/main/java/org/apache/felix/dependencymanager/Logger.java b/dependencymanager/src/main/java/org/apache/felix/dependencymanager/Logger.java
new file mode 100644
index 0000000..c5a9af1
--- /dev/null
+++ b/dependencymanager/src/main/java/org/apache/felix/dependencymanager/Logger.java
@@ -0,0 +1,279 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.apache.felix.dependencymanager;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * <p>
+ * This class mimics the standard OSGi <tt>LogService</tt> interface. An
+ * instance of this class is used by the framework for all logging. By default
+ * this class logs messages to standard out. The log level can be set to
+ * control the amount of logging performed, where a higher number results in
+ * more logging. A log level of zero turns off logging completely.
+ * </p>
+ * <p>
+ * The log levels match those specified in the OSGi Log Service (i.e., 1 = error,
+ * 2 = warning, 3 = information, and 4 = debug). The default value is 1.
+ * </p>
+ * <p>
+ * This class also uses the System Bundle's context to track log services
+ * and will use the highest ranking log service, if present, as a back end
+ * instead of printing to standard out. The class uses reflection to invoking
+ * the log service's method to avoid a dependency on the log interface.
+ * </p>
+**/
+public class Logger implements ServiceListener
+{
+    public static final int LOG_ERROR = 1;
+    public static final int LOG_WARNING = 2;
+    public static final int LOG_INFO = 3;
+    public static final int LOG_DEBUG = 4;
+
+    private final BundleContext m_context;
+
+    private final static int LOGGER_OBJECT_IDX = 0;
+    private final static int LOGGER_METHOD_IDX = 1;
+    private ServiceReference m_logRef = null;
+    private Object[] m_logger = null;
+
+    public Logger(BundleContext context)
+    {
+         m_context = context;
+         startListeningForLogService();
+    }
+
+    public final void log(int level, String msg)
+    {
+        _log(null, level, msg, null);
+    }
+
+    public final void log(int level, String msg, Throwable throwable)
+    {
+        _log(null, level, msg, throwable);
+    }
+
+    public final void log(ServiceReference sr, int level, String msg)
+    {
+        _log(sr, level, msg, null);
+    }
+
+    public final void log(ServiceReference sr, int level, String msg, Throwable throwable)
+    {
+        _log(sr, level, msg, throwable);
+    }
+
+    protected void doLog(ServiceReference sr, int level, String msg, Throwable throwable)
+    {
+        String s = (sr == null) ? null : "SvcRef " + sr;
+        s = (s == null) ? msg : s + " " + msg;
+        s = (throwable == null) ? s : s + " (" + throwable + ")";
+        switch (level)
+        {
+            case LOG_DEBUG:
+                System.out.println("DEBUG: " + s);
+                break;
+            case LOG_ERROR:
+                System.out.println("ERROR: " + s);
+                if (throwable != null)
+                {
+                    if ((throwable instanceof BundleException) &&
+                        (((BundleException) throwable).getNestedException() != null))
+                    {
+                        throwable = ((BundleException) throwable).getNestedException();
+                    }
+                    throwable.printStackTrace();
+                }
+                break;
+            case LOG_INFO:
+                System.out.println("INFO: " + s);
+                break;
+            case LOG_WARNING:
+                System.out.println("WARNING: " + s);
+                break;
+            default:
+                System.out.println("UNKNOWN[" + level + "]: " + s);
+        }
+    }
+
+    private void _log(ServiceReference sr, int level, String msg, Throwable throwable)
+    {
+        // Save our own copy just in case it changes. We could try to do
+        // more conservative locking here, but let's be optimistic.
+        Object[] logger = m_logger;
+
+        // Use the log service if available.
+        if (logger != null)
+        {
+            _logReflectively(logger, sr, level, msg, throwable);
+        }
+        // Otherwise, default logging action.
+        else
+        {
+            doLog(sr, level, msg, throwable);
+        }
+    }
+
+    private void _logReflectively(
+        Object[] logger, ServiceReference sr, int level, String msg, Throwable throwable)
+    {
+        if (logger != null)
+        {
+            Object[] params = {
+                sr, new Integer(level), msg, throwable
+            };
+            try
+            {
+                ((Method) logger[LOGGER_METHOD_IDX]).invoke(logger[LOGGER_OBJECT_IDX], params);
+            }
+            catch (InvocationTargetException ex)
+            {
+                System.err.println("Logger: " + ex);
+            }
+            catch (IllegalAccessException ex)
+            {
+                System.err.println("Logger: " + ex);
+            }
+        }
+    }
+
+    /**
+     * This method is called when the bundle context is set;
+     * it simply adds a service listener so that the bundle can track
+     * log services to be used as the back end of the logging mechanism. It also
+     * attempts to get an existing log service, if present, but in general
+     * there will never be a log service present since the system bundle is
+     * started before every other bundle.
+    **/
+    private void startListeningForLogService()
+    {
+        // Add a service listener for log services.
+        try
+        {
+            m_context.addServiceListener(
+                this, "(objectClass=org.osgi.service.log.LogService)");
+        }
+        catch (InvalidSyntaxException ex) {
+            // This will never happen since the filter is hard coded.
+        }
+        // Try to get an existing log service.
+        m_logRef = m_context.getServiceReference("org.osgi.service.log.LogService");
+        // Get the service object if available and set it in the logger.
+        if (m_logRef != null)
+        {
+            setLogger(m_context.getService(m_logRef));
+        }
+    }
+
+    /**
+     * This method implements the callback for the ServiceListener interface.
+     * It is public as a byproduct of implementing the interface and should
+     * not be called directly. This method tracks run-time changes to log
+     * service availability. If the log service being used by the framework's
+     * logging mechanism goes away, then this will try to find an alternative.
+     * If a higher ranking log service is registered, then this will switch
+     * to the higher ranking log service.
+    **/
+    public final synchronized void serviceChanged(ServiceEvent event)
+    {
+        // If no logger is in use, then grab this one.
+        if ((event.getType() == ServiceEvent.REGISTERED) && (m_logRef == null))
+        {
+            m_logRef = event.getServiceReference();
+            // Get the service object and set it in the logger.
+            setLogger(m_context.getService(m_logRef));
+        }
+        // If a logger is in use, but this one has a higher ranking, then swap
+        // it for the existing logger.
+        else if ((event.getType() == ServiceEvent.REGISTERED) && (m_logRef != null))
+        {
+            ServiceReference ref =
+                m_context.getServiceReference("org.osgi.service.log.LogService");
+            if (!ref.equals(m_logRef))
+            {
+                m_context.ungetService(m_logRef);
+                m_logRef = ref;
+                setLogger(m_context.getService(m_logRef));
+            }
+
+        }
+        // If the current logger is going away, release it and try to
+        // find another one.
+        else if ((event.getType() == ServiceEvent.UNREGISTERING) &&
+            m_logRef.equals(event.getServiceReference()))
+        {
+            // Unget the service object.
+            m_context.ungetService(m_logRef);
+            // Try to get an existing log service.
+            m_logRef = m_context.getServiceReference(
+                "org.osgi.service.log.LogService");
+            // Get the service object if available and set it in the logger.
+            if (m_logRef != null)
+            {
+                setLogger(m_context.getService(m_logRef));
+            }
+            else
+            {
+                setLogger(null);
+            }
+        }
+    }
+
+    /**
+     * This method sets the new log service object. It also caches the method to
+     * invoke. The service object and method are stored in array to optimistically
+     * eliminate the need to locking when logging.
+    **/
+    private void setLogger(Object logObj)
+    {
+        if (logObj == null)
+        {
+            m_logger = null;
+        }
+        else
+        {
+            Class[] formalParams = {
+                ServiceReference.class,
+                Integer.TYPE,
+                String.class,
+                Throwable.class
+            };
+
+            try
+            {
+                Method logMethod = logObj.getClass().getMethod("log", formalParams);
+                logMethod.setAccessible(true);
+                m_logger = new Object[] { logObj, logMethod };
+            }
+            catch (NoSuchMethodException ex)
+            {
+                System.err.println("Logger: " + ex);
+                m_logger = null;
+            }
+        }
+    }
+}
\ No newline at end of file
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 81d9825..f76ac67 100644
--- a/dependencymanager/src/main/java/org/apache/felix/dependencymanager/ServiceDependency.java
+++ b/dependencymanager/src/main/java/org/apache/felix/dependencymanager/ServiceDependency.java
@@ -50,14 +50,17 @@
     private boolean m_autoConfig;
     private ServiceReference m_reference;
     private Object m_serviceInstance;
+    private final Logger m_logger;
     
     /**
      * Creates a new service dependency.
      * 
      * @param context the bundle context
+     * @param logger 
      */
-    public ServiceDependency(BundleContext context) {
+    public ServiceDependency(BundleContext context, Logger logger) {
         m_context = context;
+        m_logger = logger;
         m_autoConfig = true;
     }
 
@@ -171,8 +174,9 @@
         if ((callbackInstance != null) && (m_callbackAdded != null)) {
             try {
                 invokeCallbackMethod(callbackInstance, m_callbackAdded, reference, serviceInstance);
-            } catch (NoSuchMethodException e) {
-                // silently ignore this
+            }
+            catch (NoSuchMethodException e) {
+                m_logger.log(Logger.LOG_ERROR, "Could not invoke method " + m_callbackAdded + " on " + callbackInstance + ".", e);
             }
         }
     }
@@ -197,7 +201,7 @@
                 invokeCallbackMethod(callbackInstance, m_callbackChanged, reference, serviceInstance);
             }
             catch (NoSuchMethodException e) {
-                // ignore when the service has no such method
+                m_logger.log(Logger.LOG_ERROR, "Could not invoke method " + m_callbackChanged + " on " + callbackInstance + ".", e);
             }
         }
     }
@@ -230,7 +234,7 @@
                 invokeCallbackMethod(callbackInstance, m_callbackRemoved, reference, serviceInstance);
             }
             catch (NoSuchMethodException e) {
-                // ignore when the service has no such method
+                m_logger.log(Logger.LOG_ERROR, "Could not invoke method " + m_callbackRemoved + " on " + callbackInstance + ".", e);
             }
         }
     }
@@ -275,6 +279,9 @@
                 currentClazz = currentClazz.getSuperclass();
             }
         }
+        if (!done && currentClazz == null) {
+            throw new NoSuchMethodException(methodName);
+        }
     }
 
     private boolean invokeMethod(Object object, Class clazz, String name, Class[][] signatures, Object[][] parameters, boolean isSuper) {
@@ -289,14 +296,18 @@
                         m.invoke(object, parameters[i]);
                     }
                     catch (InvocationTargetException e) {
-                        // thrown by the underlying method, we ignore this exception
-                        // and still return true because we could invoke the method
+                        m_logger.log(Logger.LOG_ERROR, "Exception while invoking method " + m + ".", e);
                     }
+                    // we did find and invoke the method, so we return true
                     return true;
                 }
             }
+            catch (NoSuchMethodException e) {
+                // ignore this and keep looking
+            }
             catch (Exception e) {
-                // ignore any exception and keep looking for a method
+                // could not even try to invoke the method
+                m_logger.log(Logger.LOG_ERROR, "Exception while trying to invoke method " + m + ".", e);
             }
         }
         return false;
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 a63f4b8..f29a41e 100644
--- a/dependencymanager/src/main/java/org/apache/felix/dependencymanager/ServiceImpl.java
+++ b/dependencymanager/src/main/java/org/apache/felix/dependencymanager/ServiceImpl.java
@@ -20,6 +20,7 @@
 
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.Proxy;
 import java.util.ArrayList;
@@ -80,9 +81,13 @@
 	private Object m_compositionManager;
 	private String m_compositionManagerGetMethod;
 	private Object m_compositionManagerInstance;
+	
+	// internal logging
+    private final Logger m_logger;
 
-    public ServiceImpl(BundleContext context) {
-    	m_state = new State((List) m_dependencies.clone(), false);
+    public ServiceImpl(BundleContext context, Logger logger) {
+    	m_logger = logger;
+        m_state = new State((List) m_dependencies.clone(), false);
         m_context = context;
         m_callbackInit = "init";
         m_callbackStart = "start";
@@ -352,30 +357,50 @@
 	private void stateListenersStarting() {
 		ServiceStateListener[] list = getListeners();
 		for (int i = 0; i < list.length; i++) {
-			list[i].starting(this);
+		    try {
+		        list[i].starting(this);
+		    }
+		    catch (Throwable t) {
+		        m_logger.log(Logger.LOG_ERROR, "Error invoking listener starting method.", t);
+		    }
 		}
 	}
 
 	private void stateListenersStarted() {
-		ServiceStateListener[] list = getListeners();
-		for (int i = 0; i < list.length; i++) {
-			list[i].started(this);
-		}
-	}
+        ServiceStateListener[] list = getListeners();
+        for (int i = 0; i < list.length; i++) {
+            try {
+                list[i].started(this);
+            }
+            catch (Throwable t) {
+                m_logger.log(Logger.LOG_ERROR, "Error invoking listener started method.", t);
+            }
+        }
+    }
 
-	private void stateListenersStopping() {
-		ServiceStateListener[] list = getListeners();
-		for (int i = 0; i < list.length; i++) {
-			list[i].stopping(this);
-		}
-	}
+    private void stateListenersStopping() {
+        ServiceStateListener[] list = getListeners();
+        for (int i = 0; i < list.length; i++) {
+            try {
+                list[i].stopping(this);
+            }
+            catch (Throwable t) {
+                m_logger.log(Logger.LOG_ERROR, "Error invoking listener stopping method.", t);
+            }
+        }
+    }
 
-	private void stateListenersStopped() {
-		ServiceStateListener[] list = getListeners();
-		for (int i = 0; i < list.length; i++) {
-			list[i].stopped(this);
-		}
-	}
+    private void stateListenersStopped() {
+        ServiceStateListener[] list = getListeners();
+        for (int i = 0; i < list.length; i++) {
+            try {
+                list[i].stopped(this);
+            }
+            catch (Throwable t) {
+                m_logger.log(Logger.LOG_ERROR, "Error invoking listener stopped method.", t);
+            }
+        }
+    }
 
 	private ServiceStateListener[] getListeners() {
 		synchronized (m_stateListeners) {
@@ -443,7 +468,12 @@
                 	Method method = clazz.getDeclaredMethod(name, null);
 	                	if (method != null) {
 	                		method.setAccessible(true);
-	                		method.invoke(m_serviceInstance, null);
+	                		try {
+    	                		method.invoke(m_serviceInstance, null);
+	                		}
+	                		catch (InvocationTargetException e) {
+	                		    m_logger.log(Logger.LOG_ERROR, "Exception while invoking method " + method + ".", e);
+	                		}
 	                		return;
 	                	}
                 	}
@@ -454,7 +484,7 @@
                 }
             }
             catch (Exception e) {
-                throw new RuntimeException(e);
+                m_logger.log(Logger.LOG_ERROR, "Error trying to invoke method named " + name + ".", e);
             }
         }
     }
@@ -512,19 +542,8 @@
 	            try {
 	            	m_serviceInstance = createInstance((Class) m_implementation);
 	            }
-	            catch (InstantiationException e) {
-	                // TODO handle this exception
-	                e.printStackTrace();
-	            }
-	            catch (IllegalAccessException e) {
-	                // TODO handle this exception
-	                e.printStackTrace();
-	            } catch (SecurityException e) {
-					// TODO Auto-generated catch block
-					e.printStackTrace();
-				} catch (NoSuchMethodException e) {
-					// TODO Auto-generated catch block
-					e.printStackTrace();
+	            catch (Exception e) {
+	                m_logger.log(Logger.LOG_ERROR, "Could not create service instance of class " + m_implementation + ".", e);
 				}
 	        }
 	        else {
@@ -535,20 +554,9 @@
 		        			try {
 								factory = createInstance((Class) m_instanceFactory);
 							}
-		        			catch (InstantiationException e) {
-								// TODO Auto-generated catch block
-								e.printStackTrace();
-							}
-		        			catch (IllegalAccessException e) {
-								// TODO Auto-generated catch block
-								e.printStackTrace();
-							} catch (SecurityException e) {
-								// TODO Auto-generated catch block
-								e.printStackTrace();
-							} catch (NoSuchMethodException e) {
-								// TODO Auto-generated catch block
-								e.printStackTrace();
-							}
+		                    catch (Exception e) {
+		                        m_logger.log(Logger.LOG_ERROR, "Could not create factory instance of class " + m_instanceFactory + ".", e);
+		                    }
 		        		}
 		        		else {
 		        			factory = m_instanceFactory;
@@ -560,21 +568,22 @@
 		        		// could be ???
 		        	}
 		        	if (factory == null) {
-		        		throw new IllegalStateException("Factory cannot be null");
+                        m_logger.log(Logger.LOG_ERROR, "Factory cannot be null.");
 		        	}
 		        	try {
 						Method m = factory.getClass().getDeclaredMethod(m_instanceFactoryCreateMethod, null);
 						m_serviceInstance = m.invoke(factory, null);
 					}
 		        	catch (Exception e) {
-						// TODO Auto-generated catch block
-						e.printStackTrace();
+	                    m_logger.log(Logger.LOG_ERROR, "Could not create service instance using factory " + factory + " method " + m_instanceFactoryCreateMethod + ".", e);
 					}
 	        	}
 	        	if (m_implementation == null) {
-	        		throw new IllegalStateException("Implementation cannot be null");
+                    m_logger.log(Logger.LOG_ERROR, "Implementation cannot be null.");
 	        	}
-	            m_serviceInstance = m_implementation;
+	        	if (m_serviceInstance == null) {
+	        	    m_serviceInstance = m_implementation;
+	        	}
 	        }
 	        // configure the bundle context
 	        configureImplementation(BundleContext.class, m_context);
@@ -704,8 +713,8 @@
 					instances = (Object[]) m.invoke(m_compositionManagerInstance, null);
 				}
 	    		catch (Exception e) {
-					// TODO Auto-generated catch block
-					e.printStackTrace();
+                    m_logger.log(Logger.LOG_ERROR, "Could not obtain instances from the composition manager.", e);
+                    return;
 				}
     		}
     	}
@@ -728,29 +737,8 @@
 		                        }
 		                    }
 		                    catch (Exception e) {
-		                        System.err.println("Exception while trying to set " + fields[j].getName() +
-		                            " of type " + fields[j].getType().getName() +
-		                            " by classloader " + fields[j].getType().getClassLoader() +
-		                            " which should equal type " + clazz.getName() +
-		                            " by classloader " + clazz.getClassLoader() +
-		                            " of type " + serviceClazz.getName() +
-		                            " by classloader " + serviceClazz.getClassLoader() +
-		                            " on " + serviceInstance +
-		                            " by classloader " + serviceInstance.getClass().getClassLoader() +
-		                            "\nDumping stack:"
-		                        );
-		                        e.printStackTrace();
-		                        System.out.println("C: " + clazz);
-		                        System.out.println("I: " + instance);
-		                        System.out.println("I:C: " + instance.getClass().getClassLoader());
-		                        Class[] classes = instance.getClass().getInterfaces();
-		                        for (int k = 0; k < classes.length; k++) {
-		                            Class c = classes[k];
-		                            System.out.println("I:C:I: " + c);
-		                            System.out.println("I:C:I:C: " + c.getClassLoader());
-		                        }
-		                        System.out.println("F: " + fields[j]);
-		                        throw new IllegalStateException("Could not set field " + fields[j].getName() + " on " + serviceInstance);
+		                        m_logger.log(Logger.LOG_ERROR, "Could not set field " + fields[j], e);
+		                        return;
 		                    }
 		                }
 		            }
diff --git a/dependencymanager/src/main/java/org/apache/felix/dependencymanager/ServiceTracker.java b/dependencymanager/src/main/java/org/apache/felix/dependencymanager/ServiceTracker.java
index 0202e49..1c083de 100644
--- a/dependencymanager/src/main/java/org/apache/felix/dependencymanager/ServiceTracker.java
+++ b/dependencymanager/src/main/java/org/apache/felix/dependencymanager/ServiceTracker.java
@@ -31,10 +31,7 @@
 import org.osgi.framework.ServiceReference;
 
 /**
- * TODO copied this from the OSGi specification, but it's not clear if that
- * is allowed or not, for now I modified as little as possible but I might
- * integrate only the parts I want as soon as this code is finished. Perhaps
- * it would be better to borrow the Knopflerfish implementation here.
+ * Copied this from the OSGi specification and made a slight modification.
  * 
  * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
  */
diff --git a/dependencymanager/src/main/java/org/apache/felix/dependencymanager/State.java b/dependencymanager/src/main/java/org/apache/felix/dependencymanager/State.java
index 986f70c..56aa0de 100644
--- a/dependencymanager/src/main/java/org/apache/felix/dependencymanager/State.java
+++ b/dependencymanager/src/main/java/org/apache/felix/dependencymanager/State.java
@@ -3,17 +3,19 @@
 import java.util.List;
 
 /**
- * Encapsulates the current state of the dependencies of a service.
+ * Encapsulates the current state of the dependencies of a service. A state is
+ * basically an immutable value object.
  * 
  * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
  */
 public final class State {
-	public static final String[] STATES = { "?", "inactive", "waiting for required", "tracking optional" };
-	public static final int INACTIVE = 1;
-	public static final int WAITING_FOR_REQUIRED = 2;
-	public static final int TRACKING_OPTIONAL = 3;
+    private static final String[] STATES = { "?", "inactive", "waiting for required", "tracking optional" };
+    private static final int INACTIVE = 1;
+    private static final int WAITING_FOR_REQUIRED = 2;
+    private static final int TRACKING_OPTIONAL = 3;
 	private final List m_deps;
 	private final int m_state;
+    private String m_stringValue;
 	
 	/**
 	 * Creates a new state instance.
@@ -46,10 +48,6 @@
     	}
 	}
 	
-	public int getState() {
-		return m_state;
-	}
-	
 	public boolean isInactive() {
 		return m_state == INACTIVE;
 	}
@@ -67,13 +65,17 @@
 	}
 	
 	public String toString() {
-		StringBuffer buf = new StringBuffer();
-		buf.append("State[" + STATES[m_state] + "|");
-		List deps = m_deps;
-    	for (int i = 0; i < deps.size(); i++) {
-    		Dependency dep = (Dependency) deps.get(i);
-    		buf.append("(" + dep + (dep.isRequired() ? " R" : " O") + (dep.isAvailable() ? " +" : " -") + ")");
-    	}
-    	return buf.toString();
+	    if (m_stringValue == null) {
+	        // we only need to determine this once, but we do it lazily
+	        StringBuffer buf = new StringBuffer();
+    		buf.append("State[" + STATES[m_state] + "|");
+    		List deps = m_deps;
+        	for (int i = 0; i < deps.size(); i++) {
+        		Dependency dep = (Dependency) deps.get(i);
+        		buf.append("(" + dep + (dep.isRequired() ? " R" : " O") + (dep.isAvailable() ? " +" : " -") + ")");
+        	}
+        	m_stringValue = buf.toString();
+	    }
+        return m_stringValue;
 	}
 }
diff --git a/dependencymanager/src/test/java/org/apache/felix/dependencymanager/ServiceDependencyTest.java b/dependencymanager/src/test/java/org/apache/felix/dependencymanager/ServiceDependencyTest.java
index fae1491..e5bb4e0 100644
--- a/dependencymanager/src/test/java/org/apache/felix/dependencymanager/ServiceDependencyTest.java
+++ b/dependencymanager/src/test/java/org/apache/felix/dependencymanager/ServiceDependencyTest.java
@@ -24,8 +24,8 @@
 		// start the actual test
 		c1.replay();
         c2.replay();
-		DependencyManager dm = new DependencyManager(context);
-		Service service = new ServiceImpl(context).setImplementation(svc);
+		DependencyManager dm = new DependencyManager(context, null);
+		Service service = new ServiceImpl(context, null).setImplementation(svc);
 		dm.add(service);
 		dm.remove(service);
 		// verify the results
@@ -59,8 +59,8 @@
 		c1.replay();
         c2.replay();
         c3.replay();
-		DependencyManager dm = new DependencyManager(context);
-		Service service = new ServiceImpl(context)
+		DependencyManager dm = new DependencyManager(context, null);
+		Service service = new ServiceImpl(context, null)
 			.setImplementation(svc)
 			.add(dependency);
 		dm.add(service);