FELIX-3910: Refactored public methods with the BlockingSerialExecutor class. Made protected the getService method.
Moved all public methods at the top of the class.


git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1535808 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/ServiceDependencyImpl.java b/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/ServiceDependencyImpl.java
index 084fe58..5c72943 100644
--- a/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/ServiceDependencyImpl.java
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/ServiceDependencyImpl.java
@@ -28,7 +28,6 @@
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
-import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
@@ -42,6 +41,7 @@
 import org.apache.felix.dm.InvocationUtil;
 import org.apache.felix.dm.ServiceDependency;
 import org.apache.felix.dm.ServiceUtil;
+import org.apache.felix.dm.impl.BlockingSerialExecutor;
 import org.apache.felix.dm.impl.DefaultNullObject;
 import org.apache.felix.dm.impl.Logger;
 import org.apache.felix.dm.tracker.ServiceTracker;
@@ -57,44 +57,42 @@
  * 
  * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
  */
-public class ServiceDependencyImpl extends DependencyBase implements ServiceDependency, ServiceTrackerCustomizer, ComponentDependencyDeclaration {
-    protected List m_services = new ArrayList();
+public class ServiceDependencyImpl extends DependencyBase implements ServiceDependency, ServiceTrackerCustomizer,
+        ComponentDependencyDeclaration {
+    protected final List m_services = new ArrayList();
     protected volatile ServiceTracker m_tracker;
-    protected BundleContext m_context;
+    protected final BundleContext m_context;
     protected volatile Class m_trackedServiceName;
-    private Object m_nullObject;
+    private volatile Object m_nullObject;
     private volatile String m_trackedServiceFilter;
     private volatile String m_trackedServiceFilterUnmodified;
     private volatile ServiceReference m_trackedServiceReference;
-    private Object m_callbackInstance;
-    private String m_callbackAdded;
-    private String m_callbackChanged;
-    private String m_callbackRemoved;
-    private String m_callbackSwapped;
+    private volatile Object m_callbackInstance;
+    private volatile String m_callbackAdded;
+    private volatile String m_callbackChanged;
+    private volatile String m_callbackRemoved;
+    private volatile String m_callbackSwapped;
     private boolean m_autoConfig;
-    protected ServiceReference m_reference;
-    protected Object m_serviceInstance;
-    private String m_autoConfigInstance;
+    private volatile String m_autoConfigInstance;
     private boolean m_autoConfigInvoked;
-    private Object m_defaultImplementation;
-    private Object m_defaultImplementationInstance;
+    private volatile Object m_defaultImplementation;
+    private volatile Object m_defaultImplementationInstance;
     private boolean m_isAvailable;
-    private boolean m_propagate;
-    private Object m_propagateCallbackInstance;
-    private String m_propagateCallbackMethod;
+    private volatile boolean m_propagate;
+    private volatile Object m_propagateCallbackInstance;
+    private volatile String m_propagateCallbackMethod;
     private final Map m_sr = new HashMap(); /* <DependencyService, Set<Tuple<ServiceReference, Object>> */
-	private Map m_componentByRank = new HashMap(); /* <Component, Map<Long, Map<Integer, Tuple>>> */
-	private boolean m_debug = false;
-	private String m_debugKey = null;
-	
-	private final LinkedList m_injectionQueue = new LinkedList();
-	
-	public ServiceDependency setDebug(String identifier) {
-		this.m_debug = true;
-		this.m_debugKey = identifier;
-		return this;
-	}
-    
+    private final Map m_componentByRank = new HashMap(); /* <Component, Map<Long, Map<Integer, Tuple>>> */
+    private volatile boolean m_debug = false;
+    private volatile String m_debugKey = null;
+
+    /**
+     * Executor used to ensure proper synchronization without holding locks. 
+     */
+    private final BlockingSerialExecutor _serial = new BlockingSerialExecutor();
+
+    // ----------------------- Inner classes --------------------------------------------------------------
+
     private static final Comparator COMPARATOR = new Comparator() {
         public int getRank(ServiceReference ref) {
             Object ranking = ref.getProperty(Constants.SERVICE_RANKING);
@@ -110,40 +108,39 @@
             int rankb = getRank(rb);
             if (ranka < rankb) {
                 return -1;
-            }
-            else if (ranka > rankb) {
+            } else if (ranka > rankb) {
                 return 1;
             }
             return 0;
         }
     };
-    
-    private static final class Tuple /* <ServiceReference, Object> */ {
+
+    private static final class Tuple /* <ServiceReference, Object> */{
         private final ServiceReference m_serviceReference;
         private final Object m_service;
-        
+
         public Tuple(ServiceReference first, Object last) {
             m_serviceReference = first;
             m_service = last;
         }
-        
+
         public ServiceReference getServiceReference() {
             return m_serviceReference;
         }
-        
+
         public Object getService() {
             return m_service;
         }
-        
+
         public boolean equals(Object obj) {
             return ((Tuple) obj).getServiceReference().equals(getServiceReference());
         }
-        
+
         public int hashCode() {
             return m_serviceReference.hashCode();
         }
     }
-    
+
     /**
      * Entry to wrap service properties behind a Map.
      */
@@ -218,7 +215,9 @@
             return set;
         }
     }
-        
+
+    // ----------------------- Public methods -----------------------------------------------------------
+
     /**
      * Creates a new service dependency.
      * 
@@ -230,7 +229,7 @@
         m_context = context;
         m_autoConfig = true;
     }
-    
+
     /** Copying constructor that clones an existing instance. */
     public ServiceDependencyImpl(ServiceDependencyImpl prototype) {
         super(prototype);
@@ -251,210 +250,50 @@
             m_defaultImplementation = prototype.m_defaultImplementation;
         }
     }
-    
+
+    //@Override
+    public ServiceDependency setDebug(String identifier) {
+        this.m_debug = true;
+        this.m_debugKey = identifier;
+        return this;
+    }
+
+    //@Override
     public Dependency createCopy() {
         return new ServiceDependencyImpl(this);
     }
-    
+
+    //@Override
     public synchronized boolean isAutoConfig() {
         return m_autoConfig;
     }
-    
+
+    //@Override
     public synchronized boolean isAvailable() {
         return m_isAvailable;
     }
 
-    public synchronized Object getService() {
-        Object service = null;
-        if (m_isStarted) {
-            service = m_tracker.getService();
-        }
-        if (service == null && isAutoConfig()) {
-            service = getDefaultImplementation();
-            if (service == null) {
-                service = getNullObject();
+    //@Override
+    public void start(final DependencyService service) {
+        _serial.execute(new Runnable() {
+            public void run() {
+                // this code is executed exclusively and without holding any locks
+                _start(service);
             }
-        }
-        return service;
+        });
     }
 
-    public Object lookupService() {
-        Object service = null;
-        if (m_isStarted) {
-            service = getService();
-        }
-        else {
-            ServiceReference[] refs = null;
-            ServiceReference ref = null;
-            if (m_trackedServiceName != null) {
-                if (m_trackedServiceFilter != null) {
-                    try {
-                        refs = m_context.getServiceReferences(m_trackedServiceName.getName(), m_trackedServiceFilter);
-                        if (refs != null) {
-                            Arrays.sort(refs, COMPARATOR);
-                            ref = refs[0];
-                        }
-                    }
-                    catch (InvalidSyntaxException e) {
-                        throw new IllegalStateException("Invalid filter definition for dependency.");
-                    }
-                }
-                else if (m_trackedServiceReference != null) {
-                    ref = m_trackedServiceReference;
-                }
-                else {
-                    ref = m_context.getServiceReference(m_trackedServiceName.getName());
-                }
-                if (ref != null) {
-                    service = m_context.getService(ref);
-                }
+    //@Override
+    public void stop(final DependencyService service) {
+        _serial.execute(new Runnable() {
+            public void run() {
+                // this code is executed exclusively and without holding any locks
+                _stop(service);
             }
-            else {
-                throw new IllegalStateException("Could not lookup dependency, no service name specified.");
-            }
-        }
-        if (service == null && isAutoConfig()) {
-            service = getDefaultImplementation();
-            if (service == null) {
-                service = getNullObject();
-            }
-        }
-        return service;
+        });
     }
 
-    // TODO lots of duplication in lookupService()
-    public ServiceReference lookupServiceReference() {
-        ServiceReference service = null;
-        if (m_isStarted) {
-            service = m_tracker.getServiceReference();
-        }
-        else {
-            ServiceReference[] refs = null;
-            ServiceReference ref = null;
-            if (m_trackedServiceName != null) {
-                if (m_trackedServiceFilter != null) {
-                    try {
-                        refs = m_context.getServiceReferences(m_trackedServiceName.getName(), m_trackedServiceFilter);
-                        if (refs != null) {
-                            Arrays.sort(refs, COMPARATOR);
-                            ref = refs[0];
-                        }
-                    }
-                    catch (InvalidSyntaxException e) {
-                        throw new IllegalStateException("Invalid filter definition for dependency.");
-                    }
-                }
-                else if (m_trackedServiceReference != null) {
-                    ref = m_trackedServiceReference;
-                }
-                else {
-                    ref = m_context.getServiceReference(m_trackedServiceName.getName());
-                }
-                if (ref != null) {
-                    service = ref;
-                }
-            }
-            else {
-                throw new IllegalStateException("Could not lookup dependency, no service name specified.");
-            }
-        }
-        return service;
-    }
-
-    private Object getNullObject() {
-        if (m_nullObject == null) {
-            Class trackedServiceName;
-            synchronized (this) {
-                trackedServiceName = m_trackedServiceName;
-            }
-            try {
-                m_nullObject = Proxy.newProxyInstance(trackedServiceName.getClassLoader(), new Class[] {trackedServiceName}, new DefaultNullObject()); 
-            }
-            catch (Exception e) {
-                m_logger.log(Logger.LOG_ERROR, "Could not create null object for " + trackedServiceName + ".", e);
-            }
-        }
-        return m_nullObject;
-    }
-    
-    private Object getDefaultImplementation() {
-        if (m_defaultImplementation != null) {
-            if (m_defaultImplementation instanceof Class) {
-                try {
-                    m_defaultImplementationInstance = ((Class) m_defaultImplementation).newInstance();
-                }
-                catch (Exception e) {
-                    m_logger.log(Logger.LOG_ERROR, "Could not create default implementation instance of class " + m_defaultImplementation + ".", e);
-                }
-            }
-            else {
-                m_defaultImplementationInstance = m_defaultImplementation;
-            }
-        }
-        return m_defaultImplementationInstance;
-    }
-    
-    public synchronized Class getInterface() {
-        return m_trackedServiceName;
-    }
-
-    public void start(DependencyService service) {
-        boolean needsStarting = false;
-        synchronized (this) {
-            m_services.add(service);
-            if (!m_isStarted) {
-                if (m_trackedServiceName != null) {
-                    if (m_trackedServiceFilter != null) {
-                        try {
-                            m_tracker = new ServiceTracker(m_context, m_context.createFilter(m_trackedServiceFilter), this);
-                        }
-                        catch (InvalidSyntaxException e) {
-                            throw new IllegalStateException("Invalid filter definition for dependency: " + m_trackedServiceFilter);
-                        }
-                    }
-                    else if (m_trackedServiceReference != null) {
-                        m_tracker = new ServiceTracker(m_context, m_trackedServiceReference, this);
-                    }
-                    else {
-                        m_tracker = new ServiceTracker(m_context, m_trackedServiceName.getName(), this);
-                    }
-                }
-                else {
-                    throw new IllegalStateException("Could not create tracker for dependency, no service name specified.");
-                }
-                m_isStarted = true;
-                needsStarting = true;
-            }
-        }
-        if (needsStarting) {
-        	// when the swapped callback is set, also track the aspects
-        	boolean trackAllServices = false;
-        	boolean trackAllAspects = false;
-        	if (m_callbackSwapped != null) {
-        		trackAllAspects = true;
-        	} 
-        	m_tracker.open(trackAllServices, trackAllAspects);
-        }
-    }
-
-    public void stop(DependencyService service) {
-        boolean needsStopping = false;
-        synchronized (this) {
-            if (m_services.size() == 1 && m_services.contains(service)) {
-                m_isStarted = false;
-                needsStopping = true;
-            }
-        }
-        if (needsStopping) {
-            m_tracker.close();
-            m_tracker = null;
-        }
-        //moved this down
-        synchronized (this) {
-            m_services.remove(service);
-        }
-    }
-
+    //@Override
     public Object addingService(ServiceReference ref) {
         Object service = m_context.getService(ref);
         // first check to make sure the service is actually an instance of our service
@@ -464,470 +303,133 @@
         return service;
     }
 
-    public void addedService(ServiceReference ref, Object service) {
-		debugLog("addedservice: " + ref);
-        boolean makeAvailable = makeAvailable();
-        
-        Object[] services;
-        synchronized (this) {
-            services = m_services.toArray();
+    //@Override
+    public void addedService(final ServiceReference ref, final Object service) {
+        _serial.execute(new Runnable() {
+            public void run() {
+                // this code is executed exclusively and without holding any locks
+                _addedService(ref, service);
+            }
+        });
+    }
+
+    //@Override
+    public void modifiedService(final ServiceReference ref, final Object service) {
+        _serial.execute(new Runnable() {
+            public void run() {
+                // this code is executed exclusively and without holding any locks
+                _modifiedService(ref, service);
+            }
+        });
+    }
+
+    //@Override
+    public void removedService(final ServiceReference ref, final Object service) {
+        _serial.execute(new Runnable() {
+            public void run() {
+                // this code is executed exclusively and without holding any locks
+                _removedService(ref, service);
+            }
+        });
+    }
+
+    //@Override
+    public void invokeAdded(final DependencyService service) {
+        _serial.execute(new Runnable() {
+            public void run() {
+                // this code is executed exclusively and without holding any locks
+                _invokeAdded(service);
+            }
+        });
+    }
+
+    //@Override
+    public void invokeRemoved(final DependencyService service) {
+        _serial.execute(new Runnable() {
+            public void run() {
+                // this code is executed exclusively and without holding any locks
+                _invokeRemoved(service);
+            }
+        });
+    }
+
+    //@Override
+    public synchronized String toString() {
+        return "ServiceDependency[" + m_trackedServiceName + " " + m_trackedServiceFilterUnmodified + "]";
+    }
+
+    //@Override
+    public String getAutoConfigName() {
+        return m_autoConfigInstance;
+    }
+
+    //@Override
+    public Object getAutoConfigInstance() {
+        return lookupService();
+    }
+
+    //@Override
+    public Class getAutoConfigType() {
+        return getInterface();
+    }
+
+    //@Override
+    public String getName() {
+        StringBuilder sb = new StringBuilder();
+        if (m_trackedServiceName != null) {
+            sb.append(m_trackedServiceName.getName());
+            if (m_trackedServiceFilterUnmodified != null) {
+                sb.append(' ');
+                sb.append(m_trackedServiceFilterUnmodified);
+            }
         }
-        for (int i = 0; i < services.length; i++) {
-            DependencyService ds = (DependencyService) services[i];
-            if (makeAvailable) {
-                if (ds.isInstantiated() && isInstanceBound() && isRequired()) {
-            		debugLog("invoke added: " + ref);
-                    invokeAdded(ds, ref, service); //**
+        if (m_trackedServiceReference != null) {
+            sb.append("{service.id=" + m_trackedServiceReference.getProperty(Constants.SERVICE_ID) + "}");
+        }
+        return sb.toString();
+    }
+
+    //@Override
+    public String getType() {
+        return "service";
+    }
+
+    //@Override
+    public Dictionary getProperties() {
+        ServiceReference reference = lookupServiceReference();
+        Object service = lookupService();
+        if (reference != null) {
+            if (m_propagateCallbackInstance != null && m_propagateCallbackMethod != null) {
+                try {
+                    return (Dictionary) InvocationUtil.invokeCallbackMethod(m_propagateCallbackInstance, m_propagateCallbackMethod,
+                            new Class[][]{{ServiceReference.class, Object.class}, {ServiceReference.class}}, new Object[][]{
+                                    {reference, service}, {reference}});
+                } catch (InvocationTargetException e) {
+                    m_logger.log(LogService.LOG_WARNING, "Exception while invoking callback method", e.getCause());
+                } catch (Exception e) {
+                    m_logger.log(LogService.LOG_WARNING, "Exception while trying to invoke callback method", e);
                 }
-                // The dependency callback will be deferred until all required dependency are available.
-            	debugLog("dependency available: " + ref);
-                ds.dependencyAvailable(this); //**
-                if (!isRequired()) {
-                    // For optional dependency, we always invoke callback, because at this point, we know
-                    // that the service has been started, and the service start method has been called.
-                    // (See the ServiceImpl.bindService method, which will activate optional dependencies using 
-                    // startTrackingOptional() method).
-            		debugLog("invoke added: " + ref);
-                    invokeAdded(ds, ref, service); //**
+                throw new IllegalStateException("Could not invoke callback");
+            } else {
+                Properties props = new Properties();
+                String[] keys = reference.getPropertyKeys();
+                for (int i = 0; i < keys.length; i++) {
+                    if (!(keys[i].equals(Constants.SERVICE_ID) || keys[i].equals(Constants.SERVICE_PID))) {
+                        props.put(keys[i], reference.getProperty(keys[i]));
+                    }
                 }
+                return props;
             }
-            else {
-        		debugLog("dependency changed: " + ref);
-                ds.dependencyChanged(this); //**
-                // At this point, either the dependency is optional (meaning that the service has been started,
-                // because if not, then our dependency would not be active); or the dependency is required,
-                // meaning that either the service is not yet started, or already started.
-                // In all cases, we have to inject the required dependency.
-                
-                // we only try to invoke the method here if we are really already instantiated
-                if (ds.isInstantiated() && ds.getCompositionInstances().length > 0) {
-            		debugLog("invoke added: " + ref);
-                    invokeAdded(ds, ref, service); //**
-                }
-            }
+        } else {
+            throw new IllegalStateException("cannot find service reference");
         }
     }
 
-    public void modifiedService(ServiceReference ref, Object service) {
-        Object[] services;
-        synchronized (this) {
-            services = m_services.toArray();
-        }
-        for (int i = 0; i < services.length; i++) {
-            DependencyService ds = (DependencyService) services[i];
-            ds.dependencyChanged(this);
-            if (ds.isRegistered()) {
-                invokeChanged(ds, ref, service);
-            }
-        }
+    //@Override
+    public boolean isPropagated() {
+        return m_propagate;
     }
 
-    public void removedService(ServiceReference ref, Object service) {
-		debugLog("removedservice: " + ref + ", rank: " + ref.getProperty("service.ranking"));
-        boolean makeUnavailable = makeUnavailable();
-    	debugLog("make unavailable: " + makeUnavailable);
-        Object[] services;
-        synchronized (this) {
-            services = m_services.toArray();
-        }
-
-        for (int i = 0; i < services.length; i++) {
-            DependencyService ds = (DependencyService) services[i];
-            if (makeUnavailable) {
-                ds.dependencyUnavailable(this);
-                // when the dependency is optional or the dependency is instance bound and the component is instantiated (and the dependency is required)
-                // then remove is invoked. In other cases the removed has been when the component was unconfigured.
-                if (!isRequired() || (ds.isInstantiated() && isInstanceBound())) {
-                    invokeRemoved(ds, ref, service);
-                }
-            }
-            else {
-                ds.dependencyChanged(this);
-                invokeRemoved(ds, ref, service);
-            }
-        }
-        // unget what we got in addingService (see ServiceTracker 701.4.1)
-       	m_context.ungetService(ref);
-
-    }
-    
-    public void invokeAdded(DependencyService dependencyService, ServiceReference reference, Object service) {
-		debugLog("invoke added");
-        boolean added = false;
-        synchronized (m_sr) {
-            Set set = (Set) m_sr.get(dependencyService);
-            if (set == null) {
-                set = new HashSet();
-                m_sr.put(dependencyService, set);
-            }
-            added = set.add(new Tuple(reference, service));
-        }
-        if (added) { 
-        	// when a changed callback is specified we might not call the added callback just yet
-        	if (m_callbackSwapped != null) {
-        		handleAspectAwareAdded(dependencyService, reference, service);
-        	}
-        	else {
-        		invoke(dependencyService, reference, service, m_callbackAdded);
-        	}
-        }
-    }
-    
-    private synchronized void waitForCallbackLock(Runnable runnable) {
-    	while (m_injectionQueue.indexOf(runnable) != 0) {
-    		try {
-				wait();
-			} catch (InterruptedException e) {
-			}
-    	}
-    }
-    
-    private synchronized void enqueueCallback(Runnable runnable) {
-    	m_injectionQueue.addLast(runnable);
-    }
-    
-    private synchronized void releaseCallback(Runnable runnable) {
-    	m_injectionQueue.remove(runnable);
-    	notifyAll();
-    }
-    
-    private void handleAspectAwareAdded(final DependencyService dependencyService, final ServiceReference reference, final Object service) {
-		debugLog("aspectawareadded: " + reference.getProperty("service.ranking"));
-		if (componentIsDependencyManagerFactory(dependencyService)) {
-			// component is either aspect or adapter factory instance, these must be ignored.
-			return;
-		}
-		boolean invokeAdded = false;
-		boolean invokeSwapped = false;
-		Integer ranking = ServiceUtil.getRankingAsInteger(reference);
-		Tuple newHighestRankedService = null;
-		Tuple prevHighestRankedService = null;
-		Runnable callbackRunnable = null;
-		Map rankings = null;
-		synchronized (m_componentByRank) {
-			Long originalServiceId = ServiceUtil.getServiceIdAsLong(reference);
-			Map componentMap = (Map) m_componentByRank.get(dependencyService); /* <Long, Map<Integer, Tuple>> */
-			if (componentMap == null) {
-				// create new componentMap
-				componentMap = new HashMap(); /* <Long, Map<Integer, Tuple>> */
-				m_componentByRank.put(dependencyService, componentMap);
-			}
-			rankings = (Map) componentMap.get(originalServiceId); /* <Integer, Tuple> */
-			if (rankings == null) {
-				// new component added
-				rankings = new HashMap(); /* <Integer, Tuple> */
-				componentMap.put(originalServiceId, rankings);
-				rankings.put(ranking, new Tuple(reference, service));
-				invokeAdded = true;
-			} 
-			
-			if (!invokeAdded) {
-				// current highest ranked
-				prevHighestRankedService = (Tuple)getHighestRankedService(dependencyService, originalServiceId).getValue();
-				newHighestRankedService = swapHighestRankedService(dependencyService, originalServiceId, reference, service, ranking);
-				debugLog("prevhigh: " + prevHighestRankedService.getServiceReference().getProperty("service.ranking") + ", new high: " + newHighestRankedService.getServiceReference().getProperty("service.ranking"));
-				if (!prevHighestRankedService.getServiceReference().equals(newHighestRankedService.getServiceReference())) {
-					// new highest ranked service
-					debugLog("New highest ranked to swap to");
-					invokeSwapped = true;
-				} else {
-					debugLog("Ignoring lower ranked or irrelevant swap");
-				}
-			}
-			debugLog(m_componentByRank.toString());
-			
-			// up until this point should be synchronized on m_componentsByRank to keep integrity of the administration and consequences
-			// then the do phase comes, here we want to guarantee the effects of this operation are done like they were synchronized, however
-			// synchronization on m_componentsByRank to too course grained here, so we'd like to switch to synchronization on the
-			// original service id, therefore we're using our own guarded block to ensure the correct order.
-			
-			if (invokeAdded) {
-				debugLog("invoke added: " + reference.getProperty("service.ranking"));
-				callbackRunnable = createCallbackRunnable(dependencyService, reference, service, m_callbackAdded);
-				enqueueCallback(callbackRunnable);
-			} else if (invokeSwapped) {
-				debugLog("invoke swapped: " + newHighestRankedService.getServiceReference().getProperty("service.ranking") + " replacing " + prevHighestRankedService.getServiceReference().getProperty("service.ranking"));
-				callbackRunnable = createSwapRunnable(dependencyService, prevHighestRankedService.getServiceReference(), prevHighestRankedService.getService(), newHighestRankedService.getServiceReference(), newHighestRankedService.getService());
-				enqueueCallback(callbackRunnable);
-			}    	
-		}
-		if (callbackRunnable != null) {
-			waitForCallbackLock(callbackRunnable);
-			synchronized (rankings) {
-				releaseCallback(callbackRunnable);
-				execute(callbackRunnable);
-			}
-		}
-    }
-    
-    private Runnable createCallbackRunnable(final DependencyService dependencyService, final ServiceReference reference, final Object service, final String callback) {
-    	return new Runnable() {
-			public void run() {
-				invoke(dependencyService, reference, service, callback);			
-			}
-			public String toString() {
-				return callback + " on " + dependencyService;
-			}
-		};
-    }
-    
-    private Runnable createSwapRunnable(final DependencyService dependencyService, final ServiceReference prevReference, final Object prevService, final ServiceReference newReference, final Object newService) {
-    	return new Runnable() {
-			public void run() {
-				invokeSwappedCallback(dependencyService, prevReference, prevService, newReference, newService);					
-			}
-			public String toString() {
-				return "swap on " + dependencyService;
-			}
-		};
-    }
-    
-    private void execute(Runnable runnable) {
-    	runnable.run();
-    }
-    
-    private void debugLog(String message) {
-    	if (m_debug) {
-    		m_logger.log(Logger.LOG_DEBUG, "[" + m_debugKey + "] " + message);
-    	}
-    }
-    
-    private boolean componentIsDependencyManagerFactory(DependencyService dependencyService) {
-        Object component = dependencyService.getService();
-        if (component != null) {
-            String className = component.getClass().getName();
-            return className.startsWith("org.apache.felix.dm")
-                && !className.startsWith("org.apache.felix.dm.impl.AdapterServiceImpl$AdapterImpl")
-                && !className.startsWith("org.apache.felix.dm.test");
-        }
-        return false;
-    }
-    
-	private Tuple swapHighestRankedService(DependencyService dependencyService, Long serviceId, ServiceReference newReference, Object newService, Integer newRanking) {
-		// does a component with a higher ranking exist
-		synchronized (m_componentByRank) {
-			Map componentMap = (Map) m_componentByRank.get(dependencyService); /* <Long, Map<Integer, Tuple>> */
-			Map rankings = (Map) componentMap.get(serviceId); /* <Integer, Tuple> */
-			rankings.put(newRanking, new Tuple(newReference, newService));
-			Entry highestEntry = getHighestRankedService(dependencyService, serviceId); /* <Integer, Tuple> */
-			return (Tuple) highestEntry.getValue();
-		}
-	}
-	
-	private Entry getHighestRankedService(DependencyService dependencyService, Long serviceId) { /* <Integer, Tuple> */
-		Entry highestEntry = null; /* <Integer, Tuple> */
-		Map componentMap = (Map) m_componentByRank.get(dependencyService); /* <Long, Map<Integer, Tuple>> */
-		Map rankings = (Map) componentMap.get(serviceId); /* <Integer, Tuple> */
-		if (rankings != null) {
-			for (Iterator entryIterator = rankings.entrySet().iterator(); entryIterator.hasNext(); ) { /* <Integer, Tuple> */
-				Entry mapEntry = (Entry) entryIterator.next();
-				if (highestEntry == null) {
-					highestEntry = mapEntry;
-				} else {
-					if (((Integer)mapEntry.getKey()).intValue() > ((Integer)highestEntry.getKey()).intValue()) {
-						highestEntry = mapEntry;
-					}
-				}
-			}
-		}
-		return highestEntry;
-	}
-
-
-
-	private boolean isLastService(DependencyService dependencyService, ServiceReference reference, Object object, Long serviceId) {
-		// get the collection of rankings
-		Map componentMap = (Map) m_componentByRank.get(dependencyService); /* <Long, Map<Integer, Tuple>> */
-		
-		Map rankings = null; /* <Integer, Tuple> */
-		if (componentMap != null) {
-			rankings = (Map) componentMap.get(serviceId);
-		}
-		// if there is only one element left in the collection of rankings
-		// and this last element has the same ranking as the supplied service (in other words, it is the same)
-		// then this is the last service
-		// NOTE: it is possible that there is only one element, but that it's not equal to the supplied service,
-		// because an aspect on top of the original service is being removed (but the original service is still
-		// there). That in turn triggers:
-		// 1) a call to added(original-service)
-		// 2) that causes a swap
-		// 3) a call to removed(aspect-service) <-- that's what we're talking about
-		debugLog("last service: " + m_componentByRank.toString());
-		return (componentMap != null && rankings != null && rankings.size() == 1 && ((Entry)rankings.entrySet().iterator().next()).getKey()
-				.equals(ServiceUtil.getRankingAsInteger(reference)));
-	}
-	
-	
-    public void invokeChanged(DependencyService dependencyService, ServiceReference reference, Object service) {
-        invoke(dependencyService, reference, service, m_callbackChanged);
-    }
-
-    public void invokeRemoved(DependencyService dependencyService, ServiceReference reference, Object service) {
-		debugLog("invoke removed");
-        boolean removed = false;
-        synchronized (m_sr) {
-            Set set = (Set) m_sr.get(dependencyService);
-            removed = (set != null && set.remove(new Tuple(reference, service)));
-        }
-    	debugLog("removed: " + removed);
-        if (removed) {
-        	if (m_callbackSwapped != null) {
-        		handleAspectAwareRemoved(dependencyService, reference, service);
-        	}
-        	else {
-        		invoke(dependencyService, reference, service, m_callbackRemoved);
-        	}
-        }
-    }
-    
-	private void handleAspectAwareRemoved(DependencyService dependencyService, ServiceReference reference, Object service) {
-		debugLog("aspectawareremoved: " + reference.getProperty("service.ranking"));
-		if (componentIsDependencyManagerFactory(dependencyService)) {
-			// component is either aspect or adapter factory instance, these must be ignored.
-			return;
-		}
-		// we might need to swap here too!
-		boolean invokeRemoved = false;
-		Long serviceId = ServiceUtil.getServiceIdAsLong(reference);
-		Tuple prevHighestRankedService = null;
-		Tuple newHighestRankedService = null;
-		boolean invokeSwapped = false;
-		Map rankings = null;
-		Runnable callbackRunnable = null;
-		synchronized (m_componentByRank) {
-			Long originalServiceId = ServiceUtil.getServiceIdAsLong(reference);
-			if (isLastService(dependencyService, reference, service, serviceId)) {
-				invokeRemoved = true;
-			} else {
-				// not the last service, but should we swap?
-				prevHighestRankedService = (Tuple)getHighestRankedService(dependencyService, originalServiceId).getValue();
-				if (prevHighestRankedService.getServiceReference().equals(reference)) {
-					// swapping out
-					debugLog("Swap out on remove!");
-					invokeSwapped = true;
-				}
-			}
-			debugLog("is last service: " + invokeRemoved);
-			// cleanup
-			Map componentMap = (Map) m_componentByRank.get(dependencyService); /* <Long, Map<Integer, Tuple>> */
-			if (componentMap != null) {
-				rankings = (Map) componentMap.get(originalServiceId); /* <Integer, Tuple> */
-				List rankingsToRemove = new ArrayList();
-				for (Iterator entryIterator = rankings.entrySet().iterator(); entryIterator.hasNext(); ) {
-					Entry mapEntry = (Entry) entryIterator.next();
-					if (((Tuple)mapEntry.getValue()).getServiceReference().equals(reference)) {
-						// remove the reference
-						// rankings.remove(mapEntry.getKey());
-						rankingsToRemove.add(mapEntry.getKey());
-					}
-				}
-				for (Iterator rankingIterator = rankingsToRemove.iterator(); rankingIterator.hasNext(); ) {
-					rankings.remove(rankingIterator.next());
-				}
-				if (rankings.size() == 0) {
-					componentMap.remove(originalServiceId);
-				}
-				if (componentMap.size() == 0) {
-					m_componentByRank.remove(dependencyService);
-				}
-			}
-			// determine current highest ranked service
-			if (invokeSwapped) {
-				newHighestRankedService = (Tuple)getHighestRankedService(dependencyService, originalServiceId).getValue();
-			}
-			if (invokeRemoved) {
-				// handle invoke outside the sync block since we won't know what will happen there
-				debugLog("invoke removed: " + reference.getProperty("service.ranking"));
-				callbackRunnable = createCallbackRunnable(dependencyService, reference, service, m_callbackRemoved);
-				enqueueCallback(callbackRunnable);
-			} else if (invokeSwapped) {
-				debugLog("invoke swapped: " + newHighestRankedService.getServiceReference().getProperty("service.ranking") + " replacing " + prevHighestRankedService.getServiceReference().getProperty("service.ranking"));
-				callbackRunnable = createSwapRunnable(dependencyService, prevHighestRankedService.getServiceReference(), prevHighestRankedService.getService(), newHighestRankedService.getServiceReference(), newHighestRankedService.getService());
-				enqueueCallback(callbackRunnable);
-			}
-		}
-		if (callbackRunnable != null) {
-			waitForCallbackLock(callbackRunnable);
-			synchronized (rankings) {
-				releaseCallback(callbackRunnable);
-				execute(callbackRunnable);
-			}
-		}
-	}    
-
-    public void invoke(DependencyService dependencyService, ServiceReference reference, Object service, String name) {
-		debugLog("invoke: " + name);
-        if (name != null) {
-            dependencyService.invokeCallbackMethod(getCallbackInstances(dependencyService), name, 
-                new Class[][] {
-                    {Component.class, ServiceReference.class, m_trackedServiceName}, {Component.class, ServiceReference.class, Object.class}, {Component.class, ServiceReference.class}, {Component.class, m_trackedServiceName}, {Component.class, Object.class}, {Component.class}, {Component.class, Map.class, m_trackedServiceName},
-                    {ServiceReference.class, m_trackedServiceName}, {ServiceReference.class, Object.class}, {ServiceReference.class}, {m_trackedServiceName}, {Object.class}, {}, {Map.class, m_trackedServiceName}
-                },
-                new Object[][] {
-                    {dependencyService, reference, service}, {dependencyService, reference, service}, {dependencyService, reference}, {dependencyService, service}, {dependencyService, service}, {dependencyService}, {dependencyService, new ServicePropertiesMap(reference), service},
-                    {reference, service}, {reference, service}, {reference}, {service}, {service}, {}, {new ServicePropertiesMap(reference), service}
-                }    
-            );
-        }
-    }
-    
-	private void invokeSwappedCallback(DependencyService component, ServiceReference previousReference, Object previous, ServiceReference currentServiceReference,
-			Object current) {
-		// sanity check on the service references
-		Integer oldRank = (Integer) previousReference.getProperty(Constants.SERVICE_RANKING);
-		Integer newRank = (Integer) currentServiceReference.getProperty(Constants.SERVICE_RANKING);
-		
-		if (oldRank != null && newRank != null && oldRank.equals(newRank)) {
-			throw new IllegalStateException("Attempt to swap a service for a service with the same rank! previousReference: " + previousReference + ", currentReference: " + currentServiceReference);
-		}
-		
-		component.invokeCallbackMethod(getCallbackInstances(component), m_callbackSwapped, new Class[][] { 
-			{ m_trackedServiceName, m_trackedServiceName },
-			{ Object.class, Object.class }, 
-			{ ServiceReference.class, m_trackedServiceName, ServiceReference.class, m_trackedServiceName },
-			{ ServiceReference.class, Object.class, ServiceReference.class, Object.class }, 
-			{ Component.class, m_trackedServiceName, m_trackedServiceName },
-			{ Component.class, Object.class, Object.class }, 
-			{ Component.class, ServiceReference.class, m_trackedServiceName, ServiceReference.class, m_trackedServiceName },
-			{ Component.class, ServiceReference.class, Object.class, ServiceReference.class, Object.class } 
-		}, 
-		new Object[][] { 
-			{ previous, current },
-			{ previous, current }, 
-			{ previousReference, previous, currentServiceReference, current },
-			{ previousReference, previous, currentServiceReference, current },
-			{ component, previous, current },
-			{ component, previous, current }, 
-			{ component, previousReference, previous, currentServiceReference, current },
-			{ component, previousReference, previous, currentServiceReference, current } 					
-		});
-	}    
-
-    protected synchronized boolean makeAvailable() {
-        if (!isAvailable()) {
-            m_isAvailable = true;
-            return true;
-        }
-        return false;
-    }
-    
-    private synchronized boolean makeUnavailable() {
-        if ((isAvailable()) && (!m_tracker.hasReference())) {
-            m_isAvailable = false;
-            return true;
-        }
-        return false;
-    }
-    
-    private synchronized Object[] getCallbackInstances(DependencyService dependencyService) {
-        if (m_callbackInstance == null) {
-            return dependencyService.getCompositionInstances();
-        }
-        else {
-            return new Object[] { m_callbackInstance };
-        }
-    }
-    
     // ----- CREATION
 
     /**
@@ -936,11 +438,12 @@
      * @param serviceName the name of the service
      * @return this service dependency
      */
+    //@Override
     public synchronized ServiceDependency setService(Class serviceName) {
         setService(serviceName, null, null);
         return this;
     }
-    
+
     /**
      * Sets the name of the service that should be tracked. You can either specify
      * only the name, only the filter, or the name and a filter.
@@ -958,11 +461,12 @@
      * @param serviceFilter the filter condition
      * @return this service dependency
      */
+    //@Override
     public synchronized ServiceDependency setService(Class serviceName, String serviceFilter) {
         setService(serviceName, null, serviceFilter);
         return this;
     }
-    
+
     /**
      * Sets the name of the service that should be tracked. The name is assumed to be 
      * a service of type <code>Object</code> which means that, when auto configuration 
@@ -972,6 +476,7 @@
      * @param serviceFilter the filter condition
      * @return this service dependency
      */
+    //@Override
     public synchronized ServiceDependency setService(String serviceFilter) {
         if (serviceFilter == null) {
             throw new IllegalArgumentException("Service filter cannot be null.");
@@ -990,44 +495,12 @@
      * @param serviceReference the service reference to track
      * @return this service dependency
      */
+    //@Override
     public synchronized ServiceDependency setService(Class serviceName, ServiceReference serviceReference) {
         setService(serviceName, serviceReference, null);
         return this;
     }
 
-    /** Internal method to set the name, service reference and/or filter. */
-    private void setService(Class serviceName, ServiceReference serviceReference, String serviceFilter) {
-        ensureNotActive();
-        if (serviceName == null) {
-            m_trackedServiceName = Object.class;
-        }
-        else {
-            m_trackedServiceName = serviceName;
-        }
-        if (serviceFilter != null) {
-            m_trackedServiceFilterUnmodified = serviceFilter;
-            if (serviceName == null) {
-                m_trackedServiceFilter = serviceFilter;
-            }
-            else {
-                m_trackedServiceFilter ="(&(" + Constants.OBJECTCLASS + "=" + serviceName.getName() + ")" + serviceFilter + ")";
-            }
-        }
-        else {
-            m_trackedServiceFilterUnmodified = null;
-            m_trackedServiceFilter = null;
-        }
-        if (serviceReference != null) {
-            m_trackedServiceReference = serviceReference;
-            if (serviceFilter != null) {
-                throw new IllegalArgumentException("Cannot specify both a filter and a service reference.");
-            }
-        }
-        else {
-            m_trackedServiceReference = null;
-        }
-    }
-    
     /**
      * Sets the default implementation for this service dependency. You can use this to supply
      * your own implementation that will be used instead of a Null Object when the dependency is
@@ -1038,6 +511,7 @@
      *     instantiate this implementation
      * @return this service dependency
      */
+    //@Override
     public synchronized ServiceDependency setDefaultImplementation(Object implementation) {
         ensureNotActive();
         m_defaultImplementation = implementation;
@@ -1050,12 +524,14 @@
      * @param required the required flag
      * @return this service dependency
      */
+    //@Override
     public synchronized ServiceDependency setRequired(boolean required) {
         ensureNotActive();
         setIsRequired(required);
         return this;
     }
-    
+
+    //@Override
     public ServiceDependency setInstanceBound(boolean isInstanceBound) {
         setIsInstanceBound(isInstanceBound);
         return this;
@@ -1069,13 +545,14 @@
      * @param autoConfig the value of auto config
      * @return this service dependency
      */
+    //@Override
     public synchronized ServiceDependency setAutoConfig(boolean autoConfig) {
         ensureNotActive();
         m_autoConfig = autoConfig;
         m_autoConfigInvoked = true;
         return this;
     }
-    
+
     /**
      * Sets auto configuration for this service. Auto configuration allows the
      * dependency to fill in the attribute in the service implementation that
@@ -1084,6 +561,7 @@
      * @param instanceName the name of attribute to auto config
      * @return this service dependency
      */
+    //@Override
     public synchronized ServiceDependency setAutoConfig(String instanceName) {
         ensureNotActive();
         m_autoConfig = (instanceName != null);
@@ -1091,7 +569,7 @@
         m_autoConfigInvoked = true;
         return this;
     }
-    
+
     /**
      * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
      * dependency is added or removed. When you specify callbacks, the auto configuration 
@@ -1102,6 +580,7 @@
      * @param removed the method to call when a service was removed
      * @return this service dependency
      */
+    //@Override
     public synchronized ServiceDependency setCallbacks(String added, String removed) {
         return setCallbacks((Object) null, added, null, removed);
     }
@@ -1117,10 +596,11 @@
      * @param removed the method to call when a service was removed
      * @return this service dependency
      */
+    //@Override
     public synchronized ServiceDependency setCallbacks(String added, String changed, String removed) {
         return setCallbacks((Object) null, added, changed, removed);
     }
-    
+
     /**
      * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
      * dependency is added, changed or removed. When you specify callbacks, the auto 
@@ -1133,8 +613,9 @@
      * removal of an aspect
      * @return this service dependency
      */
+    //@Override
     public synchronized ServiceDependency setCallbacks(String added, String changed, String removed, String swapped) {
-    	return setCallbacks((Object) null, added, changed, removed, swapped);
+        return setCallbacks((Object) null, added, changed, removed, swapped);
     }
 
     /**
@@ -1148,10 +629,11 @@
      * @param removed the method to call when a service was removed
      * @return this service dependency
      */
+    //@Override
     public synchronized ServiceDependency setCallbacks(Object instance, String added, String removed) {
         return setCallbacks(instance, added, (String) null, removed);
     }
-    
+
     /**
      * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
      * dependency is added, changed or removed. They are called on the instance you provide. When you
@@ -1164,10 +646,11 @@
      * @param removed the method to call when a service was removed
      * @return this service dependency
      */
+    //@Override
     public synchronized ServiceDependency setCallbacks(Object instance, String added, String changed, String removed) {
-    	return setCallbacks(instance, added, changed, removed, null);
+        return setCallbacks(instance, added, changed, removed, null);
     }
-    
+
     /**
      * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
      * dependency is added, changed or removed. When you specify callbacks, the auto 
@@ -1180,65 +663,204 @@
      * @param swapped the method to call when the service was swapped due to addition or 
      * removal of an aspect
      * @return this service dependency
-     */    
+     */
+    //@Override
     public synchronized ServiceDependency setCallbacks(Object instance, String added, String changed, String removed, String swapped) {
         ensureNotActive();
         // if at least one valid callback is specified, we turn off auto configuration, unless
         // someone already explicitly invoked autoConfig
-        if ((added != null || removed != null || changed != null || swapped != null) && ! m_autoConfigInvoked) {
+        if ((added != null || removed != null || changed != null || swapped != null) && !m_autoConfigInvoked) {
             setAutoConfig(false);
         }
-    	m_callbackInstance = instance;
+        m_callbackInstance = instance;
         m_callbackAdded = added;
         m_callbackChanged = changed;
-        m_callbackRemoved = removed;    
+        m_callbackRemoved = removed;
         m_callbackSwapped = swapped;
-    	return this;
+        return this;
     }
-    
-    private void ensureNotActive() {
-        if (m_tracker != null) {
-            throw new IllegalStateException("Cannot modify state while active.");
+
+    //@Override
+    public ServiceDependency setPropagate(boolean propagate) {
+        ensureNotActive();
+        m_propagate = propagate;
+        return this;
+    }
+
+    //@Override
+    public ServiceDependency setPropagate(Object instance, String method) {
+        setPropagate(instance != null && method != null);
+        m_propagateCallbackInstance = instance;
+        m_propagateCallbackMethod = method;
+        return this;
+    }
+
+    // --------------------------------------- Protected methods -------------------------------------------------------------------
+
+    protected synchronized boolean makeAvailable() {
+        if (!isAvailable()) {
+            m_isAvailable = true;
+            return true;
         }
-    }
-    
-    public synchronized String toString() {
-        return "ServiceDependency[" + m_trackedServiceName + " " + m_trackedServiceFilterUnmodified + "]";
+        return false;
     }
 
-    public String getAutoConfigName() {
-        return m_autoConfigInstance;
-    }
-    
-    public Object getAutoConfigInstance() {
-        return lookupService();
-    }
-    
-    public Class getAutoConfigType() {
-        return getInterface();
-    }
-
-    public String getName() {
-        StringBuilder sb = new StringBuilder();
-        if (m_trackedServiceName != null) {
-            sb.append(m_trackedServiceName.getName());
-            if (m_trackedServiceFilterUnmodified != null) {
-                sb.append(' ');
-                sb.append(m_trackedServiceFilterUnmodified);
+    protected synchronized Object getService() {
+        Object service = null;
+        if (m_isStarted) {
+            service = m_tracker.getService();
+        }
+        if (service == null && isAutoConfig()) {
+            service = getDefaultImplementation();
+            if (service == null) {
+                service = getNullObject();
             }
         }
-        if (m_trackedServiceReference != null) {
-            sb.append("{service.id=" + m_trackedServiceReference.getProperty(Constants.SERVICE_ID)+"}");
+        return service;
+    }
+
+    // --------------------------------------- Private methods --------------------------------------------
+
+    private void _start(DependencyService service) {
+        boolean needsStarting = false;
+        synchronized (this) {
+            m_services.add(service);
+            if (!m_isStarted) {
+                if (m_trackedServiceName != null) {
+                    if (m_trackedServiceFilter != null) {
+                        try {
+                            m_tracker = new ServiceTracker(m_context, m_context.createFilter(m_trackedServiceFilter), this);
+                        } catch (InvalidSyntaxException e) {
+                            throw new IllegalStateException("Invalid filter definition for dependency: " + m_trackedServiceFilter);
+                        }
+                    } else if (m_trackedServiceReference != null) {
+                        m_tracker = new ServiceTracker(m_context, m_trackedServiceReference, this);
+                    } else {
+                        m_tracker = new ServiceTracker(m_context, m_trackedServiceName.getName(), this);
+                    }
+                } else {
+                    throw new IllegalStateException("Could not create tracker for dependency, no service name specified.");
+                }
+                m_isStarted = true;
+                needsStarting = true;
+            }
         }
-        return sb.toString();
+        if (needsStarting) {
+            // when the swapped callback is set, also track the aspects
+            boolean trackAllServices = false;
+            boolean trackAllAspects = false;
+            if (m_callbackSwapped != null) {
+                trackAllAspects = true;
+            }
+            m_tracker.open(trackAllServices, trackAllAspects);
+        }
     }
 
-    public String getType() {
-        return "service";
+    private void _stop(DependencyService service) {
+        boolean needsStopping = false;
+        synchronized (this) {
+            if (m_services.size() == 1 && m_services.contains(service)) {
+                m_isStarted = false;
+                needsStopping = true;
+            }
+        }
+        if (needsStopping) {
+            m_tracker.close();
+            m_tracker = null;
+        }
+        //moved this down
+        synchronized (this) {
+            m_services.remove(service);
+        }
     }
 
-    public void invokeAdded(DependencyService service) {
-		debugLog("invoke added due to configure. (component is activated)");
+    private void _addedService(ServiceReference ref, Object service) {
+        debugLog("addedservice: " + ref);
+        boolean makeAvailable = makeAvailable();
+
+        Object[] services;
+        synchronized (this) {
+            services = m_services.toArray();
+        }
+        for (int i = 0; i < services.length; i++) {
+            DependencyService ds = (DependencyService) services[i];
+            if (makeAvailable) {
+                if (ds.isInstantiated() && isInstanceBound() && isRequired()) {
+                    debugLog("invoke added: " + ref);              
+                    invokeAdded(ds, ref, service); //**
+                }
+                // The dependency callback will be deferred until all required dependency are available.
+                debugLog("dependency available: " + ref);
+                ds.dependencyAvailable(this); //**
+                if (!isRequired()) {
+                    // For optional dependency, we always invoke callback, because at this point, we know
+                    // that the service has been started, and the service start method has been called.
+                    // (See the ServiceImpl.bindService method, which will activate optional dependencies using 
+                    // startTrackingOptional() method).
+                    debugLog("invoke added: " + ref);
+                    invokeAdded(ds, ref, service); //**
+                }
+            } else {
+                debugLog("dependency changed: " + ref);
+                ds.dependencyChanged(this); //**
+                // At this point, either the dependency is optional (meaning that the service has been started,
+                // because if not, then our dependency would not be active); or the dependency is required,
+                // meaning that either the service is not yet started, or already started.
+                // In all cases, we have to inject the required dependency.
+
+                // we only try to invoke the method here if we are really already instantiated
+                if (ds.isInstantiated() && ds.getCompositionInstances().length > 0) {
+                    debugLog("invoke added: " + ref);
+                    invokeAdded(ds, ref, service); //**
+                }
+            }
+        }
+    }
+
+    private void _modifiedService(ServiceReference ref, Object service) {
+        Object[] services;
+        synchronized (this) {
+            services = m_services.toArray();
+        }
+        for (int i = 0; i < services.length; i++) {
+            DependencyService ds = (DependencyService) services[i];
+            ds.dependencyChanged(this);
+            if (ds.isRegistered()) {
+                invokeChanged(ds, ref, service);
+            }
+        }
+    }
+
+    private void _removedService(ServiceReference ref, Object service) {
+        debugLog("removedservice: " + ref + ", rank: " + ref.getProperty("service.ranking"));
+        boolean makeUnavailable = makeUnavailable();
+        debugLog("make unavailable: " + makeUnavailable);
+        Object[] services;
+        synchronized (this) {
+            services = m_services.toArray();
+        }
+
+        for (int i = 0; i < services.length; i++) {
+            DependencyService ds = (DependencyService) services[i];
+            if (makeUnavailable) {
+                ds.dependencyUnavailable(this);
+                // when the dependency is optional or the dependency is instance bound and the component is instantiated (and the dependency is required)
+                // then remove is invoked. In other cases the removed has been when the component was unconfigured.
+                if (!isRequired() || (ds.isInstantiated() && isInstanceBound())) {
+                    invokeRemoved(ds, ref, service);
+                }
+            } else {
+                ds.dependencyChanged(this);
+                invokeRemoved(ds, ref, service);
+            }
+        }
+        // unget what we got in addingService (see ServiceTracker 701.4.1)
+        m_context.ungetService(ref);
+
+    }
+
+    private void _invokeAdded(DependencyService service) {
+        debugLog("invoke added due to configure. (component is activated)");
         ServiceReference[] refs = m_tracker.getServiceReferences();
         if (refs != null) {
             for (int i = 0; i < refs.length; i++) {
@@ -1248,21 +870,21 @@
             }
         }
     }
-    
-    public void invokeRemoved(DependencyService service) {
-		debugLog("invoke removed due to unconfigure. (component is destroyed)");
+
+    private void _invokeRemoved(DependencyService service) {
+        debugLog("invoke removed due to unconfigure. (component is destroyed)");
         Set references = null;
         Object[] tupleArray = null;
         synchronized (m_sr) {
             references = (Set) m_sr.get(service);
             // is this null check necessary ??
             if (references != null) {
-	            tupleArray = references.toArray(new Tuple[references.size()]);
+                tupleArray = references.toArray(new Tuple[references.size()]);
             }
         }
 
-        Tuple[] refs = (Tuple[]) (tupleArray != null ?  tupleArray : new Tuple[0]);
-    
+        Tuple[] refs = (Tuple[]) (tupleArray != null ? tupleArray : new Tuple[0]);
+
         for (int i = 0; i < refs.length; i++) {
             ServiceReference sr = refs[i].getServiceReference();
             Object svc = refs[i].getService();
@@ -1270,53 +892,458 @@
         }
     }
 
-    public Dictionary getProperties() {
-        ServiceReference reference = lookupServiceReference();
-        Object service = lookupService();
-        if (reference != null) {
-            if (m_propagateCallbackInstance != null && m_propagateCallbackMethod != null) {
-                try {
-                    return (Dictionary) InvocationUtil.invokeCallbackMethod(m_propagateCallbackInstance, m_propagateCallbackMethod, new Class[][] {{ ServiceReference.class, Object.class }, { ServiceReference.class }}, new Object[][] {{ reference, service }, { reference }});
-                }
-                catch (InvocationTargetException e) {
-                    m_logger.log(LogService.LOG_WARNING, "Exception while invoking callback method", e.getCause());
-                }
-                catch (Exception e) {
-                    m_logger.log(LogService.LOG_WARNING, "Exception while trying to invoke callback method", e);
-                }
-                throw new IllegalStateException("Could not invoke callback");
-            }
-            else {
-                Properties props = new Properties();
-                String[] keys = reference.getPropertyKeys();
-                for (int i = 0; i < keys.length; i++) {
-                    if (!(keys[i].equals(Constants.SERVICE_ID) || keys[i].equals(Constants.SERVICE_PID))) {
-                        props.put(keys[i], reference.getProperty(keys[i]));
+    private Object lookupService() {
+        Object service = null;
+        if (m_isStarted) {
+            service = getService();
+        } else {
+            ServiceReference[] refs = null;
+            ServiceReference ref = null;
+            if (m_trackedServiceName != null) {
+                if (m_trackedServiceFilter != null) {
+                    try {
+                        refs = m_context.getServiceReferences(m_trackedServiceName.getName(), m_trackedServiceFilter);
+                        if (refs != null) {
+                            Arrays.sort(refs, COMPARATOR);
+                            ref = refs[0];
+                        }
+                    } catch (InvalidSyntaxException e) {
+                        throw new IllegalStateException("Invalid filter definition for dependency.");
                     }
+                } else if (m_trackedServiceReference != null) {
+                    ref = m_trackedServiceReference;
+                } else {
+                    ref = m_context.getServiceReference(m_trackedServiceName.getName());
                 }
-                return props;
+                if (ref != null) {
+                    service = m_context.getService(ref);
+                }
+            } else {
+                throw new IllegalStateException("Could not lookup dependency, no service name specified.");
             }
         }
-        else {
-            throw new IllegalStateException("cannot find service reference");
+        if (service == null && isAutoConfig()) {
+            service = getDefaultImplementation();
+            if (service == null) {
+                service = getNullObject();
+            }
+        }
+        return service;
+    }
+
+    private ServiceReference lookupServiceReference() {
+        // TODO lots of duplication in lookupService()
+        ServiceReference service = null;
+        if (m_isStarted) {
+            service = m_tracker.getServiceReference();
+        } else {
+            ServiceReference[] refs = null;
+            ServiceReference ref = null;
+            if (m_trackedServiceName != null) {
+                if (m_trackedServiceFilter != null) {
+                    try {
+                        refs = m_context.getServiceReferences(m_trackedServiceName.getName(), m_trackedServiceFilter);
+                        if (refs != null) {
+                            Arrays.sort(refs, COMPARATOR);
+                            ref = refs[0];
+                        }
+                    } catch (InvalidSyntaxException e) {
+                        throw new IllegalStateException("Invalid filter definition for dependency.");
+                    }
+                } else if (m_trackedServiceReference != null) {
+                    ref = m_trackedServiceReference;
+                } else {
+                    ref = m_context.getServiceReference(m_trackedServiceName.getName());
+                }
+                if (ref != null) {
+                    service = ref;
+                }
+            } else {
+                throw new IllegalStateException("Could not lookup dependency, no service name specified.");
+            }
+        }
+        return service;
+    }
+
+    private synchronized Class getInterface() {
+        return m_trackedServiceName;
+    }
+
+    private Object getNullObject() {
+        if (m_nullObject == null) {
+            Class trackedServiceName;
+            synchronized (this) {
+                trackedServiceName = m_trackedServiceName;
+            }
+            try {
+                m_nullObject = Proxy.newProxyInstance(trackedServiceName.getClassLoader(), new Class[]{trackedServiceName},
+                        new DefaultNullObject());
+            } catch (Exception e) {
+                m_logger.log(Logger.LOG_ERROR, "Could not create null object for " + trackedServiceName + ".", e);
+            }
+        }
+        return m_nullObject;
+    }
+
+    private Object getDefaultImplementation() {
+        if (m_defaultImplementation != null) {
+            if (m_defaultImplementation instanceof Class) {
+                try {
+                    m_defaultImplementationInstance = ((Class) m_defaultImplementation).newInstance();
+                } catch (Exception e) {
+                    m_logger.log(Logger.LOG_ERROR, "Could not create default implementation instance of class " + m_defaultImplementation
+                            + ".", e);
+                }
+            } else {
+                m_defaultImplementationInstance = m_defaultImplementation;
+            }
+        }
+        return m_defaultImplementationInstance;
+    }
+
+    private void invokeAdded(DependencyService dependencyService, ServiceReference reference, Object service) {
+        // We are already serialized.
+        debugLog("invoke added");
+        boolean added = false;
+        synchronized (m_sr) {
+            Set set = (Set) m_sr.get(dependencyService);
+            if (set == null) {
+                set = new HashSet();
+                m_sr.put(dependencyService, set);
+            }
+            added = set.add(new Tuple(reference, service));
+        }
+        if (added) {
+            // when a changed callback is specified we might not call the added callback just yet
+            if (m_callbackSwapped != null) {
+                handleAspectAwareAdded(dependencyService, reference, service);
+            } else {
+                invoke(dependencyService, reference, service, m_callbackAdded);
+            }
         }
     }
 
-    public boolean isPropagated() {
-        return m_propagate;
+    private void invokeChanged(DependencyService dependencyService, ServiceReference reference, Object service) {
+        invoke(dependencyService, reference, service, m_callbackChanged);
+    }
+
+    private void invokeRemoved(DependencyService dependencyService, ServiceReference reference, Object service) {
+        debugLog("invoke removed");
+        boolean removed = false;
+        synchronized (m_sr) {
+            Set set = (Set) m_sr.get(dependencyService);
+            removed = (set != null && set.remove(new Tuple(reference, service)));
+        }
+        debugLog("removed: " + removed);
+        if (removed) {
+            if (m_callbackSwapped != null) {
+                handleAspectAwareRemoved(dependencyService, reference, service);
+            } else {
+                invoke(dependencyService, reference, service, m_callbackRemoved);
+            }
+        }
+    }
+
+    private void invoke(DependencyService dependencyService, ServiceReference reference, Object service, String name) {
+        debugLog("invoke: " + name);
+        if (name != null) {
+            dependencyService.invokeCallbackMethod(getCallbackInstances(dependencyService), name, new Class[][]{
+                    {Component.class, ServiceReference.class, m_trackedServiceName},
+                    {Component.class, ServiceReference.class, Object.class}, {Component.class, ServiceReference.class},
+                    {Component.class, m_trackedServiceName}, {Component.class, Object.class}, {Component.class},
+                    {Component.class, Map.class, m_trackedServiceName}, {ServiceReference.class, m_trackedServiceName},
+                    {ServiceReference.class, Object.class}, {ServiceReference.class}, {m_trackedServiceName}, {Object.class}, {},
+                    {Map.class, m_trackedServiceName}}, new Object[][]{{dependencyService, reference, service},
+                    {dependencyService, reference, service}, {dependencyService, reference}, {dependencyService, service},
+                    {dependencyService, service}, {dependencyService}, {dependencyService, new ServicePropertiesMap(reference), service},
+                    {reference, service}, {reference, service}, {reference}, {service}, {service}, {},
+                    {new ServicePropertiesMap(reference), service}});
+        }
+    }
+
+    private void handleAspectAwareAdded(final DependencyService dependencyService, final ServiceReference reference, final Object service) {
+        // At this point, we are already serialized: no need to synchronized.
+        debugLog("aspectawareadded: " + reference.getProperty("service.ranking"));
+        if (componentIsDependencyManagerFactory(dependencyService)) {
+            // component is either aspect or adapter factory instance, these must be ignored.
+            return;
+        }
+        boolean invokeAdded = false;
+        boolean invokeSwapped = false;
+        Integer ranking = ServiceUtil.getRankingAsInteger(reference);
+        Tuple newHighestRankedService = null;
+        Tuple prevHighestRankedService = null;
+        Map rankings = null;
+        //synchronized (m_componentByRank) { // don't synchronize: we are serialized and we can now invoke callbacks
+        Long originalServiceId = ServiceUtil.getServiceIdAsLong(reference);
+        Map componentMap = (Map) m_componentByRank.get(dependencyService); /* <Long, Map<Integer, Tuple>> */
+        if (componentMap == null) {
+            // create new componentMap
+            componentMap = new HashMap(); /* <Long, Map<Integer, Tuple>> */
+            m_componentByRank.put(dependencyService, componentMap);
+        }
+        rankings = (Map) componentMap.get(originalServiceId); /* <Integer, Tuple> */
+        if (rankings == null) {
+            // new component added
+            rankings = new HashMap(); /* <Integer, Tuple> */
+            componentMap.put(originalServiceId, rankings);
+            rankings.put(ranking, new Tuple(reference, service));
+            invokeAdded = true;
+        }
+
+        if (!invokeAdded) {
+            // current highest ranked
+            prevHighestRankedService = (Tuple) getHighestRankedService(dependencyService, originalServiceId).getValue();
+            newHighestRankedService = swapHighestRankedService(dependencyService, originalServiceId, reference, service, ranking);
+            debugLog("prevhigh: " + prevHighestRankedService.getServiceReference().getProperty("service.ranking") + ", new high: " + 
+                     newHighestRankedService.getServiceReference().getProperty("service.ranking"));
+            if (!prevHighestRankedService.getServiceReference().equals(newHighestRankedService.getServiceReference())) {
+                // new highest ranked service
+                debugLog("New highest ranked to swap to");
+                invokeSwapped = true;
+            } else {
+                debugLog("Ignoring lower ranked or irrelevant swap");
+            }
+        }
+        debugLog(m_componentByRank.toString());
+        
+        // up until this point should be synchronized on m_componentsByRank to keep integrity of the administration and consequences
+        // then the do phase comes, here we want to guarantee the effects of this operation are done like they were synchronized, however
+        // synchronization on m_componentsByRank to too course grained here, so we'd like to switch to synchronization on the
+        // original service id, therefore we're using our own guarded block to ensure the correct order.
+
+        if (invokeAdded) {
+            debugLog("invoke added: " + reference.getProperty("service.ranking"));
+            // We can safely invoke callback since we are already serialized.
+            invoke(dependencyService, reference, service, m_callbackAdded);
+        } else if (invokeSwapped) {
+            debugLog("invoke swapped: " + newHighestRankedService.getServiceReference().getProperty("service.ranking") + " replacing " + 
+                     prevHighestRankedService.getServiceReference().getProperty("service.ranking"));            
+            // We can safely invoke callback since we are already serialized.
+            invokeSwappedCallback(dependencyService, prevHighestRankedService.getServiceReference(), prevHighestRankedService.getService(),
+                    newHighestRankedService.getServiceReference(), newHighestRankedService.getService());
+        }
+        //} 
+    }
+
+    private void log(String message) {
+        m_logger.log(Logger.LOG_DEBUG, message);
+    }
+
+    private void debugLog(String message) {
+        if (m_debug) {
+            m_logger.log(Logger.LOG_DEBUG, "[" + m_debugKey + "] " + message);
+        }
     }
     
-    public ServiceDependency setPropagate(boolean propagate) {
+    private boolean componentIsDependencyManagerFactory(DependencyService dependencyService) {
+        Object component = dependencyService.getService();
+        if (component != null) {
+            String className = component.getClass().getName();
+            return className.startsWith("org.apache.felix.dm")
+                    && !className.startsWith("org.apache.felix.dm.impl.AdapterServiceImpl$AdapterImpl")
+                    && !className.startsWith("org.apache.felix.dm.test");
+        }
+        return false;
+    }
+
+    private Tuple swapHighestRankedService(DependencyService dependencyService, Long serviceId, ServiceReference newReference,
+            Object newService, Integer newRanking) {
+        // does a component with a higher ranking exist
+        synchronized (m_componentByRank) {
+            Map componentMap = (Map) m_componentByRank.get(dependencyService); /* <Long, Map<Integer, Tuple>> */
+            Map rankings = (Map) componentMap.get(serviceId); /* <Integer, Tuple> */
+            rankings.put(newRanking, new Tuple(newReference, newService));
+            Entry highestEntry = getHighestRankedService(dependencyService, serviceId); /* <Integer, Tuple> */
+            return (Tuple) highestEntry.getValue();
+        }
+    }
+
+    private Entry getHighestRankedService(DependencyService dependencyService, Long serviceId) { /* <Integer, Tuple> */
+        Entry highestEntry = null; /* <Integer, Tuple> */
+        Map componentMap = (Map) m_componentByRank.get(dependencyService); /* <Long, Map<Integer, Tuple>> */
+        Map rankings = (Map) componentMap.get(serviceId); /* <Integer, Tuple> */
+        if (rankings != null) {
+            for (Iterator entryIterator = rankings.entrySet().iterator(); entryIterator.hasNext();) { /* <Integer, Tuple> */
+                Entry mapEntry = (Entry) entryIterator.next();
+                if (highestEntry == null) {
+                    highestEntry = mapEntry;
+                } else {
+                    if (((Integer) mapEntry.getKey()).intValue() > ((Integer) highestEntry.getKey()).intValue()) {
+                        highestEntry = mapEntry;
+                    }
+                }
+            }
+        }
+        return highestEntry;
+    }
+
+    private boolean isLastService(DependencyService dependencyService, ServiceReference reference, Object object, Long serviceId) {
+        // get the collection of rankings
+        Map componentMap = (Map) m_componentByRank.get(dependencyService); /* <Long, Map<Integer, Tuple>> */
+
+        Map rankings = null; /* <Integer, Tuple> */
+        if (componentMap != null) {
+            rankings = (Map) componentMap.get(serviceId);
+        }
+        // if there is only one element left in the collection of rankings
+        // and this last element has the same ranking as the supplied service (in other words, it is the same)
+        // then this is the last service
+        // NOTE: it is possible that there is only one element, but that it's not equal to the supplied service,
+        // because an aspect on top of the original service is being removed (but the original service is still
+        // there). That in turn triggers:
+        // 1) a call to added(original-service)
+        // 2) that causes a swap
+        // 3) a call to removed(aspect-service) <-- that's what we're talking about
+        debugLog("last service: " + m_componentByRank.toString());
+        return (componentMap != null && rankings != null && rankings.size() == 1 && ((Entry) rankings.entrySet().iterator().next())
+                .getKey().equals(ServiceUtil.getRankingAsInteger(reference)));
+    }
+
+    private void handleAspectAwareRemoved(DependencyService dependencyService, ServiceReference reference, Object service) {
+        debugLog("aspectawareremoved: " + reference.getProperty("service.ranking"));
+        if (componentIsDependencyManagerFactory(dependencyService)) {
+            // component is either aspect or adapter factory instance, these must be ignored.
+            return;
+        }
+        // we might need to swap here too!
+        boolean invokeRemoved = false;
+        Long serviceId = ServiceUtil.getServiceIdAsLong(reference);
+        Tuple prevHighestRankedService = null;
+        Tuple newHighestRankedService = null;
+        boolean invokeSwapped = false;
+        Map rankings = null;
+        // synchronized (m_componentByRank) { // don't synchronize: we are serialized and we invoke callbacks
+        Long originalServiceId = ServiceUtil.getServiceIdAsLong(reference);
+        if (isLastService(dependencyService, reference, service, serviceId)) {
+            invokeRemoved = true;
+        } else {
+            // not the last service, but should we swap?
+            prevHighestRankedService = (Tuple) getHighestRankedService(dependencyService, originalServiceId).getValue();
+            if (prevHighestRankedService.getServiceReference().equals(reference)) {
+                // swapping out
+                debugLog("Swap out on remove!");
+                invokeSwapped = true;
+            }
+        }
+        debugLog("is last service: " + invokeRemoved);
+        // cleanup
+        Map componentMap = (Map) m_componentByRank.get(dependencyService); /* <Long, Map<Integer, Tuple>> */
+        if (componentMap != null) {
+            rankings = (Map) componentMap.get(originalServiceId); /* <Integer, Tuple> */
+            List rankingsToRemove = new ArrayList();
+            for (Iterator entryIterator = rankings.entrySet().iterator(); entryIterator.hasNext();) {
+                Entry mapEntry = (Entry) entryIterator.next();
+                if (((Tuple) mapEntry.getValue()).getServiceReference().equals(reference)) {
+                    // remove the reference
+                    // rankings.remove(mapEntry.getKey());
+                    rankingsToRemove.add(mapEntry.getKey());
+                }
+            }
+            for (Iterator rankingIterator = rankingsToRemove.iterator(); rankingIterator.hasNext();) {
+                rankings.remove(rankingIterator.next());
+            }
+            if (rankings.size() == 0) {
+                componentMap.remove(originalServiceId);
+            }
+            if (componentMap.size() == 0) {
+                m_componentByRank.remove(dependencyService);
+            }
+        }
+        // determine current highest ranked service
+        if (invokeSwapped) {
+            newHighestRankedService = (Tuple) getHighestRankedService(dependencyService, originalServiceId).getValue();
+        }
+        if (invokeRemoved) {
+            // handle invoke outside the sync block since we won't know what will happen there
+            debugLog("invoke removed: " + reference.getProperty("service.ranking"));
+            // We can safely invoke callback, since we are already serialized
+            invoke(dependencyService, reference, service, m_callbackRemoved);
+        } else if (invokeSwapped) {
+            debugLog("invoke swapped: " + newHighestRankedService.getServiceReference().getProperty("service.ranking") + " replacing " + 
+                     prevHighestRankedService.getServiceReference().getProperty("service.ranking"));
+            // We can safely invoke callback, since we are already serialized
+            invokeSwappedCallback(dependencyService, prevHighestRankedService.getServiceReference(), prevHighestRankedService.getService(),
+                    newHighestRankedService.getServiceReference(), newHighestRankedService.getService());
+        }
+        //}
+    }
+
+    private void invokeSwappedCallback(DependencyService component, ServiceReference previousReference, Object previous,
+            ServiceReference currentServiceReference, Object current) {
+        // sanity check on the service references
+        Integer oldRank = (Integer) previousReference.getProperty(Constants.SERVICE_RANKING);
+        Integer newRank = (Integer) currentServiceReference.getProperty(Constants.SERVICE_RANKING);
+
+        if (oldRank != null && newRank != null && oldRank.equals(newRank)) {
+            throw new IllegalStateException("Attempt to swap a service for a service with the same rank! previousReference: "
+                    + previousReference + ", currentReference: " + currentServiceReference);
+        }
+
+        component.invokeCallbackMethod(getCallbackInstances(component), m_callbackSwapped, new Class[][]{
+                {m_trackedServiceName, m_trackedServiceName}, {Object.class, Object.class},
+                {ServiceReference.class, m_trackedServiceName, ServiceReference.class, m_trackedServiceName},
+                {ServiceReference.class, Object.class, ServiceReference.class, Object.class},
+                {Component.class, m_trackedServiceName, m_trackedServiceName}, {Component.class, Object.class, Object.class},
+                {Component.class, ServiceReference.class, m_trackedServiceName, ServiceReference.class, m_trackedServiceName},
+                {Component.class, ServiceReference.class, Object.class, ServiceReference.class, Object.class}}, new Object[][]{
+                {previous, current}, {previous, current}, {previousReference, previous, currentServiceReference, current},
+                {previousReference, previous, currentServiceReference, current}, {component, previous, current},
+                {component, previous, current}, {component, previousReference, previous, currentServiceReference, current},
+                {component, previousReference, previous, currentServiceReference, current}});
+    }
+
+    private synchronized boolean makeUnavailable() {
+        // TODO should we check also m_isStarted ?
+        if ((isAvailable()) && (m_isStarted == false || !m_tracker.hasReference())) {
+            m_isAvailable = false;
+            return true;
+        }
+        return false;
+    }
+
+    private synchronized Object[] getCallbackInstances(DependencyService dependencyService) {
+        if (m_callbackInstance == null) {
+            return dependencyService.getCompositionInstances();
+        } else {
+            return new Object[]{m_callbackInstance};
+        }
+    }
+
+    // ----- CREATION
+
+    /** Internal method to set the name, service reference and/or filter. */
+    private void setService(Class serviceName, ServiceReference serviceReference, String serviceFilter) {
         ensureNotActive();
-        m_propagate = propagate;
-        return this;
+        if (serviceName == null) {
+            m_trackedServiceName = Object.class;
+        } else {
+            m_trackedServiceName = serviceName;
+        }
+        if (serviceFilter != null) {
+            m_trackedServiceFilterUnmodified = serviceFilter;
+            if (serviceName == null) {
+                m_trackedServiceFilter = serviceFilter;
+            } else {
+                m_trackedServiceFilter = "(&(" + Constants.OBJECTCLASS + "=" + serviceName.getName() + ")" + serviceFilter + ")";
+            }
+        } else {
+            m_trackedServiceFilterUnmodified = null;
+            m_trackedServiceFilter = null;
+        }
+        if (serviceReference != null) {
+            m_trackedServiceReference = serviceReference;
+            if (serviceFilter != null) {
+                throw new IllegalArgumentException("Cannot specify both a filter and a service reference.");
+            }
+        } else {
+            m_trackedServiceReference = null;
+        }
     }
-    
-    public ServiceDependency setPropagate(Object instance, String method) {
-        setPropagate(instance != null && method != null);
-        m_propagateCallbackInstance = instance;
-        m_propagateCallbackMethod = method;
-        return this;
+
+    private void ensureNotActive() {
+        if (m_tracker != null) {
+            throw new IllegalStateException("Cannot modify state while active.");
+        }
     }
-   
 }