You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by pd...@apache.org on 2015/06/04 12:15:36 UTC

svn commit: r1683504 - in /felix/trunk/dependencymanager: org.apache.felix.dependencymanager.annotation/ org.apache.felix.dependencymanager.itest/ org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ org.apache.felix.dependencyma...

Author: pderop
Date: Thu Jun  4 10:15:35 2015
New Revision: 1683504

URL: http://svn.apache.org/r1683504
Log:
FELIX-4907: ConfigurationDependency calls updated(null) when component is stopped.
FELIX-4910: ComponentExecutorFactory does not allow to return null from getExecutorFor method.
FELIX-4913: DM Optional callbacks may sometimes be invoked twice.
FELIX-4876: DM Annotations bnd plugin compatibility with Bndtools 2.4.1 / 3.0.0 versions.
[FELIX-4877: DM Annotations should detect service type using more method signatures.

Modified:
    felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/bnd.bnd
    felix/trunk/dependencymanager/org.apache.felix.dependencymanager.itest/bnd.bnd
    felix/trunk/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ServiceRaceTest.java
    felix/trunk/dependencymanager/org.apache.felix.dependencymanager.samples/bnd.bnd
    felix/trunk/dependencymanager/org.apache.felix.dependencymanager/bnd.bnd
    felix/trunk/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/ConfigurationDependency.java
    felix/trunk/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ComponentImpl.java
    felix/trunk/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ComponentScheduler.java
    felix/trunk/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ConfigurationDependencyImpl.java
    felix/trunk/dependencymanager/org.apache.felix.dependencymanager/test/test/ServiceRaceTest.java

Modified: felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/bnd.bnd
URL: http://svn.apache.org/viewvc/felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/bnd.bnd?rev=1683504&r1=1683503&r2=1683504&view=diff
==============================================================================
--- felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/bnd.bnd (original)
+++ felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/bnd.bnd Thu Jun  4 10:15:35 2015
@@ -14,7 +14,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 #
-Bundle-Version: 4.0.1
+Bundle-Version: 4.0.2
 -buildpath:  \
 	osgi.core;version=4.2,\
 	de.twentyeleven.skysail.org.json-osgi;version=20080701.0,\

Modified: felix/trunk/dependencymanager/org.apache.felix.dependencymanager.itest/bnd.bnd
URL: http://svn.apache.org/viewvc/felix/trunk/dependencymanager/org.apache.felix.dependencymanager.itest/bnd.bnd?rev=1683504&r1=1683503&r2=1683504&view=diff
==============================================================================
--- felix/trunk/dependencymanager/org.apache.felix.dependencymanager.itest/bnd.bnd (original)
+++ felix/trunk/dependencymanager/org.apache.felix.dependencymanager.itest/bnd.bnd Thu Jun  4 10:15:35 2015
@@ -18,7 +18,7 @@
 	org.apache.felix.metatype;version=1.0.4,\
 	org.apache.felix.gogo.runtime;version=0.10.0,\
 	org.apache.felix.log;version=1.0.1,\
-	org.apache.felix.configadmin;version=1.8.4,\
+	org.apache.felix.configadmin;version=1.8.6,\
 	org.apache.felix.dependencymanager;version=latest,\
 	org.apache.felix.dependencymanager.shell;version=latest
 -runee: JavaSE-1.7

Modified: felix/trunk/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ServiceRaceTest.java
URL: http://svn.apache.org/viewvc/felix/trunk/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ServiceRaceTest.java?rev=1683504&r1=1683503&r2=1683504&view=diff
==============================================================================
--- felix/trunk/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ServiceRaceTest.java (original)
+++ felix/trunk/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ServiceRaceTest.java Thu Jun  4 10:15:35 2015
@@ -221,11 +221,9 @@ public class ServiceRaceTest extends Tes
         // Ensure that client has been stopped, then destroyed, then unbound from all dependencies
         expectedStep += 2; // stop/destroy
         expectedStep += DEPENDENCIES; // removed all dependencies
-        expectedStep += 1; // removed configuration
         step.waitForStep(expectedStep, STEP_WAIT);
         step.ensure();
         Assert.assertEquals(0, clientImpl.getDependencies());
-        Assert.assertNull(clientImpl.getConfiguration());                
 
         if (super.errorsLogged()) {
             throw new IllegalStateException("Race test interrupted (some error occured, see previous logs)");
@@ -258,29 +256,27 @@ public class ServiceRaceTest extends Tes
         }
 
         public void updated(Dictionary conf) throws ConfigurationException {
-            m_conf = conf;
-            try {
-                if (conf != null) {
+            if (conf != null) {
+                try {
                     Assert.assertEquals("bar", conf.get("foo"));
+                    m_conf = conf;
                     m_step.step(1);
-                } else {
-                    m_step.step();
+                } catch (Throwable t) {
+                    m_step.throwable(t);
                 }
-            } catch (Throwable t) {
-                m_step.throwable(t);
             }
         }
         
         void add(Dep d) {
             Assert.assertNotNull(d);
-            m_step.step();
             m_dependencies ++;
+            m_step.step();
         }
         
         void remove(Dep d) {
             Assert.assertNotNull(d);
-            m_step.step();
             m_dependencies --;
+            m_step.step();
         }
                 
         void start() {

Modified: felix/trunk/dependencymanager/org.apache.felix.dependencymanager.samples/bnd.bnd
URL: http://svn.apache.org/viewvc/felix/trunk/dependencymanager/org.apache.felix.dependencymanager.samples/bnd.bnd?rev=1683504&r1=1683503&r2=1683504&view=diff
==============================================================================
--- felix/trunk/dependencymanager/org.apache.felix.dependencymanager.samples/bnd.bnd (original)
+++ felix/trunk/dependencymanager/org.apache.felix.dependencymanager.samples/bnd.bnd Thu Jun  4 10:15:35 2015
@@ -34,7 +34,7 @@ Bundle-Version: 1.0.0
 	org.apache.felix.gogo.shell;version=0.10.0,\
 	org.apache.felix.dependencymanager.shell;version=latest,\
 	org.apache.felix.dependencymanager.runtime;version=latest,\
-	org.apache.felix.configadmin;version=1.8.0,\
+	org.apache.felix.configadmin;version=1.8.6,\
 	org.apache.felix.eventadmin;version=1.4.3,\
 	biz.aQute.bndlib;version=2.3.0,\
 	org.apache.felix.webconsole;version=4.2.2,\

Modified: felix/trunk/dependencymanager/org.apache.felix.dependencymanager/bnd.bnd
URL: http://svn.apache.org/viewvc/felix/trunk/dependencymanager/org.apache.felix.dependencymanager/bnd.bnd?rev=1683504&r1=1683503&r2=1683504&view=diff
==============================================================================
--- felix/trunk/dependencymanager/org.apache.felix.dependencymanager/bnd.bnd (original)
+++ felix/trunk/dependencymanager/org.apache.felix.dependencymanager/bnd.bnd Thu Jun  4 10:15:35 2015
@@ -35,7 +35,7 @@ Include-Resource: META-INF/=resources/LI
 	META-INF/=resources/changelog.txt
 Import-Package: !org.junit,!org.mockito.*,*
 Bundle-Activator: org.apache.felix.dm.impl.Activator
-Bundle-Version: 4.1.0
+Bundle-Version: 4.1.1
 Bundle-Name: Apache Felix Dependency Manager
 Bundle-Description: Provides dynamic service and component dependency management
 Bundle-License: http://www.apache.org/licenses/LICENSE-2.0.txt

Modified: felix/trunk/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/ConfigurationDependency.java
URL: http://svn.apache.org/viewvc/felix/trunk/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/ConfigurationDependency.java?rev=1683504&r1=1683503&r2=1683504&view=diff
==============================================================================
--- felix/trunk/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/ConfigurationDependency.java (original)
+++ felix/trunk/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/ConfigurationDependency.java Thu Jun  4 10:15:35 2015
@@ -23,15 +23,24 @@ package org.apache.felix.dm;
  * it, specify a PID for the configuration. The dependency is always required, because if it is
  * not, it does not make sense to use the dependency manager. In that scenario, simply register
  * your component as a <code>ManagedService(Factory)</code> and handle everything yourself. Also,
- * only managed services are supported, not factories. There are a couple of things you need to
- * be aware of when implementing the <code>updated(Dictionary)</code> method:
+ * only managed services are supported, not factories. If you need support for factories, then
+ * you can use 
+ * {@link DependencyManager#createFactoryConfigurationAdapterService(String, String, boolean)}.
+ * There are a couple of things you need to be aware of when implementing the 
+ * <code>updated(Dictionary)</code> method:
  * <ul>
- * <li>Make sure it throws a <code>ConfigurationException</code> when you get a configuration
- * that is invalid. In this case, the dependency will not change: if it was not available, it
- * will still not be. If it was available, it will remain available and implicitly assume you
- * keep working with your old configuration.</li>
+ * <li>Make sure it throws a <code>ConfigurationException</code> or any other exception when you 
+ * get a configuration that is invalid. In this case, the dependency will not change: 
+ * if it was not available, it will still not be. If it was available, it will remain available 
+ * and implicitly assume you keep working with your old configuration.</li>
  * <li>This method will be called before all required dependencies are available. Make sure you
  * do not depend on these to parse your settings.</li>
+ * <li>unlike all other DM dependency callbacks, the update method is called from the CM configuration
+ * update thread, and is not serialized with the internal queue maintained by the DM component.
+ * So, take care to concurrent calls between updated callback and your other lifecycle callbacks.
+ * <li>When the configuration is lost, updated callback is invoked with a null dictionary parameter,
+ * and then the component stop lifecycle callback is invoked.
+ * <li>When the DM component is stopped, then updated(null) is not invoked.
  * </ul>
  * 
  * The callback invoked when a configuration dependency is updated can supports the following signatures:<p>
@@ -46,6 +55,7 @@ public interface ConfigurationDependency
      * Sets the name of the callback method that should be invoked when a configuration
      * is available. The contract for this method is identical to that of
      * <code>ManagedService.updated(Dictionary) throws ConfigurationException</code>.
+     * By default, if this method is not called, the callback name is "updated".
      * 
      * @param callback the name of the callback method
      */

Modified: felix/trunk/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ComponentImpl.java
URL: http://svn.apache.org/viewvc/felix/trunk/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ComponentImpl.java?rev=1683504&r1=1683503&r2=1683504&view=diff
==============================================================================
--- felix/trunk/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ComponentImpl.java (original)
+++ felix/trunk/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ComponentImpl.java Thu Jun  4 10:15:35 2015
@@ -33,6 +33,7 @@ import java.util.Dictionary;
 import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.Hashtable;
+import java.util.IdentityHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -46,6 +47,7 @@ import java.util.concurrent.atomic.Atomi
 import org.apache.felix.dm.Component;
 import org.apache.felix.dm.ComponentDeclaration;
 import org.apache.felix.dm.ComponentDependencyDeclaration;
+import org.apache.felix.dm.ComponentExecutorFactory;
 import org.apache.felix.dm.ComponentState;
 import org.apache.felix.dm.ComponentStateListener;
 import org.apache.felix.dm.Dependency;
@@ -53,8 +55,8 @@ import org.apache.felix.dm.DependencyMan
 import org.apache.felix.dm.Logger;
 import org.apache.felix.dm.context.ComponentContext;
 import org.apache.felix.dm.context.DependencyContext;
-import org.apache.felix.dm.context.EventType;
 import org.apache.felix.dm.context.Event;
+import org.apache.felix.dm.context.EventType;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.ServiceRegistration;
@@ -66,57 +68,211 @@ import org.osgi.service.log.LogService;
  * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
  */
 public class ComponentImpl implements Component, ComponentContext, ComponentDeclaration {
+    /**
+     * NullObject ServiceRegistration that is injected in components that don't provide any services. 
+     */
 	private static final ServiceRegistration NULL_REGISTRATION = (ServiceRegistration) Proxy
 			.newProxyInstance(ComponentImpl.class.getClassLoader(),
 					new Class[] { ServiceRegistration.class },
 					new DefaultNullObject());
+	
+	/**
+	 * Constant Used to get empty constructor by reflection. 
+	 */
     private static final Class<?>[] VOID = new Class[] {};
+    
+    /**
+     * Default Component Executor, which is by default single threaded. The first thread which schedules a task
+     * is the master thread and will execute all tasks that are scheduled by other threads at the time the master
+     * thread is executing. Internal tasks scheduled by the master thread are executed immediately (inline execution).
+     * 
+     * If a ComponentExecutorFactory is provided in the OSGI registry, then this executor will be replaced by the
+     * executor returned by the ComponentExecutorFactory (however, the same semantic of the default executor is used: 
+     * all tasks are serially executed).
+     * 
+     * @see @link {@link ComponentExecutorFactory}
+     */
 	private volatile Executor m_executor = new SerialExecutor(new Logger(null));
+	
+	/**
+	 * The current state of the component state machine.
+	 */
 	private ComponentState m_state = ComponentState.INACTIVE;
+	
+    /**
+     * Indicates that the handleChange method is currently being executed.
+     */
+    private boolean m_handlingChange;
+    
+    /**
+     * List of dependencies. We use a COW list in order to avoid ConcurrentModificationException while iterating on the 
+     * list and while a component synchronously add more dependencies from one of its callback method.
+     */
 	private final CopyOnWriteArrayList<DependencyContext> m_dependencies = new CopyOnWriteArrayList<>();
+	
+	/**
+	 * List of Component state listeners. We use a COW list in order to avoid ConcurrentModificationException while iterating on the 
+     * list and while a component synchronously add more listeners from one of its callback method.
+	 */
 	private final List<ComponentStateListener> m_listeners = new CopyOnWriteArrayList<>();
+	
+	/**
+	 * Is the component active ?
+	 */
 	private boolean m_isStarted;
+	
+	/**
+	 * The Component logger.
+	 */
     private final Logger m_logger;
+    
+    /**
+     * The Component bundle context.
+     */
     private final BundleContext m_context;
+    
+    /**
+     * The DependencyManager object that has created this component.
+     */
     private final DependencyManager m_manager;
+    
+    /**
+     * The object used to create the component. Can be a class name, or the component implementation instance.
+     */
     private Object m_componentDefinition;
+    
+    /**
+     * The component instance.
+     */
 	private Object m_componentInstance;
+	
+	/**
+	 * The service(s) provided by this component. Can be a String, or a String array.
+	 */
     private volatile Object m_serviceName;
+    
+    /**
+     * The service properties, if this component is providing a service.
+     */
     private volatile Dictionary<Object, Object> m_serviceProperties;
+    
+    /**
+     * The component service registration. Can be a NullObject in case the component does not provide a service.
+     */
     private volatile ServiceRegistration m_registration;
+    
+    /**
+     * Map of auto configured fields (BundleContext, ServiceRegistration, DependencyManager, or Component).
+     * By default, all fields mentioned above are auto configured (injected in class fields having the same type).
+     */
     private final Map<Class<?>, Boolean> m_autoConfig = new ConcurrentHashMap<>();
+    
+    /**
+     * Map of auto configured instance fields that will be used when injected auto configured fields.
+     * @see #m_autoConfig
+     */
     private final Map<Class<?>, String> m_autoConfigInstance = new ConcurrentHashMap<>();
+    
+    /**
+     * Data structure used to record the elapsed time used by component lifecycle callbacks.
+     * Key = callback name ("init", "start", "stop", "destroy").
+     * Value = elapsed time in nanos.
+     */
     private final Map<String, Long> m_stopwatch = new ConcurrentHashMap<>();
+    
+    /**
+     * Unique component id.
+     */
     private final long m_id;
-    private static AtomicLong m_idGenerator = new AtomicLong();
-    // Holds all the services of a given dependency context. Caution: the last entry in the skiplist is the highest ranked service.
+    
+    /**
+     * Unique ID generator.
+     */
+    private final static AtomicLong m_idGenerator = new AtomicLong();
+    
+    /**
+     * Holds all the services of a given dependency context. Caution: the last entry in the skiplist is the highest 
+     * ranked service.
+     */
     private final Map<DependencyContext, ConcurrentSkipListSet<Event>> m_dependencyEvents = new HashMap<>();
+    
+    /**
+     * Flag used to check if this component has been added in a DependencyManager object.
+     */
     private final AtomicBoolean m_active = new AtomicBoolean(false);
         
-    public Component setDebug(String debugKey) {
-    	// Force debug level in our logger
-        m_logger.setEnabledLevel(LogService.LOG_DEBUG);
-        m_logger.setDebugKey(debugKey);
-    	return this;
-    }
-
-    // configuration (static)
+    /**
+     * Init lifecycle callback. From that method, component are expected to add more extra dependencies.
+     * When this callback is invoked, all required dependencies have been injected. 
+     */
     private volatile String m_callbackInit;
+    
+    /**
+     * Start lifecycle callback. When this method is called, all required + all extra required dependencies defined in the
+     * init callback have been injected. The component may then perform its initialization.
+     */
     private volatile String m_callbackStart;
+    
+    /**
+     * Stop callback. When this method is called, the component has been unregistered (if it provides any services),
+     * and all optional dependencies have been unbound.
+     */
     private volatile String m_callbackStop;
+    
+    /**
+     * Destroy callback. When this method is called, all required dependencies defined in the init method have been unbound.
+     * After this method is called, then all required dependencies defined in the Activator will be unbound.
+     */
     private volatile String m_callbackDestroy;
+    
+    /**
+     * By default, the init/start/stop/destroy callbacks are invoked on the component instance(s).
+     * But you can specify a separate callback instance.
+     */
     private volatile Object m_callbackInstance;
     
-    // instance factory
+    /**
+     * Component Factory instance object, that can be used to instantiate the component instance.
+     */
 	private volatile Object m_instanceFactory;
+	
+	/**
+	 * Name of the Factory method to call.
+	 */
 	private volatile String m_instanceFactoryCreateMethod;
 	
-	// composition manager
+	/**
+	 * Composition Manager that can be used to create a graph of objects that are used to implement the component.
+	 */
 	private volatile Object m_compositionManager;
+	
+	/**
+	 * Name of the method used to invoke in order to get the list of component instance objects.
+	 */
 	private volatile String m_compositionManagerGetMethod;
+	
+	/**
+	 * The composition manager instance object, if specified.
+	 */
 	private volatile Object m_compositionManagerInstance;
+	
+	/**
+	 * The Component bundle.
+	 */
     private final Bundle m_bundle;
+        
+    /**
+     * Cache of callback invocation used to avoid calling the same callback twice.
+     * This situation may sometimes happen when the state machine triggers a lifecycle callback ("bind" call), and
+     * when the bind method registers a service which is tracked by another optional component dependency.
+     * 
+     * @see org.apache.felix.dm.itest.api.FELIX4913_OptionalCallbackInvokedTwiceTest which reproduces the use case.
+     */
+    private final Map<Event, Event> m_invokeCallbackCache = new IdentityHashMap<>();
 	
+    /**
+     * Default component declaration implementation.
+     */
     static class SCDImpl implements ComponentDependencyDeclaration {
         private final String m_name;
         private final int m_state;
@@ -149,10 +305,19 @@ public class ComponentImpl implements Co
         }
     }
 
+    /**
+     * Constructor. Only used for tests.
+     */
     public ComponentImpl() {
 	    this(null, null, new Logger(null));
 	}
 	
+    /**
+     * Constructor
+     * @param context the component bundle context 
+     * @param manager the manager used to create the component
+     * @param logger the logger to use
+     */
     public ComponentImpl(BundleContext context, DependencyManager manager, Logger logger) {
         m_context = context;
         m_bundle = context != null ? context.getBundle() : null;
@@ -169,6 +334,14 @@ public class ComponentImpl implements Co
         m_id = m_idGenerator.getAndIncrement();
     }
 
+    @Override
+    public Component setDebug(String debugKey) {
+        // Force debug level in our logger
+        m_logger.setEnabledLevel(LogService.LOG_DEBUG);
+        m_logger.setDebugKey(debugKey);
+        return this;
+    }
+
 	@Override
 	public Component add(final Dependency ... dependencies) {
 		getExecutor().execute(new Runnable() {
@@ -218,6 +391,7 @@ public class ComponentImpl implements Co
 		return this;
 	}
 
+	@Override
 	public void start() {
 	    if (m_active.compareAndSet(false, true)) {
             getExecutor().execute(new Runnable() {
@@ -230,6 +404,7 @@ public class ComponentImpl implements Co
 	    }
 	}
 	
+	@Override
 	public void stop() {           
 	    if (m_active.compareAndSet(true, false)) {
 	        Executor executor = getExecutor();
@@ -284,19 +459,25 @@ public class ComponentImpl implements Co
         getExecutor().execute(new Runnable() {
             @Override
             public void run() {
-                switch (type) {
-                case ADDED:
-                    handleAdded(dc, event[0]);
-                    break;
-                case CHANGED:
-                    handleChanged(dc, event[0]);
-                    break;
-                case REMOVED:
-                    handleRemoved(dc, event[0]);
-                    break;
-                case SWAPPED:
-                    handleSwapped(dc, event[0], event[1]);
-                    break;
+                try {
+                    switch (type) {
+                    case ADDED:
+                        handleAdded(dc, event[0]);
+                        break;
+                    case CHANGED:
+                        handleChanged(dc, event[0]);
+                        break;
+                    case REMOVED:
+                        handleRemoved(dc, event[0]);
+                        break;
+                    case SWAPPED:
+                        handleSwapped(dc, event[0], event[1]);
+                        break;
+                    }
+                } finally {
+                	// Clear cache of component callbacks invocation, except if we are currently called from handleChange().
+                	// (See FELIX-4913).
+                    clearInvokeCallbackCache();
                 }
             }
         });
@@ -313,275 +494,682 @@ public class ComponentImpl implements Co
         return m_dependencyEvents.get(dc);
     }
 
+    @Override
     public Component setAutoConfig(Class<?> clazz, boolean autoConfig) {
         m_autoConfig.put(clazz, Boolean.valueOf(autoConfig));
         return this;
     }
     
+    @Override
     public Component setAutoConfig(Class<?> clazz, String instanceName) {
         m_autoConfig.put(clazz, Boolean.valueOf(instanceName != null));
         m_autoConfigInstance.put(clazz, instanceName);
         return this;
     }
     
+    @Override
     public boolean getAutoConfig(Class<?> clazz) {
         Boolean result = (Boolean) m_autoConfig.get(clazz);
         return (result != null && result.booleanValue());
     }
     
+    @Override
     public String getAutoConfigInstance(Class<?> clazz) {
         return (String) m_autoConfigInstance.get(clazz);
     }
 
-    private void handleAdded(DependencyContext dc, Event e) {
-        if (! m_isStarted) {
-            return;
+    @SuppressWarnings("unchecked")
+    public <T> T getInstance() {     
+        Object[] instances  = getCompositionInstances();
+        return instances.length == 0 ? null : (T) instances[0]; 
+    }
+
+    public Object[] getInstances() {
+        return getCompositionInstances();
+    }
+    
+    public void invokeCallbackMethod(Object[] instances, String methodName, Class<?>[][] signatures, Object[][] parameters) {
+        invokeCallbackMethod(instances, methodName, signatures, parameters, true);
+    }
+
+    public void invokeCallbackMethod(Object[] instances, String methodName, Class<?>[][] signatures,
+        Object[][] parameters, boolean logIfNotFound) {
+        boolean callbackFound = false;
+        for (int i = 0; i < instances.length; i++) {
+            try {
+                InvocationUtil.invokeCallbackMethod(instances[i], methodName, signatures, parameters);
+                callbackFound |= true;
+            }
+            catch (NoSuchMethodException e) {
+                // if the method does not exist, ignore it
+            }
+            catch (InvocationTargetException e) {
+                // the method itself threw an exception, log that
+                m_logger.log(Logger.LOG_ERROR, "Invocation of '" + methodName + "' failed.", e.getCause());
+            }
+            catch (Throwable e) {
+                m_logger.log(Logger.LOG_ERROR, "Could not invoke '" + methodName + "'.", e);
+            }
         }
-        m_logger.debug("handleAdded %s", e);
         
-        Set<Event> dependencyEvents = m_dependencyEvents.get(dc);
-        dependencyEvents.add(e);        
-        dc.setAvailable(true);
-        
-        // In the following switch block, we sometimes only recalculate state changes 
-        // if the dependency is fully started. If the dependency is not started,
-        // it means it is actually starting (the service tracker is executing the open method). 
-        // And in this case, depending on the state, we don't recalculate state changes now. We'll do it 
-        // once all currently available services are found, and then after, 
-        // we'll recalculate state change (see the startDependencies method). 
-        // 
-        // All this is done for two reasons:
-        // 1- optimization: it is preferable to recalculate state changes once we know about all currently 
-        //    available dependency services (after the tracker has returned from its open method).
-        // 2- This also allows to determine the list of currently available dependency services from within 
-        //    the component start method callback (this will be extremely useful when porting the Felix SCR 
-        //    on top of DM4).
-        
-        switch (m_state) {
-        case WAITING_FOR_REQUIRED:            
-            if (dc.isStarted() && dc.isRequired()) {
-                // if dependency is starting, we'll handle change after the tracker has returned (see startDependencies method).
-                handleChange();
-            }
-            break;
-        case INSTANTIATED_AND_WAITING_FOR_REQUIRED:
-            if (!dc.isInstanceBound()) {
-                if (dc.isRequired()) {
-                    dc.invokeCallback(EventType.ADDED, e);
-                }
-                updateInstance(dc, e, false, true);
+        // If the callback is not found, we don't log if the method is on an AbstractDecorator.
+        // (Aspect or Adapter are not interested in user dependency callbacks)        
+        if (logIfNotFound && ! callbackFound && ! (getInstance() instanceof AbstractDecorator)) {
+            if (m_logger == null) {
+                System.out.println("Callback \"" + methodName + "\" not found on componnent instances "
+                    + Arrays.toString(getInstances()));
             } else {
-                if (dc.isStarted() && dc.isRequired()) {
-                    // if dependency is starting, we'll handle change after the tracker has returned (see startDependencies method).
-                    handleChange();
-                }
+                m_logger.log(LogService.LOG_ERROR, "Callback \"" + methodName + "\" callback not found on componnent instances "
+                    + Arrays.toString(getInstances()));
             }
-            break;
-        case TRACKING_OPTIONAL:
-            dc.invokeCallback(EventType.ADDED, e);
-            updateInstance(dc, e, false, true);
-            break;
-        default:
+
         }
     }
-        
-    private void handleChanged(final DependencyContext dc, final Event e) {
-        if (! m_isStarted) {
-            return;
-        }
-        Set<Event> dependencyEvents = m_dependencyEvents.get(dc);
-        dependencyEvents.remove(e);
-        dependencyEvents.add(e);
-        
-        switch (m_state) {
-        case TRACKING_OPTIONAL:
-            dc.invokeCallback(EventType.CHANGED, e);
-            updateInstance(dc, e, true, false);
-            break;
 
-        case INSTANTIATED_AND_WAITING_FOR_REQUIRED:
-            if (!dc.isInstanceBound()) {
-                dc.invokeCallback(EventType.CHANGED, e);
-                updateInstance(dc, e, true, false);
-            }
-            break;
-        default:
-            // noop
-        }
+    @Override
+    public boolean isAvailable() {
+        return m_state == TRACKING_OPTIONAL;
     }
     
-    private void handleRemoved(DependencyContext dc, Event e) {
-        if (! m_isStarted) {
-            return;
-        }
-        // Check if the dependency is still available.
-        Set<Event> dependencyEvents = m_dependencyEvents.get(dc);
-        int size = dependencyEvents.size();
-        if (dependencyEvents.contains(e)) {
-            size--; // the dependency is currently registered and is about to be removed.
-        }
-        dc.setAvailable(size > 0);
-        
-        // If the dependency is now unavailable, we have to recalculate state change. This will result in invoking the
-        // "removed" callback with the removed dependency (which we have not yet removed from our dependency events list.).
-        // But we don't recalculate the state if the dependency is not started (if not started, it means that it is currently starting,
-        // and the tracker is detecting a removed service).
-        if (size == 0 && dc.isStarted()) {
-            handleChange();
-        }
-        
-        // Now, really remove the dependency event.
-        dependencyEvents.remove(e);    
-        
-        // Depending on the state, we possible have to invoke the callbacks and update the component instance.        
-        switch (m_state) {
-        case INSTANTIATED_AND_WAITING_FOR_REQUIRED:
-            if (!dc.isInstanceBound()) {
-                if (dc.isRequired()) {
-                    dc.invokeCallback(EventType.REMOVED, e);
-                }
-                updateInstance(dc, e, false, false);
-            }
-            break;
-        case TRACKING_OPTIONAL:
-            dc.invokeCallback(EventType.REMOVED, e);
-            updateInstance(dc, e, false, false);
-            break;
-        default:
-        }
+    @Override
+    public boolean isActive() {
+        return m_active.get();
     }
     
-    private void handleSwapped(DependencyContext dc, Event oldEvent, Event newEvent) {
-        if (! m_isStarted) {
-            return;
-        }
-        Set<Event> dependencyEvents = m_dependencyEvents.get(dc);        
-        dependencyEvents.remove(oldEvent);
-        dependencyEvents.add(newEvent);
-                
-        // Depending on the state, we possible have to invoke the callbacks and update the component instance.        
-        switch (m_state) {
-        case WAITING_FOR_REQUIRED:
-            // No need to swap, we don't have yet injected anything
-            break;
-        case INSTANTIATED_AND_WAITING_FOR_REQUIRED:
-            // Only swap *non* instance-bound dependencies
-            if (!dc.isInstanceBound()) {
-                if (dc.isRequired()) {
-                    dc.invokeCallback(EventType.SWAPPED, oldEvent, newEvent);
+    @Override
+    public Component add(final ComponentStateListener l) {
+        m_listeners.add(l);
+        return this;
+    }
+
+    @Override
+    public Component remove(ComponentStateListener l) {
+        m_listeners.remove(l);
+        return this;
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public List<DependencyContext> getDependencies() {
+        return (List<DependencyContext>) m_dependencies.clone();
+    }
+
+    @Override
+    public Component setImplementation(Object implementation) {
+        m_componentDefinition = implementation;
+        return this;
+    }
+    
+    @Override
+    public ServiceRegistration getServiceRegistration() {
+        return m_registration;
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public <K,V> Dictionary<K, V> getServiceProperties() {
+        if (m_serviceProperties != null) {
+            // Applied patch from FELIX-4304
+            Hashtable<Object, Object> serviceProperties = new Hashtable<>();
+            addTo(serviceProperties, m_serviceProperties);
+            return (Dictionary<K, V>) serviceProperties;
+        }
+        return null;
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public Component setServiceProperties(final Dictionary<?, ?> serviceProperties) {
+        getExecutor().execute(new Runnable() {
+            @Override
+            public void run() {
+                Dictionary<Object, Object> properties = null;
+                m_serviceProperties = (Dictionary<Object, Object>) serviceProperties;
+                if ((m_registration != null) && (m_serviceName != null)) {
+                    properties = calculateServiceProperties();
+                    m_registration.setProperties(properties);
                 }
             }
-            break;
-        case TRACKING_OPTIONAL:
-            dc.invokeCallback(EventType.SWAPPED, oldEvent, newEvent);
-            break;
-        default:
+        });
+        return this;
+    }
+    
+    public Component setCallbacks(String init, String start, String stop, String destroy) {
+        ensureNotActive();
+        m_callbackInit = init;
+        m_callbackStart = start;
+        m_callbackStop = stop;
+        m_callbackDestroy = destroy;
+        return this;
+    }
+    
+    public Component setCallbacks(Object instance, String init, String start, String stop, String destroy) {
+        ensureNotActive();
+        m_callbackInstance = instance;
+        m_callbackInit = init;
+        m_callbackStart = start;
+        m_callbackStop = stop;
+        m_callbackDestroy = destroy;
+        return this;
+    }
+
+    @Override
+    public Component setFactory(Object factory, String createMethod) {
+        ensureNotActive();
+        m_instanceFactory = factory;
+        m_instanceFactoryCreateMethod = createMethod;
+        return this;
+    }
+
+    @Override
+    public Component setFactory(String createMethod) {
+        return setFactory(null, createMethod);
+    }
+
+    @Override
+    public Component setComposition(Object instance, String getMethod) {
+        ensureNotActive();
+        m_compositionManager = instance;
+        m_compositionManagerGetMethod = getMethod;
+        return this;
+    }
+
+    @Override
+    public Component setComposition(String getMethod) {
+        return setComposition(null, getMethod);
+    }
+
+    @Override
+    public DependencyManager getDependencyManager() {
+        return m_manager;
+    }
+    
+    public ComponentDependencyDeclaration[] getComponentDependencies() {
+        List<DependencyContext> deps = getDependencies();
+        if (deps != null) {
+            ComponentDependencyDeclaration[] result = new ComponentDependencyDeclaration[deps.size()];
+            for (int i = 0; i < result.length; i++) {
+                DependencyContext dep = (DependencyContext) deps.get(i);
+                if (dep instanceof ComponentDependencyDeclaration) {
+                    result[i] = (ComponentDependencyDeclaration) dep;
+                }
+                else {
+                    result[i] = new SCDImpl(dep.toString(), (dep.isAvailable() ? 1 : 0) + (dep.isRequired() ? 2 : 0), dep.getClass().getName());
+                }
+            }
+            return result;
         }
+        return null;
     }
     
-	private void handleChange() {
-	    m_logger.debug("handleChanged");
-		try {
-			ComponentState oldState;
-			ComponentState newState;
-			do {
-				oldState = m_state;
-				newState = calculateNewState(oldState);
-				m_logger.debug("%s -> %s", oldState, newState);
-				m_state = newState;
-			} while (performTransition(oldState, newState));
-		} finally {
-		    m_logger.debug("end handling change.");
-		}
-	}
+    public String getName() {
+        StringBuffer sb = new StringBuffer();
+        Object serviceName = m_serviceName;
+        if (serviceName instanceof String[]) {
+            String[] names = (String[]) serviceName;
+            for (int i = 0; i < names.length; i++) {
+                if (i > 0) {
+                    sb.append(", ");
+                }
+                sb.append(names[i]);
+            }
+            appendProperties(sb);
+        } else if (serviceName instanceof String) {
+            sb.append(serviceName.toString());
+            appendProperties(sb);
+        } else {
+            Object implementation = m_componentDefinition;
+            if (implementation != null) {
+                if (implementation instanceof Class) {
+                    sb.append(((Class<?>) implementation).getName());
+                } else {
+                    // If the implementation instance does not override "toString", just display
+                    // the class name, else display the component using its toString method
+                    try {
+                    Method m = implementation.getClass().getMethod("toString", new Class[0]);
+                        if (m.getDeclaringClass().equals(Object.class)) {
+                            sb.append(implementation.getClass().getName());
+                        } else {
+                            sb.append(implementation.toString());
+                        }
+                    }  catch (java.lang.NoSuchMethodException e) {
+                        // Just display the class name
+                        sb.append(implementation.getClass().getName());
+                    }
+                }
+            } else {
+                sb.append(super.toString());
+            }
+        }
+        return sb.toString();
+    }
     
-	/** Based on the current state, calculate the new state. */
-	private ComponentState calculateNewState(ComponentState currentState) {
-		if (currentState == INACTIVE) {
-			if (m_isStarted) {
-				return WAITING_FOR_REQUIRED;
-			}
-		}
-		if (currentState == WAITING_FOR_REQUIRED) {
-			if (!m_isStarted) {
-				return INACTIVE;
-			}
-			if (allRequiredAvailable()) {
-				return INSTANTIATED_AND_WAITING_FOR_REQUIRED;
-			}
-		}
-		if (currentState == INSTANTIATED_AND_WAITING_FOR_REQUIRED) {
-			if (m_isStarted && allRequiredAvailable()) {
-				if (allInstanceBoundAvailable()) {
-					return TRACKING_OPTIONAL;
-				}
-				return currentState;
-			}
-			return WAITING_FOR_REQUIRED;
-		}
-		if (currentState == TRACKING_OPTIONAL) {
-			if (m_isStarted && allRequiredAvailable() && allInstanceBoundAvailable()) {
-				return currentState;
-			}
-			return INSTANTIATED_AND_WAITING_FOR_REQUIRED;
-		}
-		return currentState;
-	}
+    @Override
+    public BundleContext getBundleContext() {
+        return m_context;
+    }
+    
+    @Override
+    public Bundle getBundle() {
+        return m_bundle;
+    }
 
-	/** Perform all the actions associated with state transitions. Returns true if a transition was performed. */
-	private boolean performTransition(ComponentState oldState, ComponentState newState) {
-//		System.out.println("transition from " + oldState + " to " + newState);
-		if (oldState == ComponentState.INACTIVE && newState == ComponentState.WAITING_FOR_REQUIRED) {
-		    startDependencies(m_dependencies);
-			notifyListeners(newState);
-			return true;
-		}
-		if (oldState == ComponentState.WAITING_FOR_REQUIRED && newState == ComponentState.INSTANTIATED_AND_WAITING_FOR_REQUIRED) {
-			instantiateComponent();
+    public long getId() {
+        return m_id;
+    }
+    
+    public String getClassName() {
+        Object serviceInstance = m_componentInstance;
+        if (serviceInstance != null) {
+            return serviceInstance.getClass().getName();
+        } 
+        
+        Object implementation = m_componentDefinition;
+        if (implementation != null) {
+            if (implementation instanceof Class) {
+                return ((Class<?>) implementation).getName();
+            }
+            return implementation.getClass().getName();
+        } 
+        
+        Object instanceFactory = m_instanceFactory;
+        if (instanceFactory != null) {
+            return instanceFactory.getClass().getName();
+        } else {
+            // unexpected.
+            return ComponentImpl.class.getName();
+        }
+    }
+    
+    public String[] getServices() {
+        if (m_serviceName instanceof String[]) {
+            return (String[]) m_serviceName;
+        } else if (m_serviceName instanceof String) {
+            return new String[] { (String) m_serviceName };
+        } else {
+            return null;
+        }
+    }
+    
+    public int getState() {
+        return (isAvailable() ? ComponentDeclaration.STATE_REGISTERED : ComponentDeclaration.STATE_UNREGISTERED);
+    }
+
+    public void ensureNotActive() {
+        if (m_active.get()) {
+            throw new IllegalStateException("Can't modify an already started component.");
+        }
+    }
+    
+    public ComponentDeclaration getComponentDeclaration() {
+        return this;
+    }
+    
+    @Override
+    public String toString() {
+        if (m_logger.getDebugKey() != null) {
+            return m_logger.getDebugKey();
+        }
+        return getClassName();
+    }
+    
+    @Override
+    public void setThreadPool(Executor threadPool) {
+        ensureNotActive();
+        m_executor = new DispatchExecutor(threadPool, m_logger);
+    }
+    
+    @Override
+    public Logger getLogger() {
+        return m_logger;
+    }
+
+    @Override
+    public Map<String, Long> getCallbacksTime() {
+        return m_stopwatch;
+    }
+    
+    // ---------------------- Package/Private methods ---------------------------
+    
+    void instantiateComponent() {
+        m_logger.debug("instantiating component.");
+
+        // TODO add more complex factory instantiations of one or more components in a composition here
+        if (m_componentInstance == null) {
+            if (m_componentDefinition instanceof Class) {
+                try {
+                    m_componentInstance = createInstance((Class<?>) m_componentDefinition);
+                }
+                catch (Exception e) {
+                    m_logger.log(Logger.LOG_ERROR, "Could not instantiate class " + m_componentDefinition, e);
+                }
+            }
+            else {
+                if (m_instanceFactoryCreateMethod != null) {
+                    Object factory = null;
+                    if (m_instanceFactory != null) {
+                        if (m_instanceFactory instanceof Class) {
+                            try {
+                                factory = createInstance((Class<?>) m_instanceFactory);
+                            }
+                            catch (Exception e) {
+                                m_logger.log(Logger.LOG_ERROR, "Could not create factory instance of class " + m_instanceFactory + ".", e);
+                            }
+                        }
+                        else {
+                            factory = m_instanceFactory;
+                        }
+                    }
+                    else {
+                        // TODO review if we want to try to default to something if not specified
+                        // for now the JavaDoc of setFactory(method) reflects the fact that we need
+                        // to review it
+                    }
+                    if (factory == null) {
+                        m_logger.log(Logger.LOG_ERROR, "Factory cannot be null.");
+                    }
+                    else {
+                        try {
+                            m_componentInstance = InvocationUtil.invokeMethod(factory, 
+                                factory.getClass(), m_instanceFactoryCreateMethod, 
+                                new Class[][] {{}, {Component.class}}, new Object[][] {{}, {this}}, false);
+                        }
+                        catch (Exception e) {
+                            m_logger.log(Logger.LOG_ERROR, "Could not create service instance using factory " + factory + " method " + m_instanceFactoryCreateMethod + ".", e);
+                        }
+                    }
+                }
+            }
+            
+            if (m_componentInstance == null) {
+                m_componentInstance = m_componentDefinition;
+            }
+            
+            // configure the bundle context
+            autoConfigureImplementation(BundleContext.class, m_context);
+            autoConfigureImplementation(ServiceRegistration.class, NULL_REGISTRATION);
+            autoConfigureImplementation(DependencyManager.class, m_manager);
+            autoConfigureImplementation(Component.class, this);
+        }
+    }    
+    
+    /**
+     * Runs the state machine, to see if a change event has to trigger some component state transition.
+     */
+    private void handleChange() {
+        m_logger.debug("handleChanged");
+    	handlingChange(true);
+        try {
+            ComponentState oldState;
+            ComponentState newState;
+            do {
+                oldState = m_state;
+                newState = calculateNewState(oldState);
+                m_logger.debug("%s -> %s", oldState, newState);
+                m_state = newState;
+            } while (performTransition(oldState, newState));
+        } finally {
+        	handlingChange(false);
+            clearInvokeCallbackCache();
+            m_logger.debug("end handling change.");
+        }
+    }
+    
+    /** 
+     * Based on the current state, calculate the new state. 
+     */
+    private ComponentState calculateNewState(ComponentState currentState) {
+        if (currentState == INACTIVE) {
+            if (m_isStarted) {
+                return WAITING_FOR_REQUIRED;
+            }
+        }
+        if (currentState == WAITING_FOR_REQUIRED) {
+            if (!m_isStarted) {
+                return INACTIVE;
+            }
+            if (allRequiredAvailable()) {
+                return INSTANTIATED_AND_WAITING_FOR_REQUIRED;
+            }
+        }
+        if (currentState == INSTANTIATED_AND_WAITING_FOR_REQUIRED) {
+            if (m_isStarted && allRequiredAvailable()) {
+                if (allInstanceBoundAvailable()) {
+                    return TRACKING_OPTIONAL;
+                }
+                return currentState;
+            }
+            return WAITING_FOR_REQUIRED;
+        }
+        if (currentState == TRACKING_OPTIONAL) {
+            if (m_isStarted && allRequiredAvailable() && allInstanceBoundAvailable()) {
+                return currentState;
+            }
+            return INSTANTIATED_AND_WAITING_FOR_REQUIRED;
+        }
+        return currentState;
+    }
+
+    /** 
+     * Perform all the actions associated with state transitions. 
+     * @returns true if a transition was performed.
+     **/
+    private boolean performTransition(ComponentState oldState, ComponentState newState) {
+        if (oldState == ComponentState.INACTIVE && newState == ComponentState.WAITING_FOR_REQUIRED) {
+            startDependencies(m_dependencies);
+            notifyListeners(newState);
+            return true;
+        }
+        if (oldState == ComponentState.WAITING_FOR_REQUIRED && newState == ComponentState.INSTANTIATED_AND_WAITING_FOR_REQUIRED) {
+            instantiateComponent();
             invokeAutoConfigDependencies();
-			invokeAddRequiredDependencies();
+            invokeAddRequiredDependencies();
 			ComponentState stateBeforeCallingInit = m_state;
-	        invoke(m_callbackInit); 
+            invoke(m_callbackInit); 
 	        if (stateBeforeCallingInit == m_state) {
 	            notifyListeners(newState); // init did not change current state, we can notify about this new state
 	        }
-			return true;
-		}
-		if (oldState == ComponentState.INSTANTIATED_AND_WAITING_FOR_REQUIRED && newState == ComponentState.TRACKING_OPTIONAL) {
+            return true;
+        }
+        if (oldState == ComponentState.INSTANTIATED_AND_WAITING_FOR_REQUIRED && newState == ComponentState.TRACKING_OPTIONAL) {
             invokeAutoConfigInstanceBoundDependencies();
-			invokeAddRequiredInstanceBoundDependencies();
-			invoke(m_callbackStart);
-			invokeAddOptionalDependencies();
-			registerService();
-			notifyListeners(newState);
-			return true;
-		}
-		if (oldState == ComponentState.TRACKING_OPTIONAL && newState == ComponentState.INSTANTIATED_AND_WAITING_FOR_REQUIRED) {
-		    unregisterService();
-		    invokeRemoveOptionalDependencies();
-			invoke(m_callbackStop);
-			invokeRemoveInstanceBoundDependencies();
-			notifyListeners(newState);
-			return true;
-		}
-		if (oldState == ComponentState.INSTANTIATED_AND_WAITING_FOR_REQUIRED && newState == ComponentState.WAITING_FOR_REQUIRED) {
-			invoke(m_callbackDestroy);
-			invokeRemoveRequiredDependencies();
-			notifyListeners(newState);
-			if (! someDependenciesNeedInstance()) {
+            invokeAddRequiredInstanceBoundDependencies();
+            invoke(m_callbackStart);
+            invokeAddOptionalDependencies();
+            registerService();
+            notifyListeners(newState);
+            return true;
+        }
+        if (oldState == ComponentState.TRACKING_OPTIONAL && newState == ComponentState.INSTANTIATED_AND_WAITING_FOR_REQUIRED) {
+            unregisterService();
+            invokeRemoveOptionalDependencies();
+            invoke(m_callbackStop);
+            invokeRemoveInstanceBoundDependencies();
+            notifyListeners(newState);
+            return true;
+        }
+        if (oldState == ComponentState.INSTANTIATED_AND_WAITING_FOR_REQUIRED && newState == ComponentState.WAITING_FOR_REQUIRED) {
+            invoke(m_callbackDestroy);
+            invokeRemoveRequiredDependencies();
+            notifyListeners(newState);
+            if (! someDependenciesNeedInstance()) {
                 destroyComponent();
             }
-			return true;
-		}
-		if (oldState == ComponentState.WAITING_FOR_REQUIRED && newState == ComponentState.INACTIVE) {
-		    stopDependencies();
+            return true;
+        }
+        if (oldState == ComponentState.WAITING_FOR_REQUIRED && newState == ComponentState.INACTIVE) {
+            stopDependencies();
             destroyComponent();
-			notifyListeners(newState);
-			return true;
-		}
-		return false;
-	}
-	
+            notifyListeners(newState);
+            return true;
+        }
+        return false;
+    }
+    
+    /**
+     * Sets the m_handlingChange flag that indicates if the state machine is currently running the handleChange method.
+     */
+    private void handlingChange(boolean transiting) {
+        m_handlingChange = transiting;
+    }
+    
+    /**
+     * Are we currently running the handleChange method ?
+     */
+    private boolean isHandlingChange() {
+    	return m_handlingChange;
+    }
+
+    /**
+     * Then handleEvent calls this method when a dependency service is being added.
+     */
+    private void handleAdded(DependencyContext dc, Event e) {
+        if (! m_isStarted) {
+            return;
+        }
+        m_logger.debug("handleAdded %s", e);
+        
+        Set<Event> dependencyEvents = m_dependencyEvents.get(dc);
+        dependencyEvents.add(e);        
+        dc.setAvailable(true);
+                  
+        // In the following switch block, we sometimes only recalculate state changes 
+        // if the dependency is fully started. If the dependency is not started,
+        // it means it is actually starting (the service tracker is executing the open method). 
+        // And in this case, depending on the state, we don't recalculate state changes now. 
+        // 
+        // All this is done for two reasons:
+        // 1- optimization: it is preferable to recalculate state changes once we know about all currently 
+        //    available dependency services (after the tracker has returned from its open method).
+        // 2- This also allows to determine the list of currently available dependency services before calling
+        //    the component start() callback.
+        
+        switch (m_state) {
+        case WAITING_FOR_REQUIRED:            
+            if (dc.isStarted() && dc.isRequired()) {
+                handleChange();
+            }
+            break;
+        case INSTANTIATED_AND_WAITING_FOR_REQUIRED:
+            if (!dc.isInstanceBound()) {
+                if (dc.isRequired()) {
+                    invokeCallbackSafe(dc, EventType.ADDED, e);
+                }
+                updateInstance(dc, e, false, true);
+            } else {
+                if (dc.isStarted() && dc.isRequired()) {
+                    handleChange();
+                }
+            }
+            break;
+        case TRACKING_OPTIONAL:
+            invokeCallbackSafe(dc, EventType.ADDED, e);
+            updateInstance(dc, e, false, true);
+            break;
+        default:
+        }
+    }       
+    
+    /**
+     * Then handleEvent calls this method when a dependency service is being changed.
+     */
+    private void handleChanged(final DependencyContext dc, final Event e) {
+        if (! m_isStarted) {
+            return;
+        }
+        Set<Event> dependencyEvents = m_dependencyEvents.get(dc);
+        dependencyEvents.remove(e);
+        dependencyEvents.add(e);
+                
+        switch (m_state) {
+        case TRACKING_OPTIONAL:
+            invokeCallbackSafe(dc, EventType.CHANGED, e);
+            updateInstance(dc, e, true, false);
+            break;
+
+        case INSTANTIATED_AND_WAITING_FOR_REQUIRED:
+            if (!dc.isInstanceBound()) {
+                invokeCallbackSafe(dc, EventType.CHANGED, e);
+                updateInstance(dc, e, true, false);
+            }
+            break;
+        default:
+            // noop
+        }
+    }
+    
+    /**
+     * Then handleEvent calls this method when a dependency service is being removed.
+     */
+    private void handleRemoved(DependencyContext dc, Event e) {
+        if (! m_isStarted) {
+            return;
+        }
+        // Check if the dependency is still available.
+        Set<Event> dependencyEvents = m_dependencyEvents.get(dc);
+        int size = dependencyEvents.size();
+        if (dependencyEvents.contains(e)) {
+            size--; // the dependency is currently registered and is about to be removed.
+        }
+        dc.setAvailable(size > 0);
+        
+        // If the dependency is now unavailable, we have to recalculate state change. This will result in invoking the
+        // "removed" callback with the removed dependency (which we have not yet removed from our dependency events list.).
+        // But we don't recalculate the state if the dependency is not started (if not started, it means that it is currently starting,
+        // and the tracker is detecting a removed service).
+        if (size == 0 && dc.isStarted()) {
+            handleChange();
+        }
+        
+        // Now, really remove the dependency event.
+        dependencyEvents.remove(e);    
+        
+        // Depending on the state, we possible have to invoke the callbacks and update the component instance.        
+        switch (m_state) {
+        case INSTANTIATED_AND_WAITING_FOR_REQUIRED:
+            if (!dc.isInstanceBound()) {
+                if (dc.isRequired()) {
+                    invokeCallbackSafe(dc, EventType.REMOVED, e);
+                }
+                updateInstance(dc, e, false, false);
+            }
+            break;
+        case TRACKING_OPTIONAL:
+            invokeCallbackSafe(dc, EventType.REMOVED, e);
+            updateInstance(dc, e, false, false);
+            break;
+        default:
+        }
+    }
+    
+    private void handleSwapped(DependencyContext dc, Event oldEvent, Event newEvent) {
+        if (! m_isStarted) {
+            return;
+        }
+        Set<Event> dependencyEvents = m_dependencyEvents.get(dc);        
+        dependencyEvents.remove(oldEvent);
+        dependencyEvents.add(newEvent);
+                
+        // Depending on the state, we possible have to invoke the callbacks and update the component instance.        
+        switch (m_state) {
+        case WAITING_FOR_REQUIRED:
+            // No need to swap, we don't have yet injected anything
+            break;
+        case INSTANTIATED_AND_WAITING_FOR_REQUIRED:
+            // Only swap *non* instance-bound dependencies
+            if (!dc.isInstanceBound()) {
+                if (dc.isRequired()) {
+                    dc.invokeCallback(EventType.SWAPPED, oldEvent, newEvent);
+                }
+            }
+            break;
+        case TRACKING_OPTIONAL:
+            dc.invokeCallback(EventType.SWAPPED, oldEvent, newEvent);
+            break;
+        default:
+        }
+    }
+    	
     private boolean allRequiredAvailable() {
         boolean available = true;
         for (DependencyContext d : m_dependencies) {
@@ -656,8 +1244,6 @@ public class ComponentImpl implements Co
             }
             d.start();
         }
-        // The started dependencies has probably called our handleAdded method: we now have to run our state machine.
-        handleChange();
     }
     
     private void stopDependencies() {
@@ -739,68 +1325,6 @@ public class ComponentImpl implements Co
 			}
 		}
 	}
-
-	void instantiateComponent() {
-        m_logger.debug("instantiating component.");
-
-		// TODO add more complex factory instantiations of one or more components in a composition here
-	    if (m_componentInstance == null) {
-            if (m_componentDefinition instanceof Class) {
-                try {
-                    m_componentInstance = createInstance((Class<?>) m_componentDefinition);
-                }
-                catch (Exception e) {
-                    m_logger.log(Logger.LOG_ERROR, "Could not instantiate class " + m_componentDefinition, e);
-                }
-            }
-            else {
-	        	if (m_instanceFactoryCreateMethod != null) {
-	        		Object factory = null;
-		        	if (m_instanceFactory != null) {
-		        		if (m_instanceFactory instanceof Class) {
-		        			try {
-								factory = createInstance((Class<?>) m_instanceFactory);
-							}
-		                    catch (Exception e) {
-		                        m_logger.log(Logger.LOG_ERROR, "Could not create factory instance of class " + m_instanceFactory + ".", e);
-		                    }
-		        		}
-		        		else {
-		        			factory = m_instanceFactory;
-		        		}
-		        	}
-		        	else {
-		        		// TODO review if we want to try to default to something if not specified
-		        	    // for now the JavaDoc of setFactory(method) reflects the fact that we need
-		        	    // to review it
-		        	}
-		        	if (factory == null) {
-                        m_logger.log(Logger.LOG_ERROR, "Factory cannot be null.");
-		        	}
-		        	else {
-    		        	try {
-    		        		m_componentInstance = InvocationUtil.invokeMethod(factory, 
-    		        		    factory.getClass(), m_instanceFactoryCreateMethod, 
-    		        		    new Class[][] {{}, {Component.class}}, new Object[][] {{}, {this}}, false);
-    					}
-    		        	catch (Exception e) {
-    	                    m_logger.log(Logger.LOG_ERROR, "Could not create service instance using factory " + factory + " method " + m_instanceFactoryCreateMethod + ".", e);
-    					}
-		        	}
-	        	}
-            }
-            
-            if (m_componentInstance == null) {
-                m_componentInstance = m_componentDefinition;
-            }
-            
-	        // configure the bundle context
-            autoConfigureImplementation(BundleContext.class, m_context);
-            autoConfigureImplementation(ServiceRegistration.class, NULL_REGISTRATION);
-            autoConfigureImplementation(DependencyManager.class, m_manager);
-            autoConfigureImplementation(Component.class, this);
-	    }
-	}
 	
 	private void destroyComponent() {
 		m_componentInstance = null;
@@ -810,7 +1334,7 @@ public class ComponentImpl implements Co
 		for (DependencyContext d : m_dependencies) {
 			if (d.isRequired() && !d.isInstanceBound()) {
 			    for (Event e : m_dependencyEvents.get(d)) {
-			        d.invokeCallback(EventType.ADDED, e);
+			        invokeCallbackSafe(d, EventType.ADDED, e);
 			    }
 			}
 		}
@@ -836,7 +1360,7 @@ public class ComponentImpl implements Co
 		for (DependencyContext d : m_dependencies) {
 			if (d.isRequired() && d.isInstanceBound()) {
 	             for (Event e : m_dependencyEvents.get(d)) {
-	                 d.invokeCallback(EventType.ADDED, e);
+	                 invokeCallbackSafe(d, EventType.ADDED, e);
 	             }
 			}
 		}
@@ -846,7 +1370,7 @@ public class ComponentImpl implements Co
         for (DependencyContext d : m_dependencies) {
             if (! d.isRequired()) {
                 for (Event e : m_dependencyEvents.get(d)) {
-                    d.invokeCallback(EventType.ADDED, e);
+                    invokeCallbackSafe(d, EventType.ADDED, e);
                 }
             }
         }
@@ -856,7 +1380,7 @@ public class ComponentImpl implements Co
 		for (DependencyContext d : m_dependencies) {
 			if (!d.isInstanceBound() && d.isRequired()) {
                 for (Event e : m_dependencyEvents.get(d)) {
-                    d.invokeCallback(EventType.REMOVED, e);
+                    invokeCallbackSafe(d, EventType.REMOVED, e);
                 }
 			}
 		}
@@ -866,7 +1390,7 @@ public class ComponentImpl implements Co
         for (DependencyContext d : m_dependencies) {
             if (! d.isRequired()) {
                 for (Event e : m_dependencyEvents.get(d)) {
-                    d.invokeCallback(EventType.REMOVED, e);
+                    invokeCallbackSafe(d, EventType.REMOVED, e);
                 }
             }
         }
@@ -876,80 +1400,53 @@ public class ComponentImpl implements Co
 		for (DependencyContext d : m_dependencies) {
 			if (d.isInstanceBound()) {
                 for (Event e : m_dependencyEvents.get(d)) {
-                    d.invokeCallback(EventType.REMOVED, e);
+                    invokeCallbackSafe(d, EventType.REMOVED, e);
                 }
 			}
 		}
 	}
+	
+	/**
+	 * This method ensures that a dependency callback is invoked only one time;
+	 */
+	private void invokeCallbackSafe(DependencyContext dc, EventType type, Event event) {
+	    if (m_invokeCallbackCache.put(event, event) == null) {
+	        dc.invokeCallback(type, event);
+	    }
+	}
+	
+	/**
+	 * Clears the cache of invoked components callbacks.
+	 * We only clear the cache when the state machine is not running.
+	 * The cache is used to avoid calling the same bind callback twice.
+	 * See FELIX-4913.
+	 */
+	private void clearInvokeCallbackCache() {
+	    if (! isHandlingChange()) {
+	    	m_invokeCallbackCache.clear();
+	    }
+	}
 
 	private void invoke(String name) {
         if (name != null) {
-            // if a callback instance was specified, look for the method there, if not,
-            // ask the service for its composition instances
-            Object[] instances = m_callbackInstance != null ? new Object[] { m_callbackInstance } : getCompositionInstances();
-
-            long t1 = System.nanoTime();
-            try {
-                invokeCallbackMethod(instances, name, 
-                    new Class[][] {{ Component.class }, {}}, 
-                    new Object[][] {{ this }, {}},
-                    false);
-            } finally {
-                long t2 = System.nanoTime();
-                m_stopwatch.put(name, t2 - t1);
-            }
-        }
-    }
-    
-	@SuppressWarnings("unchecked")
-    public <T> T getInstance() {     
-	    Object[] instances  = getCompositionInstances();
-	    return instances.length == 0 ? null : (T) instances[0]; 
-    }
-
-    public Object[] getInstances() {
-    	return getCompositionInstances();
-    }
-    
-    public void invokeCallbackMethod(Object[] instances, String methodName, Class<?>[][] signatures, Object[][] parameters) {
-        invokeCallbackMethod(instances, methodName, signatures, parameters, true);
-    }
+            // if a callback instance was specified, look for the method there, if not,
+            // ask the service for its composition instances
+            Object[] instances = m_callbackInstance != null ? new Object[] { m_callbackInstance } : getCompositionInstances();
 
-    public void invokeCallbackMethod(Object[] instances, String methodName, Class<?>[][] signatures,
-        Object[][] parameters, boolean logIfNotFound) {
-        boolean callbackFound = false;
-        for (int i = 0; i < instances.length; i++) {
+            long t1 = System.nanoTime();
             try {
-                InvocationUtil.invokeCallbackMethod(instances[i], methodName, signatures, parameters);
-                callbackFound |= true;
-            }
-            catch (NoSuchMethodException e) {
-                // if the method does not exist, ignore it
-            }
-            catch (InvocationTargetException e) {
-                // the method itself threw an exception, log that
-                m_logger.log(Logger.LOG_ERROR, "Invocation of '" + methodName + "' failed.", e.getCause());
-            }
-            catch (Throwable e) {
-                m_logger.log(Logger.LOG_ERROR, "Could not invoke '" + methodName + "'.", e);
-            }
-        }
-        
-        // If the callback is not found, we don't log if the method is on an AbstractDecorator.
-        // (Aspect or Adapter are not interested in user dependency callbacks)        
-        if (logIfNotFound && ! callbackFound && ! (getInstance() instanceof AbstractDecorator)) {
-            if (m_logger == null) {
-                System.out.println("Callback \"" + methodName + "\" not found on componnent instances "
-                    + Arrays.toString(getInstances()));
-            } else {
-                m_logger.log(LogService.LOG_ERROR, "Callback \"" + methodName + "\" callback not found on componnent instances "
-                    + Arrays.toString(getInstances()));
+                invokeCallbackMethod(instances, name, 
+                    new Class[][] {{ Component.class }, {}}, 
+                    new Object[][] {{ this }, {}},
+                    false);
+            } finally {
+                long t2 = System.nanoTime();
+                m_stopwatch.put(name, t2 - t1);
             }
-
         }
     }
-
-   private Object createInstance(Class<?> clazz) throws SecurityException, NoSuchMethodException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
+    
+    private Object createInstance(Class<?> clazz) throws SecurityException, NoSuchMethodException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
 		Constructor<?> constructor = clazz.getConstructor(VOID);
 		constructor.setAccessible(true);
         return constructor.newInstance();
@@ -961,40 +1458,6 @@ public class ComponentImpl implements Co
 		}
 	}
 	
-	@Override
-	public boolean isAvailable() {
-		return m_state == TRACKING_OPTIONAL;
-	}
-	
-	@Override
-	public boolean isActive() {
-	    return m_active.get();
-	}
-	
-	@Override
-	public Component add(final ComponentStateListener l) {
-	    m_listeners.add(l);
-	    return this;
-	}
-
-	@Override
-	public Component remove(ComponentStateListener l) {
-	    m_listeners.remove(l);
-	    return this;
-	}
-
-	@SuppressWarnings("unchecked")
-    @Override
-	public List<DependencyContext> getDependencies() {
-		return (List<DependencyContext>) m_dependencies.clone();
-	}
-
-	@Override
-	public Component setImplementation(Object implementation) {
-		m_componentDefinition = implementation;
-		return this;
-	}
-	
     private void autoConfigureImplementation(Class<?> clazz, Object instance) {
         if (((Boolean) m_autoConfig.get(clazz)).booleanValue()) {
             configureImplementation(clazz, instance, (String) m_autoConfigInstance.get(clazz));
@@ -1053,85 +1516,6 @@ public class ComponentImpl implements Co
         FieldUtil.updateDependencyField(targets, fieldName, update, add, clazz, event, dc, m_logger);
     }
 
-    @Override
-	public ServiceRegistration getServiceRegistration() {
-        return m_registration;
-	}
-
-	@SuppressWarnings("unchecked")
-    @Override
-	public <K,V> Dictionary<K, V> getServiceProperties() {
-		if (m_serviceProperties != null) {
-			// Applied patch from FELIX-4304
-			Hashtable<Object, Object> serviceProperties = new Hashtable<>();
-			addTo(serviceProperties, m_serviceProperties);
-			return (Dictionary<K, V>) serviceProperties;
-		}
-		return null;
-	}
-
-	@Override
-    @SuppressWarnings("unchecked")
-	public Component setServiceProperties(final Dictionary<?, ?> serviceProperties) {
-	    getExecutor().execute(new Runnable() {
-            @Override
-			public void run() {
-			    Dictionary<Object, Object> properties = null;
-		        m_serviceProperties = (Dictionary<Object, Object>) serviceProperties;
-		        if ((m_registration != null) && (m_serviceName != null)) {
-		            properties = calculateServiceProperties();
-			        m_registration.setProperties(properties);
-		        }
-			}
-		});
-	    return this;
-	}
-	
-	public Component setCallbacks(String init, String start, String stop, String destroy) {
-	    ensureNotActive();
-	    m_callbackInit = init;
-	    m_callbackStart = start;
-	    m_callbackStop = stop;
-	    m_callbackDestroy = destroy;
-	    return this;
-	}
-	
-    public Component setCallbacks(Object instance, String init, String start, String stop, String destroy) {
-	    ensureNotActive();
-        m_callbackInstance = instance;
-        m_callbackInit = init;
-        m_callbackStart = start;
-        m_callbackStop = stop;
-        m_callbackDestroy = destroy;
-        return this;
-    }
-
-	@Override
-	public Component setFactory(Object factory, String createMethod) {
-	    ensureNotActive();
-		m_instanceFactory = factory;
-		m_instanceFactoryCreateMethod = createMethod;
-		return this;
-	}
-
-	@Override
-	public Component setFactory(String createMethod) {
-		return setFactory(null, createMethod);
-	}
-
-	@Override
-	public Component setComposition(Object instance, String getMethod) {
-	    ensureNotActive();
-		m_compositionManager = instance;
-		m_compositionManagerGetMethod = getMethod;
-		return this;
-	}
-
-	@Override
-	public Component setComposition(String getMethod) {
-		return setComposition(null, getMethod);
-	}
-
 	private Object[] getCompositionInstances() {
         Object[] instances = null;
         if (m_compositionManagerGetMethod != null) {
@@ -1157,71 +1541,6 @@ public class ComponentImpl implements Co
         return instances;
 	}
 
-	@Override
-	public DependencyManager getDependencyManager() {
-        return m_manager;
-	}
-	
-    public ComponentDependencyDeclaration[] getComponentDependencies() {
-        List<DependencyContext> deps = getDependencies();
-        if (deps != null) {
-            ComponentDependencyDeclaration[] result = new ComponentDependencyDeclaration[deps.size()];
-            for (int i = 0; i < result.length; i++) {
-                DependencyContext dep = (DependencyContext) deps.get(i);
-                if (dep instanceof ComponentDependencyDeclaration) {
-                    result[i] = (ComponentDependencyDeclaration) 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() {
-        StringBuffer sb = new StringBuffer();
-        Object serviceName = m_serviceName;
-        if (serviceName instanceof String[]) {
-            String[] names = (String[]) serviceName;
-            for (int i = 0; i < names.length; i++) {
-                if (i > 0) {
-                    sb.append(", ");
-                }
-                sb.append(names[i]);
-            }
-            appendProperties(sb);
-        } else if (serviceName instanceof String) {
-            sb.append(serviceName.toString());
-            appendProperties(sb);
-        } else {
-            Object implementation = m_componentDefinition;
-            if (implementation != null) {
-                if (implementation instanceof Class) {
-                    sb.append(((Class<?>) implementation).getName());
-                } else {
-                    // If the implementation instance does not override "toString", just display
-                    // the class name, else display the component using its toString method
-                    try {
-                	Method m = implementation.getClass().getMethod("toString", new Class[0]);
-                        if (m.getDeclaringClass().equals(Object.class)) {
-                            sb.append(implementation.getClass().getName());
-                        } else {
-                            sb.append(implementation.toString());
-                        }
-                    }  catch (java.lang.NoSuchMethodException e) {
-                        // Just display the class name
-                        sb.append(implementation.getClass().getName());
-                    }
-                }
-            } else {
-                sb.append(super.toString());
-            }
-        }
-        return sb.toString();
-    }
-    
     private void appendProperties(StringBuffer result) {
         Dictionary<Object, Object> properties = calculateServiceProperties();
         if (properties != null) {
@@ -1254,91 +1573,6 @@ public class ComponentImpl implements Co
         }
     }
     
-    @Override
-    public BundleContext getBundleContext() {
-        return m_context;
-    }
-    
-    @Override
-    public Bundle getBundle() {
-        return m_bundle;
-    }
-
-    public long getId() {
-        return m_id;
-    }
-    
-    public String getClassName() {
-        Object serviceInstance = m_componentInstance;
-        if (serviceInstance != null) {
-            return serviceInstance.getClass().getName();
-        } 
-        
-        Object implementation = m_componentDefinition;
-        if (implementation != null) {
-            if (implementation instanceof Class) {
-                return ((Class<?>) implementation).getName();
-            }
-            return implementation.getClass().getName();
-        } 
-        
-        Object instanceFactory = m_instanceFactory;
-        if (instanceFactory != null) {
-            return instanceFactory.getClass().getName();
-        } else {
-            // unexpected.
-            return ComponentImpl.class.getName();
-        }
-    }
-    
-    public String[] getServices() {
-        if (m_serviceName instanceof String[]) {
-            return (String[]) m_serviceName;
-        } else if (m_serviceName instanceof String) {
-            return new String[] { (String) m_serviceName };
-        } else {
-            return null;
-        }
-    }
-    
-    public int getState() {
-        return (isAvailable() ? ComponentDeclaration.STATE_REGISTERED : ComponentDeclaration.STATE_UNREGISTERED);
-    }
-
-    public void ensureNotActive() {
-        if (m_active.get()) {
-            throw new IllegalStateException("Can't modify an already started component.");
-        }
-    }
-    
-    public ComponentDeclaration getComponentDeclaration() {
-        return this;
-    }
-    
-    @Override
-    public String toString() {
-    	if (m_logger.getDebugKey() != null) {
-    		return m_logger.getDebugKey();
-    	}
-    	return getClassName();
-    }
-    
-    @Override
-    public void setThreadPool(Executor threadPool) {
-        ensureNotActive();
-        m_executor = new DispatchExecutor(threadPool, m_logger);
-    }
-    
-    @Override
-    public Logger getLogger() {
-        return m_logger;
-    }
-
-    @Override
-    public Map<String, Long> getCallbacksTime() {
-        return m_stopwatch;
-    }
-    
     private Executor getExecutor() {
         return m_executor;
     }

Modified: felix/trunk/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ComponentScheduler.java
URL: http://svn.apache.org/viewvc/felix/trunk/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ComponentScheduler.java?rev=1683504&r1=1683503&r2=1683504&view=diff
==============================================================================
--- felix/trunk/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ComponentScheduler.java (original)
+++ felix/trunk/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ComponentScheduler.java Thu Jun  4 10:15:35 2015
@@ -114,7 +114,7 @@ public class ComponentScheduler {
             // But if the "parallel" system property is specified, the component will use the threadpool only if it's
             // classname is starting with one of the prefixes specified in the property.
             if (parallel == null || requiresThreadPool(c, parallel)) {
-                ((ComponentContext) c).setThreadPool(execFactory.getExecutorFor(c));
+		createComponentExecutor(execFactory, c);
             }
             return true; // start the component now, possibly using the threadpool (see above).
         }

Modified: felix/trunk/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ConfigurationDependencyImpl.java
URL: http://svn.apache.org/viewvc/felix/trunk/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ConfigurationDependencyImpl.java?rev=1683504&r1=1683503&r2=1683504&view=diff
==============================================================================
--- felix/trunk/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ConfigurationDependencyImpl.java (original)
+++ felix/trunk/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ConfigurationDependencyImpl.java Thu Jun  4 10:15:35 2015
@@ -196,15 +196,13 @@ public class ConfigurationDependencyImpl
         // If this is initial settings, or a configuration update, we handle it synchronously.
         // We'll conclude that the dependency is available only if invoking updated did not cause
         // any ConfigurationException.
-        if (settings != null) {
-            Object[] instances = m_component.getInstances();
-            if (instances != null) {
-                try {
-                    invokeUpdated(settings);
-                } catch (ConfigurationException e) {
-                    logConfigurationException(e);
-                    throw e;
-                }
+        Object[] instances = m_component.getInstances();
+        if (instances != null) {
+            try {
+                invokeUpdated(settings);
+            } catch (ConfigurationException e) {
+                logConfigurationException(e);
+                throw e;
             }
         }
         
@@ -244,15 +242,8 @@ public class ConfigurationDependencyImpl
             break;
         case REMOVED:
             // The state machine is stopping us. We have to invoke updated(null).
-            try {
-                m_updateInvokedCache.set(false);
-                invokeUpdated(null);
-            } catch (ConfigurationException e) {
-                logConfigurationException(e);
-            } finally {
-                // Reset for the next time the state machine calls invokeAdd
-                m_updateInvokedCache.set(false);
-            }
+            // Reset for the next time the state machine calls invokeAdd
+            m_updateInvokedCache.set(false);
             break;
         default:
             break;

Modified: felix/trunk/dependencymanager/org.apache.felix.dependencymanager/test/test/ServiceRaceTest.java
URL: http://svn.apache.org/viewvc/felix/trunk/dependencymanager/org.apache.felix.dependencymanager/test/test/ServiceRaceTest.java?rev=1683504&r1=1683503&r2=1683504&view=diff
==============================================================================
--- felix/trunk/dependencymanager/org.apache.felix.dependencymanager/test/test/ServiceRaceTest.java (original)
+++ felix/trunk/dependencymanager/org.apache.felix.dependencymanager/test/test/ServiceRaceTest.java Thu Jun  4 10:15:35 2015
@@ -178,6 +178,7 @@ public class ServiceRaceTest extends Tes
         m_threadpool.execute(new Runnable() {
             public void run() {
                 try {
+                    // simulate a configuration suppression.
                     confDependency.updated(null);
                 }
                 catch (ConfigurationException e) {
@@ -189,11 +190,9 @@ public class ServiceRaceTest extends Tes
         // Ensure that client has been stopped, then destroyed, then unbound from all dependencies
         expectedStep += 2; // stop/destroy
         expectedStep += DEPENDENCIES; // removed all dependencies
-        expectedStep += 1; // removed configuration
         step.waitForStep(expectedStep, STEP_WAIT);
         step.ensure();
         Assert.assertEquals(0, theClient.getDependencies());
-        Assert.assertNull(theClient.getConfiguration());
 
         debug("finished one test loop");
         if ((loop + 1) % 100 == 0) {
@@ -213,12 +212,11 @@ public class ServiceRaceTest extends Tes
         }
 
         public void updated(Dictionary conf) throws ConfigurationException {
-            m_conf = conf;
             if (conf != null) {
+                Assert.assertNotNull(conf);
                 Assert.assertEquals("bar", conf.get("foo"));
+                m_conf = conf;
                 m_step.step(1);
-            } else {
-                m_step.step();
             }
         }