FELIX-5192: ConfigurationDependency race condition when component is stopped.
FELIX-5193: Factory Pid Adapter race condition when component is stopped.
Removed Eclipse warnings. Reworked the way configuration exception is handled.


git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1731471 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/dependencymanager/org.apache.felix.dependencymanager.benchmark/src/org/apache/felix/dm/benchmark/controller/impl/ScenarioControllerImpl.java b/dependencymanager/org.apache.felix.dependencymanager.benchmark/src/org/apache/felix/dm/benchmark/controller/impl/ScenarioControllerImpl.java
index ccf0f56..b27e312 100644
--- a/dependencymanager/org.apache.felix.dependencymanager.benchmark/src/org/apache/felix/dm/benchmark/controller/impl/ScenarioControllerImpl.java
+++ b/dependencymanager/org.apache.felix.dependencymanager.benchmark/src/org/apache/felix/dm/benchmark/controller/impl/ScenarioControllerImpl.java
@@ -299,17 +299,6 @@
     }
     
     /**
-     * Initialize the latches used to track when all scenario bundle components are started or stopped.
-     */
-    private void initLatches() {
-        m_startLatch = new CountDownLatch(ARTISTS
-            + (ARTISTS * (ALBUMS + (ALBUMS * TRACKS))));
-        
-        m_stopLatch = new CountDownLatch(ARTISTS
-            + (ARTISTS * (ALBUMS + (ALBUMS * TRACKS))));
-    }
-
-    /**
      * Returns the time consumed by the given runnable, ²ch is executed by this method.
      */
     private long durationOf(Runnable scenario) {
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AspectWithCallbacksServiceDependencyTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AspectWithCallbacksServiceDependencyTest.java
index 9905aac..afe581d 100644
--- a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AspectWithCallbacksServiceDependencyTest.java
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AspectWithCallbacksServiceDependencyTest.java
@@ -20,11 +20,8 @@
 
 import org.apache.felix.dm.Component;
 import org.apache.felix.dm.DependencyManager;
-import org.apache.felix.dm.itest.api.AspectBaseTest.ServiceAspect;
-import org.apache.felix.dm.itest.api.AspectBaseTest.ServiceInterface;
 import org.apache.felix.dm.itest.util.Ensure;
 import org.apache.felix.dm.itest.util.TestBase;
-import org.osgi.framework.ServiceReference;
 
 
 /**
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/conf/Configurator.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/conf/Configurator.java
index 727f284..beafc6c 100644
--- a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/conf/Configurator.java
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/conf/Configurator.java
@@ -21,7 +21,6 @@
 import java.io.IOException;
 import java.util.Hashtable;
 
-import org.apache.felix.dependencymanager.samples.hello.api.ServiceConsumerConf;
 import org.osgi.service.cm.Configuration;
 import org.osgi.service.cm.ConfigurationAdmin;
 import org.osgi.service.log.LogService;
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/hello/annot/ServiceConsumer.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/hello/annot/ServiceConsumer.java
index 7d3e5bf..5f81710 100644
--- a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/hello/annot/ServiceConsumer.java
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/hello/annot/ServiceConsumer.java
@@ -18,8 +18,6 @@
  */
 package org.apache.felix.dependencymanager.samples.hello.annot;
 
-import java.util.Dictionary;
-
 import org.apache.felix.dm.annotation.api.Component;
 import org.apache.felix.dm.annotation.api.ConfigurationDependency;
 import org.apache.felix.dm.annotation.api.ServiceDependency;
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/hello/api/ServiceConsumer.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/hello/api/ServiceConsumer.java
index a5097b7..b49fac0 100644
--- a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/hello/api/ServiceConsumer.java
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/hello/api/ServiceConsumer.java
@@ -18,8 +18,7 @@
  */
 package org.apache.felix.dependencymanager.samples.hello.api;
 
-import java.util.Dictionary;
-
+import org.osgi.service.cm.ConfigurationException;
 import org.osgi.service.log.LogService;
 
 /**
@@ -32,7 +31,7 @@
     volatile LogService log;
     ServiceConsumerConf conf;
     
-    protected void updated(ServiceConsumerConf conf) {
+    protected void updated(ServiceConsumerConf conf) throws ConfigurationException {
         this.conf = conf;
     }
 
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/AbstractDecorator.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/AbstractDecorator.java
index 044166b..b3dded5 100644
--- a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/AbstractDecorator.java
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/AbstractDecorator.java
@@ -40,14 +40,16 @@
 public abstract class AbstractDecorator  {
     protected volatile DependencyManager m_manager;
     private final Map<Object, Component> m_services = new ConcurrentHashMap<>();
-    
-    public abstract Component createService(Object[] properties);
+    private volatile ComponentContext m_decoratorComponent;
+
+    public abstract Component createService(Object[] properties) throws Exception;
     
     /**
      * Catches our DependencyManager handle from our component init method.
      */
     public void init(Component c) {
         m_manager = c.getDependencyManager();
+        m_decoratorComponent = (ComponentContext) c;
     }
     
     /**
@@ -55,8 +57,8 @@
      * For now, it's only used by the FactoryConfigurationAdapterImpl class, 
      * but it might also make sense to use this for Resource Adapters ...
      */
-    public void updateService(Object[] properties) {
-        throw new NoSuchMethodError("Method updateService not implemented");
+    public void updateService(Object[] properties) throws Exception {
+        throw new NoSuchMethodException("Method updateService not implemented");
     }
     
     /**
@@ -104,29 +106,27 @@
         }
     }
     
-    // callbacks for FactoryConfigurationAdapterImpl
-    public void updated(String pid, @SuppressWarnings("rawtypes") Dictionary properties) throws ConfigurationException {
-        try {
-            Component service = m_services.get(pid);
-            if (service == null) { 
-                service = createService(new Object[] { properties });
-                m_services.put(pid, service);
-                m_manager.add(service);
-            }
-            else {
-                updateService(new Object[] { properties, service });
-            }
+    // callbacks for FactoryConfigurationAdapterImpl from the ConfigAdmin thread
+    @SuppressWarnings("rawtypes")
+    public void updated(String pid, Dictionary properties) throws ConfigurationException {
+        // FELIX-5193: invoke the updated callback in the internal decorator component queue, in order
+        // to safely detect if the component is still active or not.
+        InvocationUtil.invokeUpdated(m_decoratorComponent.getExecutor(), () -> updatedSafe(pid, properties));
+    }
+    
+    @SuppressWarnings("rawtypes")
+    private void updatedSafe(String pid, Dictionary properties) throws Exception {
+        if (!m_decoratorComponent.isActive()) {
+            // Our decorator component has been removed: ignore the configuration update.
+            return;
         }
-        catch (Throwable t) {
-            if (t instanceof ConfigurationException) {
-                throw (ConfigurationException) t;
-            }
-            else if (t.getCause() instanceof ConfigurationException) {
-                throw (ConfigurationException) t.getCause();
-            }
-            else {
-                throw new ConfigurationException(null, "Could not create service for ManagedServiceFactory Pid " + pid, t);
-            }
+        Component service = m_services.get(pid);
+        if (service == null) {
+            service = createService(new Object[] { properties });
+            m_services.put(pid, service);
+            m_manager.add(service);
+        } else {
+            updateService(new Object[] { properties, service });
         }
     }
 
@@ -138,7 +138,7 @@
     }
 
     // callbacks for resources
-    public void added(URL resource) {
+    public void added(URL resource) throws Exception {
         Component newService = createService(new Object[] { resource });
         m_services.put(resource, newService);
         m_manager.add(newService);
@@ -153,7 +153,7 @@
     }
     
     // callbacks for services
-    public void added(ServiceReference ref, Object service) {
+    public void added(ServiceReference ref, Object service) throws Exception {
         Component newService = createService(new Object[] { ref, service });
         m_services.put(ref, newService);
         m_manager.add(newService);
@@ -177,7 +177,7 @@
     }
     
     // callbacks for bundles
-    public void added(Bundle bundle) {
+    public void added(Bundle bundle) throws Exception {
         Component newService = createService(new Object[] { bundle });
         m_services.put(bundle, newService);
         m_manager.add(newService);
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/Configurable.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/Configurable.java
index e05e985..2ca5c3e 100644
--- a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/Configurable.java
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/Configurable.java
@@ -112,6 +112,7 @@
             return result;
         }
 
+        @SuppressWarnings("unchecked")
         private Object convert(ParameterizedType type, String key, Object value) throws Exception {
             Class<?> resultType = (Class<?>) type.getRawType();
             if (Class.class.isAssignableFrom(resultType)) {
@@ -171,6 +172,7 @@
             throw new RuntimeException("Unhandled type: " + type);
         }
 
+        @SuppressWarnings({ "unchecked", "rawtypes" })
         private Object convert(Type type, String key, Object value, boolean useImplicitDefault) throws Exception {
             if (type instanceof ParameterizedType) {
                 return convert((ParameterizedType) type, key, value);
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ConfigurationDependencyImpl.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ConfigurationDependencyImpl.java
index 5725161..3749fac 100644
--- a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ConfigurationDependencyImpl.java
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ConfigurationDependencyImpl.java
@@ -18,16 +18,10 @@
  */
 package org.apache.felix.dm.impl;
 
-import java.lang.reflect.InvocationTargetException;
 import java.util.Arrays;
 import java.util.Dictionary;
 import java.util.Objects;
 import java.util.Properties;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.FutureTask;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.stream.Stream;
 
@@ -61,7 +55,6 @@
 	private final Logger m_logger;
 	private final BundleContext m_context;
 	private boolean m_needsInstance = true;
-	private final static int UPDATE_MAXWAIT = 30000; // max time to wait until a component has handled a configuration change event.
 
     public ConfigurationDependencyImpl() {
         this(null, null);
@@ -250,7 +243,7 @@
 		}
 		return m_settings;
 	}
-    
+	    
     @SuppressWarnings({"unchecked", "rawtypes"})
     @Override
     public void updated(final Dictionary settings) throws ConfigurationException {
@@ -272,43 +265,12 @@
         // callback is invoked safely. So, we use a Callable and a FutureTask that allows to handle the 
         // configuration update through the component executor. We still wait for the result because
         // in case of any configuration error, we have to return it from the current thread.
+        // Notice that scheduling the handling of the configuration update in the component queue also
+        // allows to safely check if the component is still active (it could be being stopped concurrently:
+        // see the invokeUpdated method which tests if our dependency is still alive (by calling super.istarted()
+        // method).
         
-        Callable<ConfigurationException> result = new Callable<ConfigurationException>() {
-            @Override
-            public ConfigurationException call() throws Exception {
-                try {
-                    invokeUpdated(settings); // either the callback instance or the component instances, if available.
-                } catch (ConfigurationException e) {
-                    return e;
-                }
-                return null;
-            }            
-        };
-        
-        // Schedule the configuration update in the component executor. In Normal case, the task is immediately executed.
-        // But in a highly concurrent system, and if the component is being reconfigured, the component may be currently busy
-        // (handling a service dependency event for example), so the task will be enqueued in the component executor, and
-        // we'll wait for the task execution by using a FutureTask:
-        
-        FutureTask<ConfigurationException> ft = new FutureTask<>(result);
-        m_component.getExecutor().execute(ft);
-        
-        try {
-            ConfigurationException confError = ft.get(UPDATE_MAXWAIT, TimeUnit.MILLISECONDS);
-            if (confError != null) {
-                throw confError; // will be logged by the Configuration Admin service;
-            }
-          }
-
-        catch (ExecutionException error) {
-            throw new ConfigurationException(null, "Configuration update error, unexpected exception.", error);
-        } catch (InterruptedException error) {
-            // will be logged by the Configuration Admin service;
-            throw new ConfigurationException(null, "Configuration update interrupted.", error);
-        } catch (TimeoutException error) {
-            // will be logged by the Configuration Admin service;
-            throw new ConfigurationException(null, "Component did not handle configuration update timely.", error);
-        }
+        InvocationUtil.invokeUpdated(m_component.getExecutor(), () -> invokeUpdated(settings));
         
         // At this point, we have accepted the configuration.
         synchronized (this) {
@@ -337,8 +299,8 @@
         case ADDED:
             try {
                 invokeUpdated(m_settings);
-            } catch (ConfigurationException e) {
-                logConfigurationException(e);
+            } catch (Throwable err) {
+                logConfigurationException(err);
             }
             break;
         case CHANGED:
@@ -386,9 +348,18 @@
         return new CallbackTypeDef(sigs, args);
     }
 
-    private void invokeUpdated(Dictionary<?, ?> settings) throws ConfigurationException {
+    // Called from the configuration component internal queue. 
+    private void invokeUpdated(Dictionary<?, ?> settings) throws Exception {
         if (m_updateInvokedCache.compareAndSet(false, true)) {
             
+            // FELIX-5192: we have to handle the following race condition: one thread stops a component (removes it from a DM object);
+            // another thread removes the configuration (from ConfigurationAdmin). in this case we may be called in our
+            // ManagedService.updated(null), but our component instance has been destroyed and does not exist anymore.
+            // In this case: do nothing.            
+            if (! super.isStarted()) {
+                return;
+            }
+            
             // FELIX-5155: if component impl is an internal DM adapter, we must not invoke the callback on it
             // because in case there is an external callback instance specified for the configuration callback,
             // then we don't want to invoke it now. The external callback instance will be invoked
@@ -400,11 +371,8 @@
                 return;
             }
             
-            Object[] instances = super.getInstances(); // either the callback instance or the component instances
-            if (instances == null) {
-                return;
-            }
-
+            Object[] instances = super.getInstances(); // never null, either the callback instance or the component instances            
+            
             CallbackTypeDef callbackInfo = createCallbackType(m_logger, m_component, m_configType, settings);
             boolean callbackFound = false;
             for (int i = 0; i < instances.length; i++) {
@@ -412,24 +380,9 @@
                     InvocationUtil.invokeCallbackMethod(instances[i], m_add, callbackInfo.m_sigs, callbackInfo.m_args);
                     callbackFound |= true;
                 }
-                catch (InvocationTargetException e) {
-                    // The component has thrown an exception during it's callback invocation.
-                    if (e.getTargetException() instanceof ConfigurationException) {
-                        // the callback threw an OSGi ConfigurationException: just re-throw it.
-                        throw (ConfigurationException) e.getTargetException();
-                    }
-                    else {
-                        // wrap the callback exception into a ConfigurationException.
-                        throw new ConfigurationException(null, "Configuration update failed", e.getTargetException());
-                    }
-                }
                 catch (NoSuchMethodException e) {
                     // if the method does not exist, ignore it
                 }
-                catch (Throwable t) {
-                    // wrap any other exception as a ConfigurationException.
-                    throw new ConfigurationException(null, "Configuration update failed", t);
-                }
             }
             
             if (! callbackFound) {
@@ -445,7 +398,7 @@
         }
     }
         
-    private void logConfigurationException(ConfigurationException e) {
-        m_logger.log(Logger.LOG_ERROR, "Got exception while handling configuration update for pid " + m_pid, e);
+    private void logConfigurationException(Throwable err) {
+        m_logger.log(Logger.LOG_ERROR, "Got exception while handling configuration update for pid " + m_pid, err);
     }
 }
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/FactoryConfigurationAdapterImpl.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/FactoryConfigurationAdapterImpl.java
index 9d1c978..3578950 100644
--- a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/FactoryConfigurationAdapterImpl.java
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/FactoryConfigurationAdapterImpl.java
@@ -18,7 +18,8 @@
  */
 package org.apache.felix.dm.impl;
 
-import java.lang.reflect.InvocationTargetException;
+import static org.apache.felix.dm.impl.ConfigurationDependencyImpl.createCallbackType;
+
 import java.util.Arrays;
 import java.util.Dictionary;
 import java.util.Enumeration;
@@ -36,8 +37,6 @@
 import org.osgi.service.metatype.MetaTypeProvider;
 import org.osgi.service.metatype.ObjectClassDefinition;
 
-import static org.apache.felix.dm.impl.ConfigurationDependencyImpl.createCallbackType;
-
 /**
  * Factory configuration adapter service implementation. This class extends the FilterService in order to catch
  * some Service methods for configuring actual adapter service implementation.
@@ -128,7 +127,7 @@
          * Method called from our superclass, when we need to create a service.
          */
         @SuppressWarnings("unchecked")
-        public Component createService(Object[] properties) {
+        public Component createService(Object[] properties) throws Exception {
             Dictionary<String, ?> settings = (Dictionary<String, ?>) properties[0];     
             Component newService = m_manager.createComponent();        
 
@@ -160,7 +159,7 @@
          * the configuration has changed.
          */
         @SuppressWarnings("unchecked")
-        public void updateService(Object[] properties) {
+        public void updateService(Object[] properties) throws Exception {
             Dictionary<String, ?> cmSettings = (Dictionary<String, ?>) properties[0];
             Component service = (Component) properties[1];
             CallbackTypeDef callbackInfo = createCallbackType(m_logger, service, m_configType, cmSettings);
@@ -173,7 +172,7 @@
             }
         }
         
-        private void invokeUpdated(Component service, CallbackTypeDef callbackInfo) {
+        private void invokeUpdated(Component service, CallbackTypeDef callbackInfo) throws Exception {
             boolean callbackFound = false;
             Object[] instances = getUpdateCallbackInstances(service);
             for (Object instance : instances) {
@@ -181,16 +180,9 @@
                     InvocationUtil.invokeCallbackMethod(instance, m_update, callbackInfo.m_sigs, callbackInfo.m_args);
                     callbackFound |= true;
                 }
-                catch (InvocationTargetException e) {
-                    // The component has thrown an exception during it's callback invocation.
-                    handleException(e.getTargetException());
-                }
                 catch (NoSuchMethodException e) {
                     // if the method does not exist, ignore it
                 }
-                catch (Throwable t) {
-                    handleException(t); // will rethrow a runtime exception.
-                }
             }
             
             if (! callbackFound) {
@@ -247,25 +239,6 @@
             
             return props;
         }
-
-        private void handleException(Throwable t) {
-            if (m_logger != null) {
-                m_logger.log(Logger.LOG_ERROR, "Got exception while handling configuration update for factory pid " + m_factoryPid, t);
-            } else {
-                
-            }
-            if (t instanceof InvocationTargetException) {
-                // Our super class will check if the target exception is itself a ConfigurationException.
-                // In this case, it will simply re-thrown.
-                throw new RuntimeException(((InvocationTargetException) t).getTargetException());
-            }
-            else if (t instanceof RuntimeException) {
-                throw (RuntimeException) t;
-            }
-            else {
-                throw new RuntimeException(t);
-            }
-        }
     }
     
     /**
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/InvocationUtil.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/InvocationUtil.java
index 880eba2..e452e3c 100644
--- a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/InvocationUtil.java
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/InvocationUtil.java
@@ -25,6 +25,12 @@
 import java.util.Arrays;
 import java.util.LinkedHashMap;
 import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Executor;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.TimeUnit;
+
+import org.osgi.service.cm.ConfigurationException;
 
 /**
  * Utility methods for invoking callbacks. Lookups of callbacks are accellerated by using a LRU cache.
@@ -47,6 +53,19 @@
     }
     
     /**
+     * Interface internally used to handle a ConfigurationAdmin update synchronously, in a component executor queue.
+     */
+    @FunctionalInterface
+    public interface ConfigurationHandler {
+        public void handle() throws Exception;
+    }
+
+    /**
+     * Max time to wait until a configuration update callback has returned.
+     */
+    private final static int UPDATED_MAXWAIT = 30000; // max time to wait until a CM update has completed
+    
+    /**
      * Invokes a callback method on an instance. The code will search for a callback method with
      * the supplied name and any of the supplied signatures in order, invoking the first one it finds.
      * 
@@ -202,4 +221,44 @@
             return size() > m_size;
         }
     }
+    
+    /**
+     * Invokes a configuration update callback synchronously, but through the component executor queue.
+     */
+    public static void invokeUpdated(Executor queue, ConfigurationHandler handler) throws ConfigurationException {
+        Callable<Exception> result = () -> {
+            try {
+                handler.handle();
+            } catch (Exception e) {
+                return e;
+            }
+            return null;
+        };
+        
+        FutureTask<Exception> ft = new FutureTask<>(result);
+        queue.execute(ft);
+                
+        try {
+            Exception err = ft.get(UPDATED_MAXWAIT, TimeUnit.MILLISECONDS);
+            if (err != null) {
+                throw err;
+            }
+        }
+        
+        catch (ConfigurationException e) {
+            throw e;
+        }
+
+        catch (Throwable error) {
+            Throwable rootCause = error.getCause();
+            if (rootCause != null) {
+                if (rootCause instanceof ConfigurationException) {
+                    throw (ConfigurationException) rootCause;
+                }
+                throw new ConfigurationException("", "Configuration update error, unexpected exception.", rootCause);
+            } else {
+                throw new ConfigurationException("", "Configuration update error, unexpected exception.", error);
+            }
+        }
+    }
 }
diff --git a/dependencymanager/org.apache.felix.dependencymanager/test/org/apache/felix/dm/impl/ConfigurationDependencyImplTest.java b/dependencymanager/org.apache.felix.dependencymanager/test/org/apache/felix/dm/impl/ConfigurationDependencyImplTest.java
index af82ff0..04bebdc 100644
--- a/dependencymanager/org.apache.felix.dependencymanager/test/org/apache/felix/dm/impl/ConfigurationDependencyImplTest.java
+++ b/dependencymanager/org.apache.felix.dependencymanager/test/org/apache/felix/dm/impl/ConfigurationDependencyImplTest.java
@@ -75,6 +75,7 @@
             super(ensure);
         }
 
+        @SuppressWarnings("rawtypes")
         @Override
         public void updated(Dictionary config) throws ConfigurationException {
             super.updated(config);
@@ -126,6 +127,7 @@
             m_ensure = ensure;
         }
 
+        @SuppressWarnings("rawtypes")
         public void updated(Dictionary config) throws ConfigurationException {
             m_ensure.step();
 
@@ -141,6 +143,7 @@
             m_ensure.step();
         }
 
+        @SuppressWarnings("rawtypes")
         private void assertConfiguration(Dictionary cfg) {
             assertEquals("isTrue", "true", cfg.get("true"));
             assertEquals("getValue", "42", cfg.get("value"));
@@ -229,9 +232,11 @@
         ConfigurationDependencyImpl result = new ConfigurationDependencyImpl(bc, mockLogger);
         result.setCallback(service, "updated").setPid("does.not.matter");
         result.setComponentContext(component);
+        result.start();
         return result;
     }
 
+    @SuppressWarnings("rawtypes")
     private Dictionary createDictionary() {
         Dictionary<String, Object> result = new Hashtable<>();
         result.put("true", "true");
diff --git a/dependencymanager/org.apache.felix.dependencymanager/test/test/Ensure.java b/dependencymanager/org.apache.felix.dependencymanager/test/test/Ensure.java
index 4d2378f..ff2c308 100644
--- a/dependencymanager/org.apache.felix.dependencymanager/test/test/Ensure.java
+++ b/dependencymanager/org.apache.felix.dependencymanager/test/test/Ensure.java
@@ -49,7 +49,7 @@
         }
     }
 
-    public void setStream(PrintStream output) {
+    public static void setStream(PrintStream output) {
         STREAM = output;
     }