You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by ui...@apache.org on 2014/05/19 08:47:08 UTC

svn commit: r1595756 [2/2] - in /felix/sandbox/pderop/dependencymanager-prototype: dm.it/src/dm/it/ dm/src/dm/ dm/src/dm/impl/ dm/src/dm/impl/index/ dm/src/dm/impl/index/multiproperty/ dm/src/dm/impl/metatype/ dm/src/tracker/

Added: felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/AspectServiceImpl.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/AspectServiceImpl.java?rev=1595756&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/AspectServiceImpl.java (added)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/AspectServiceImpl.java Mon May 19 06:47:07 2014
@@ -0,0 +1,242 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package dm.impl;
+
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Properties;
+
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+
+import dm.Component;
+import dm.ComponentStateListener;
+import dm.Dependency;
+import dm.DependencyManager;
+import dm.ServiceDependency;
+import dm.context.DependencyContext;
+
+public class AspectServiceImpl extends FilterComponent {
+	
+	private final String m_add;
+	private final String m_change;
+	private final String m_remove;
+	private final String m_swap;
+	private int m_ranking;
+
+	public AspectServiceImpl(DependencyManager dm, Class<?> aspectInterface, String aspectFilter, int ranking, String autoConfig, String add, String change, String remove, String swap) {
+		super(dm.createComponent());
+		this.m_ranking = ranking;
+		this.m_add = add;
+		this.m_change = change;
+		this.m_remove = remove;
+		this.m_swap = swap;
+		
+		m_component.setImplementation(new AspectImpl(aspectInterface, autoConfig))
+			.add(dm.createServiceDependency()
+					.setService(aspectInterface, createDependencyFilterForAspect(aspectFilter))
+					.setAutoConfig(false)
+					.setCallbacks("added", "removed"))
+					.setCallbacks("init", null, "stop", null);
+		
+//		m_component.setDebug("aspectfactory-" + m_ranking);
+	}
+	
+	private String createDependencyFilterForAspect(String aspectFilter) {
+        // we only want to match services which are not themselves aspects
+        if (aspectFilter == null || aspectFilter.length() == 0) {
+            return "(!(" + DependencyManager.ASPECT + "=*))";
+        }
+        else {
+            return "(&(!(" + DependencyManager.ASPECT + "=*))" + aspectFilter + ")";
+        }  
+	}
+	
+    private Properties getServiceProperties(ServiceReference ref) {
+        Properties props = new Properties();
+        String[] keys = ref.getPropertyKeys();
+        for (int i = 0; i < keys.length; i++) {
+            String key = keys[i];
+            if (key.equals(Constants.SERVICE_ID) || key.equals(Constants.SERVICE_RANKING) || key.equals(DependencyManager.ASPECT) || key.equals(Constants.OBJECTCLASS)) {
+                // do not copy these
+            }
+            else {
+                props.put(key, ref.getProperty(key));
+            }
+        }
+        if (m_serviceProperties != null) {
+            Enumeration<?> e = m_serviceProperties.keys();
+            while (e.hasMoreElements()) {
+                Object key = e.nextElement();
+                props.put(key, m_serviceProperties.get(key));
+            }
+        }
+        // finally add our aspect property
+        props.put(DependencyManager.ASPECT, ref.getProperty(Constants.SERVICE_ID));
+        // and the ranking
+        props.put(Constants.SERVICE_RANKING, Integer.valueOf(m_ranking));
+        return props;
+    }
+	
+	class AspectImpl extends AbstractDecorator {
+
+		private final Class<?> m_aspectInterface;
+		private final String m_autoConfig;
+
+		public AspectImpl(Class<?> aspectInterface, String autoConfig) {
+			this.m_aspectInterface = aspectInterface;
+			this.m_autoConfig = autoConfig;
+		}
+
+        /**
+         * Creates an aspect implementation component for a new original service.
+         * @param param First entry contains the ref to the original service
+         */
+		@Override
+        public Component createService(Object[] params) {
+            // Get the new original service reference.
+            ServiceReference ref = (ServiceReference) params[0];
+            List<DependencyContext> dependencies = m_component.getDependencies();
+            // remove our internal dependency, replace it with one that points to the specific service that just was passed in.
+            dependencies.remove(0);
+            Properties serviceProperties = getServiceProperties(ref);
+            String[] serviceInterfaces = getServiceInterfaces();
+            
+            ServiceDependency aspectDependency = (ServiceDependencyImpl) 
+                    m_manager.createServiceDependency().setService(m_aspectInterface, createAspectFilter(ref)).setRequired(true);
+            aspectDependency.setDebug("aspect " + m_ranking);
+
+            aspectDependency.setCallbacks(new CallbackProxy(aspectDependency, ref), 
+                            m_add != null ? "addAspect" : null, 
+                            "changeAspect", // We have to propagate in case aspect does not have a change callback
+                            m_remove != null ? "removeAspect" : null, 
+                            m_swap != null ? "swapAspect" : null);
+            
+            if (m_autoConfig != null) {
+                aspectDependency.setAutoConfig(m_autoConfig);
+            } else if (m_add == null && m_change == null && m_remove == null && m_swap == null) {
+                // Since we have set callbacks, we must reactivate setAutoConfig because user has not specified any callbacks.
+                aspectDependency.setAutoConfig(true);
+            }
+            
+            Component service = m_manager.createComponent()
+                .setInterface(serviceInterfaces, serviceProperties)
+                .setImplementation(m_serviceImpl)
+                .setFactory(m_factory, m_factoryCreateMethod) // if not set, no effect
+                .setComposition(m_compositionInstance, m_compositionMethod) // if not set, no effect
+                .setCallbacks(m_callbackObject, m_init, m_start, m_stop, m_destroy) // if not set, no effect
+                .add(aspectDependency);
+            
+            service.setDebug("aspectimpl-" + m_ranking);
+            
+            configureAutoConfigState(service, m_component);
+            
+            for (DependencyContext dc : dependencies) {
+                service.add((Dependency) dc.createCopy());
+            }
+
+            for (int i = 0; i < m_stateListeners.size(); i++) {
+                service.add((ComponentStateListener) m_stateListeners.get(i));
+            }
+            return service;                
+        }
+		
+        private String[] getServiceInterfaces() {
+            List<String> serviceNames = new ArrayList<>();
+            // Of course, we provide the aspect interface.
+            serviceNames.add(m_aspectInterface.getName());
+            // But also append additional aspect implementation interfaces.
+            if (m_serviceInterfaces != null) {
+                for (int i = 0; i < m_serviceInterfaces.length; i ++) {
+                    if (!m_serviceInterfaces[i].equals(m_aspectInterface.getName())) {
+                        serviceNames.add(m_serviceInterfaces[i]);
+                    }
+                }
+            }
+            return (String[]) serviceNames.toArray(new String[serviceNames.size()]);
+        }
+        
+        private String createAspectFilter(ServiceReference ref) {
+            Long sid = (Long) ref.getProperty(Constants.SERVICE_ID);
+            return "(&(|(!(" + Constants.SERVICE_RANKING + "=*))(" + Constants.SERVICE_RANKING + "<=" + (m_ranking - 1) + "))(|(" + Constants.SERVICE_ID + "=" + sid + ")(" + DependencyManager.ASPECT + "=" + sid + ")))";
+        }
+		
+	}
+	
+    class CallbackProxy {
+        private final ServiceDependencyImpl m_aspectDependency;
+        private final ServiceReference m_originalServiceRef;
+
+        CallbackProxy(ServiceDependency aspectDependency, ServiceReference originalServiceRef) {
+            m_aspectDependency = (ServiceDependencyImpl) aspectDependency;
+            m_originalServiceRef = originalServiceRef;
+        }
+
+        @SuppressWarnings("unused")
+		private void addAspect(Component c, ServiceReference ref, Object service) {
+            // Just forward "add" service dependency callback.
+        	
+        	// Invoke is done on dependency.getInstances() which unfortunately returns this callback instance...
+        	ServiceEventImpl event = new ServiceEventImpl(ref, service);
+        	m_aspectDependency.invoke(m_add, event, m_aspectDependency.getComponentContext().getInstances());
+        }
+
+        @SuppressWarnings("unused")
+		private void changeAspect(Component c, ServiceReference ref, Object service) {
+            // Invoke "change" service dependency callback
+            if (m_change != null) {
+            	ServiceEventImpl event = new ServiceEventImpl(ref, service);
+                m_aspectDependency.invoke(m_change, event, m_aspectDependency.getComponentContext().getInstances());
+            }
+            // Propagate change to immediate higher aspect, or to client using our aspect.
+            // We always propagate our own properties, and the ones from the original service, but we don't inherit
+            // from lower ranked aspect service properties.
+            Dictionary<?,?> props = getServiceProperties(m_originalServiceRef);
+            c.setServiceProperties(props);
+        }
+
+        @SuppressWarnings("unused")
+		private void removeAspect(Component c, ServiceReference ref, Object service) {
+            // Just forward "remove" service dependency callback.
+        	ServiceEventImpl event = new ServiceEventImpl(ref, service);
+        	m_aspectDependency.invoke(m_remove, event, m_aspectDependency.getComponentContext().getInstances());
+        }
+
+        @SuppressWarnings("unused")
+		private void swapAspect(Component c, ServiceReference prevRef, Object prev, ServiceReference currRef,
+                                Object curr) {
+        	Object[] instances = m_aspectDependency.getComponentContext().getInstances();
+        	
+        	// TODO ASPECTS: It sometimes appears (mostly on component/dependency remove) the size of the instances array is 0, meaning the component
+        	// is no longer registered. This should not happen! Figure out why it happens anyway. 
+//        	System.out.println("[proxy] swapAspect..." + instances.length);
+        	
+            // Just forward "swap" service dependency callback.
+        	m_aspectDependency.invokeSwap(m_swap, prevRef, prev, currRef, curr, m_aspectDependency.getComponentContext().getInstances());
+        }
+        
+        @Override
+        public String toString() {
+        	return "CallbackProxy";
+        }
+    }
+
+}

Propchange: felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/AspectServiceImpl.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/ComponentImpl.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/ComponentImpl.java?rev=1595756&r1=1595755&r2=1595756&view=diff
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/ComponentImpl.java (original)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/ComponentImpl.java Mon May 19 06:47:07 2014
@@ -64,6 +64,15 @@ public class ComponentImpl implements Co
     private final long m_id;
     private static AtomicLong m_idGenerator = new AtomicLong();
     private final Map<DependencyContext, ConcurrentSkipListSet<Event>> m_dependencyEvents = new HashMap<>();
+    
+    private boolean debug = false;
+    private String debugKey;
+    
+    public void setDebug(String debugKey) {
+    	System.out.println("*" + debugKey + " set debug");
+    	this.debugKey = debugKey;
+    	this.debug = true;
+    }
 
     // configuration (static)
     private volatile String m_callbackInit;
@@ -80,6 +89,8 @@ public class ComponentImpl implements Co
 	private volatile Object m_compositionManager;
 	private volatile String m_compositionManagerGetMethod;
 	private volatile Object m_compositionManagerInstance;
+	
+	private boolean m_handlingChange;
 
     static class SCDImpl implements ComponentDependencyDeclaration {
         private final String m_name;
@@ -217,6 +228,10 @@ public class ComponentImpl implements Co
 
 	@Override
 	public void handleAdded(DependencyContext dc, Event e) {
+		if (debug) {
+			System.out.println("*" + debugKey + " T" + Thread.currentThread().getId() + " handleAdded " + e);
+		}
+
 	    Set<Event> dependencyEvents = m_dependencyEvents.get(dc);
 	    dependencyEvents.add(e);
 	    dc.setAvailable(true);
@@ -327,14 +342,37 @@ public class ComponentImpl implements Co
     }
 
     private void handleChange() {
-        ComponentState oldState;
-        ComponentState newState;
-        do {
-            oldState = m_state;
-            newState = calculateNewState(oldState);
-            m_state = newState;
-        }
-        while (performTransition(oldState, newState));
+    	// At this point, our component is starting, or a dependency is being added/changed/removed. 
+    	// So, we have to calculate a new state change for this component.
+    	// Now, if we decide to call the component's init method, then at this point, if the component adds
+    	// some additional instance-bound (and *available*) dependencies, then this will trigger a recursive call to 
+    	// our handleChange method, which we are currently executing. Since this would mess around with the execution of 
+    	// our current handleChange method execution, we are using a special "m_handlingChange" flag, which avoids this 
+    	// kind of problem.
+    	if (! m_handlingChange) {
+    		if (debug) {
+    			System.out.println("*" + debugKey + " T" + Thread.currentThread().getId() + " handleChange");
+    		}
+    	    try {
+    	        m_handlingChange = true;
+    	        ComponentState oldState;
+    	        ComponentState newState;
+    	        do {
+    	            oldState = m_state;
+    	            newState = calculateNewState(oldState);
+    	        	if (debug) {
+    	        		System.out.println("*" + debugKey + " T" + Thread.currentThread().getId() + " " + oldState + " -> " + newState);
+    	        	}
+    	            m_state = newState;
+    	        }
+    	        while (performTransition(oldState, newState));
+    	    } finally {
+    	        m_handlingChange = false;
+    	        if (debug) {
+    	        	System.out.println("*" + debugKey + " T" + Thread.currentThread().getId() + " end handling change.");
+    	        }
+    	    }
+    	}
     }
     
 	/** Based on the current state, calculate the new state. */
@@ -379,6 +417,9 @@ public class ComponentImpl implements Co
 			return true;
 		}
 		if (oldState == ComponentState.WAITING_FOR_REQUIRED && newState == ComponentState.INSTANTIATED_AND_WAITING_FOR_REQUIRED) {
+			if (debug) {
+				System.out.println("*" + debugKey + " T" + Thread.currentThread().getId() + " instantiate!");
+			}
 			instantiateComponent();
 			invokeAddRequiredDependencies();
 			invokeAutoConfigDependencies();
@@ -470,6 +511,9 @@ public class ComponentImpl implements Co
     
     private void startDependencies(List<DependencyContext> dependencies) {
         // Start first optional dependencies first.
+    	if (debug) {
+    		System.out.println("*" + debugKey + " T" + Thread.currentThread().getId() + " startDependencies.");
+    	}
         List<DependencyContext> requiredDeps = new ArrayList();
         for (DependencyContext d : dependencies) {
             if (d.isRequired()) {
@@ -1104,4 +1148,12 @@ public class ComponentImpl implements Co
     public ComponentDeclaration getComponentDeclaration() {
         return this;
     }
+    
+    @Override
+    public String toString() {
+    	if (debug) {
+    		System.out.println(debugKey);
+    	}
+    	return super.toString();
+    }
 }

Modified: felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/DependencyImpl.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/DependencyImpl.java?rev=1595756&r1=1595755&r2=1595756&view=diff
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/DependencyImpl.java (original)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/DependencyImpl.java Mon May 19 06:47:07 2014
@@ -203,9 +203,13 @@ public class DependencyImpl<T extends De
         }
 	}
 
-	public void invoke(String method, Event e) {
+	public void invoke(String method, Event e, Object[] instances) {
 		// specific for this type of dependency
-		m_component.invokeCallbackMethod(getInstances(), method, new Class[][] {{}}, new Object[][] {{}});
+		m_component.invokeCallbackMethod(instances, method, new Class[][] {{}}, new Object[][] {{}});
+	}
+	
+	public void invoke(String method, Event e) {
+		invoke(method, e, getInstances());
 	}
 	
 	public T setRequired(boolean required) {
@@ -285,6 +289,10 @@ public class DependencyImpl<T extends De
 		return null;
 	}
 	
+	public ComponentContext getComponentContext() {
+		return m_component;
+	}
+	
     protected void ensureNotActive() {
         if (isStarted()) {
             throw new IllegalStateException("Cannot modify state while active.");

Modified: felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/FactoryConfigurationAdapterImpl.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/FactoryConfigurationAdapterImpl.java?rev=1595756&r1=1595755&r2=1595756&view=diff
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/FactoryConfigurationAdapterImpl.java (original)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/FactoryConfigurationAdapterImpl.java Mon May 19 06:47:07 2014
@@ -5,7 +5,6 @@ import java.lang.reflect.InvocationTarge
 import java.util.Dictionary;
 import java.util.Enumeration;
 import java.util.Hashtable;
-import java.util.List;
 
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.Constants;
@@ -14,7 +13,6 @@ import org.osgi.service.metatype.MetaTyp
 import org.osgi.service.metatype.ObjectClassDefinition;
 
 import dm.Component;
-import dm.ComponentStateListener;
 import dm.Dependency;
 import dm.DependencyManager;
 import dm.PropertyMetaData;

Modified: felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/FilterComponent.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/FilterComponent.java?rev=1595756&r1=1595755&r2=1595756&view=diff
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/FilterComponent.java (original)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/FilterComponent.java Mon May 19 06:47:07 2014
@@ -29,7 +29,6 @@ import org.osgi.framework.ServiceRegistr
 import dm.Component;
 import dm.ComponentDeclaration;
 import dm.ComponentDependencyDeclaration;
-import dm.ComponentState;
 import dm.ComponentStateListener;
 import dm.Dependency;
 import dm.DependencyManager;
@@ -311,4 +310,11 @@ public class FilterComponent implements 
     public ComponentDeclaration getComponentDeclaration() {
         return this;
     }
+
+    // TODO: Remove..
+	@Override
+	public void setDebug(String string) {
+		// TODO Auto-generated method stub
+		
+	}
 }
\ No newline at end of file

Modified: felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/Logger.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/Logger.java?rev=1595756&r1=1595755&r2=1595756&view=diff
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/Logger.java (original)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/Logger.java Mon May 19 06:47:07 2014
@@ -23,7 +23,6 @@ import java.lang.reflect.Method;
 
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.BundleException;
-import org.osgi.framework.FrameworkUtil;
 import org.osgi.framework.InvalidSyntaxException;
 import org.osgi.framework.ServiceEvent;
 import org.osgi.framework.ServiceListener;

Modified: felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/SerialExecutor.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/SerialExecutor.java?rev=1595756&r1=1595755&r2=1595756&view=diff
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/SerialExecutor.java (original)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/SerialExecutor.java Mon May 19 06:47:07 2014
@@ -90,7 +90,52 @@ public class SerialExecutor implements E
             command.run();
         }
         catch (Throwable t) {
-            m_logger.log(LogService.LOG_ERROR, "Error processing tasks", t);
+        	if (m_logger != null) {
+        		m_logger.log(LogService.LOG_ERROR, "Error processing tasks", t);
+        	}
         }
     }
+
+    // TODO ASPECTS: Review. Added methods for separately scheduling and executing tasks
+    // on the SerialExecutor. This is used in the ServiceTracker which must ensure
+    // customizer callback methods are executed in the correct order.
+    
+    
+	public void schedule(Runnable runnable) {
+		synchronized (this) {
+			m_queue.add(runnable);
+		}
+	}
+
+	public void execute() {
+		Runnable next = null;
+		synchronized (this) {
+			if (m_runningThread == null || m_runningThread == Thread.currentThread()) {
+				// It's our turn
+				if (!m_queue.isEmpty()) {
+					next = m_queue.get(0);
+					m_runningThread = Thread.currentThread();
+				} else {
+					return;
+				}
+			} else {
+				return;
+			}
+		} 
+        while (next != null) {
+            runTask(next);
+            synchronized (this) {
+                m_queue.remove(0); // The first element is the one we have just executed
+                next = m_queue.isEmpty() ? null : (Runnable) m_queue.get(0);
+                if (next == null) {
+                    m_runningThread = null;
+                }
+            }
+        }
+	}
+	
+	@Override
+	public String toString() {
+		return "[Executor: queue size: " + m_queue.size() + "]";
+	}
 }
\ No newline at end of file

Modified: felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/ServiceDependencyImpl.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/ServiceDependencyImpl.java?rev=1595756&r1=1595755&r2=1595756&view=diff
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/ServiceDependencyImpl.java (original)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/ServiceDependencyImpl.java Mon May 19 06:47:07 2014
@@ -3,6 +3,7 @@ package dm.impl;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Proxy;
 import java.util.AbstractMap;
+import java.util.Arrays;
 import java.util.Dictionary;
 import java.util.HashSet;
 import java.util.Map;
@@ -18,6 +19,7 @@ import org.osgi.service.log.LogService;
 import tracker.ServiceTracker;
 import tracker.ServiceTrackerCustomizer;
 import dm.Component;
+import dm.ComponentDeclaration;
 import dm.ServiceDependency;
 import dm.context.DependencyContext;
 import dm.context.Event;
@@ -33,6 +35,13 @@ public class ServiceDependencyImpl exten
     private volatile Object m_defaultImplementation;
     private volatile Object m_defaultImplementationInstance;
     private volatile Object m_nullObject;
+    private boolean debug = false;
+    private String debugKey;
+    
+    public void setDebug(String debugKey) {
+    	this.debugKey = debugKey;
+    	this.debug = true;
+    }
 
     /**
      * Entry to wrap service properties behind a Map.
@@ -193,6 +202,9 @@ public class ServiceDependencyImpl exten
             } else {
                 throw new IllegalStateException("Could not create tracker for dependency, no service name specified.");
             }
+            if (debug) {
+            	m_tracker.setDebug(debugKey);
+            }
             m_tracker.open();
         }
 	}
@@ -214,6 +226,9 @@ public class ServiceDependencyImpl exten
 
 	@Override
 	public void addedService(ServiceReference reference, Object service) {
+		if (debug) {
+			System.out.println(debugKey + " addedService");
+		}
 		add(new ServiceEventImpl(reference, service));
 	}
 
@@ -228,9 +243,9 @@ public class ServiceDependencyImpl exten
 	}
 	
 	@Override
-	public void invoke(String method, Event e) {
+	public void invoke(String method, Event e, Object[] instances) {
 		ServiceEventImpl se = (ServiceEventImpl) e;
-		m_component.invokeCallbackMethod(getInstances(), method,
+		m_component.invokeCallbackMethod(instances, method,
 		    new Class[][]{
             {Component.class, ServiceReference.class, m_trackedServiceName},
             {Component.class, ServiceReference.class, Object.class}, 
@@ -265,6 +280,10 @@ public class ServiceDependencyImpl exten
 		);
 	}
 	
+	public void invoke(String method, Event e) {
+		invoke(method, e, getInstances());
+	}
+	
 	@Override
     public Class<?> getAutoConfigType() {
         return m_trackedServiceName;
@@ -425,15 +444,63 @@ public class ServiceDependencyImpl exten
         return m_defaultImplementationInstance;
     }
 
+    public void invokeSwap(String swapMethod, ServiceReference previousReference, Object previous,
+			ServiceReference currentReference, Object current) {
+    	try {
+    		invokeSwap(swapMethod, previousReference, previous, currentReference, current, getInstances());
+    	} catch (Exception e) {
+    		e.printStackTrace();
+    	}
+    }
+    
+    public void invokeSwap(String swapMethod, ServiceReference previousReference, Object previous,
+			ServiceReference currentReference, Object current, Object[] instances) {
+    	if (debug) {
+    		System.out.println("invoke swap: " + swapMethod + " on component " + m_component + ", instances: " + Arrays.toString(instances) + " - " + ((ComponentDeclaration)m_component).getState());
+    	}
+    	try {
+		m_component.invokeCallbackMethod(instances, swapMethod,
+				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, currentReference, current},
+                    {previousReference, previous, currentReference, current}, {m_component, previous, current},
+                    {m_component, previous, current}, {m_component, previousReference, previous, currentReference, current},
+                    {m_component, previousReference, previous, currentReference, current}}
+			);
+    	} catch (Exception e) {
+    		e.printStackTrace();
+    	}
+	}
+
 	@Override
-	public void swappedService(ServiceReference reference, Object service,
-			ServiceReference newReference, Object newService) {
-		System.out.println("### SWAPPED");
+	public void swappedService(final ServiceReference reference, final Object service,
+			final ServiceReference newReference, final Object newService) {
 		if (m_swap != null) {
-			// TODO: invoke swap callback
+			// it's will not trigger a state change, but the actual swap should be scheduled to prevent things
+			// getting out of order.
+			// TODO ASPECTS: Check the relation with the component lifecycle. When not adding the 'false' to the execute,
+			// the swap is sometimes performed before the actual initial component.added has been completed. This
+			// is due to prevention of re-entrant execution issues as described in ComponentImpl.handleChange()
+			((SerialExecutor)m_component.getExecutor()).execute(new Runnable() {
+				@Override
+				public void run() {
+				    invokeSwap(m_swap, reference, service, newReference, newService);
+				}
+			}, false);
 		} else {
 			addedService(newReference, newService);
-			removedService(newReference, newService);
+			removedService(reference, service);
 		}
 	}
 

Modified: felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/TemporalServiceDependencyImpl.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/TemporalServiceDependencyImpl.java?rev=1595756&r1=1595755&r2=1595756&view=diff
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/TemporalServiceDependencyImpl.java (original)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/TemporalServiceDependencyImpl.java Mon May 19 06:47:07 2014
@@ -8,6 +8,7 @@ import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.ServiceReference;
 
+import dm.DependencyActivatorBase;
 import dm.ServiceDependency;
 
 /**

Modified: felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/index/AspectFilterIndex.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/index/AspectFilterIndex.java?rev=1595756&r1=1595755&r2=1595756&view=diff
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/index/AspectFilterIndex.java (original)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/index/AspectFilterIndex.java Mon May 19 06:47:07 2014
@@ -29,10 +29,6 @@ import java.util.SortedMap;
 import java.util.SortedSet;
 import java.util.TreeMap;
 
-import dm.*;
-import dm.impl.ServiceUtil;
-import tracker.*;
-
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.Constants;
 import org.osgi.framework.InvalidSyntaxException;
@@ -40,6 +36,12 @@ import org.osgi.framework.ServiceEvent;
 import org.osgi.framework.ServiceListener;
 import org.osgi.framework.ServiceReference;
 
+import tracker.ServiceTracker;
+import tracker.ServiceTrackerCustomizer;
+import dm.DependencyManager;
+import dm.FilterIndex;
+import dm.impl.ServiceUtil;
+
 /**
  * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
  */

Modified: felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/index/ServiceRegistryCache.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/index/ServiceRegistryCache.java?rev=1595756&r1=1595755&r2=1595756&view=diff
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/index/ServiceRegistryCache.java (original)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/index/ServiceRegistryCache.java Mon May 19 06:47:07 2014
@@ -18,7 +18,6 @@
  */
 package dm.impl.index;
 
-import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;

Modified: felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/index/multiproperty/Property.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/index/multiproperty/Property.java?rev=1595756&r1=1595755&r2=1595756&view=diff
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/index/multiproperty/Property.java (original)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/index/multiproperty/Property.java Mon May 19 06:47:07 2014
@@ -20,12 +20,9 @@ package dm.impl.index.multiproperty;
 
 import java.util.Set;
 import java.util.TreeSet;
-
 /**
  * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
  */
-import java.util.Set;
-import java.util.TreeSet;
 
 public class Property {
 	boolean m_negate;

Modified: felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/metatype/MetaTypeProviderImpl.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/metatype/MetaTypeProviderImpl.java?rev=1595756&r1=1595755&r2=1595756&view=diff
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/metatype/MetaTypeProviderImpl.java (original)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/metatype/MetaTypeProviderImpl.java Mon May 19 06:47:07 2014
@@ -32,8 +32,6 @@ import java.util.Properties;
 import java.util.StringTokenizer;
 import java.util.TreeSet;
 
-import dm.PropertyMetaData;
-import dm.impl.Logger;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.Constants;
 import org.osgi.service.cm.ConfigurationException;
@@ -43,6 +41,9 @@ import org.osgi.service.log.LogService;
 import org.osgi.service.metatype.MetaTypeProvider;
 import org.osgi.service.metatype.ObjectClassDefinition;
 
+import dm.PropertyMetaData;
+import dm.impl.Logger;
+
 /**
  * When a ConfigurationDepdendency is configured with properties metadata, we provide
  * a specific ManagedService which also implements the MetaTypeProvider interface. This interface

Modified: felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/metatype/PropertyMetaDataImpl.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/metatype/PropertyMetaDataImpl.java?rev=1595756&r1=1595755&r2=1595756&view=diff
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/metatype/PropertyMetaDataImpl.java (original)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/metatype/PropertyMetaDataImpl.java Mon May 19 06:47:07 2014
@@ -23,9 +23,10 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
-import dm.PropertyMetaData;
 import org.osgi.service.metatype.AttributeDefinition;
 
+import dm.PropertyMetaData;
+
 /**
  * DependencyManager PropertyMetaData Implementation. This class describes meta informations regarding
  * one given configuration property.

Modified: felix/sandbox/pderop/dependencymanager-prototype/dm/src/tracker/AbstractTracked.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm/src/tracker/AbstractTracked.java?rev=1595756&r1=1595755&r2=1595756&view=diff
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm/src/tracker/AbstractTracked.java (original)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm/src/tracker/AbstractTracked.java Mon May 19 06:47:07 2014
@@ -21,6 +21,8 @@ import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 
+import dm.impl.SerialExecutor;
+
 /**
  * Abstract class to track items. If a Tracker is reused (closed then reopened),
  * then a new AbstractTracked object is used. This class acts a map of tracked
@@ -94,6 +96,8 @@ abstract class AbstractTracked {
 	 * @GuardedBy this
 	 */
 	private final LinkedList	initial;
+	
+	private SerialExecutor m_executor = new SerialExecutor(null); 
 
 	/**
 	 * AbstractTracked constructor.
@@ -180,15 +184,20 @@ abstract class AbstractTracked {
 					continue; /* skip this item */
 				}
 				adding.add(item);
+				final AbstractCustomizerActionSet actionSet = trackAdding(item, null);
+				m_executor.schedule(new Runnable() {
+
+					@Override
+					public void run() {
+						actionSet.execute();
+						
+					}
+					
+				});
 			}
 			if (DEBUG) {
 				System.out.println("AbstractTracked.trackInitial: " + item); //$NON-NLS-1$
 			}
-			trackAdding(item, null).execute(); /*
-									 * Begin tracking it. We call trackAdding
-									 * since we have already put the item in the
-									 * adding list.
-									 */
 		}
 	}
 
@@ -429,6 +438,14 @@ abstract class AbstractTracked {
 	int getTrackingCount() {
 		return trackingCount;
 	}
+	
+	/**
+	 * Returns the serial executor used by this tracked.
+	 * @return
+	 */
+	SerialExecutor getExecutor() {
+		return m_executor;
+	}
 
 	/**
 	 * Call the specific customizer adding method. This method must not be

Modified: felix/sandbox/pderop/dependencymanager-prototype/dm/src/tracker/ServiceTracker.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm/src/tracker/ServiceTracker.java?rev=1595756&r1=1595755&r2=1595756&view=diff
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm/src/tracker/ServiceTracker.java (original)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm/src/tracker/ServiceTracker.java Mon May 19 06:47:07 2014
@@ -25,6 +25,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.TreeSet;
+import java.util.concurrent.atomic.AtomicInteger;
 
 import org.osgi.framework.AllServiceListener;
 import org.osgi.framework.BundleContext;
@@ -140,6 +141,14 @@ public class ServiceTracker implements S
 	 * track all aspects or just the highest ranked ones.
 	 */
     public boolean m_trackAllAspects;
+    
+    private boolean debug = false;
+    private String debugKey;
+    
+    public void setDebug(String debugKey) {
+    	this.debug = true;
+    	this.debugKey = debugKey;
+    }
 
 	/**
 	 * Create a <code>ServiceTracker</code> on the specified
@@ -338,6 +347,9 @@ public class ServiceTracker implements S
      *         longer valid.
      */
 	public void open(boolean trackAllServices, boolean trackAllAspects) {
+		if (debug) {
+			System.out.println("[ServiceTracker] " + debugKey + " T" + Thread.currentThread().getId() + " open");
+		}
 		final Tracked t;
 		synchronized (this) {
 			if (tracked != null) {
@@ -370,7 +382,12 @@ public class ServiceTracker implements S
 						}
 					}
 					/* set tracked with the initial references */
-					t.setInitial(references); 
+					t.setInitial(references);
+					
+					// only actually schedules the actions for execution within this synchronized block,
+					// but do the actual execution afterwards.
+					t.trackInitial(); 
+
 				}
 				catch (InvalidSyntaxException e) {
 					throw new RuntimeException(
@@ -381,7 +398,8 @@ public class ServiceTracker implements S
 			tracked = t;
 		}
 		/* Call tracked outside of synchronized region */
-		t.trackInitial(); /* process the initial references */
+		// just trigger the executor
+		t.getExecutor().execute();
 	}
 
 	/**
@@ -1101,12 +1119,12 @@ public class ServiceTracker implements S
 		 * @param event <code>ServiceEvent</code> object from the framework.
 		 */
 		public void serviceChanged(final ServiceEvent event) {
-		    if (m_trackAllAspects) {
-		        serviceChangedIncludeAspects(event);
-		    }
-		    else {
-		        serviceChangedHideAspects(event);
-		    }
+			if (m_trackAllAspects) {
+				serviceChangedIncludeAspects(event);
+			}
+			else {
+				serviceChangedHideAspects(event);
+			}
 		}
 		
         public void serviceChangedIncludeAspects(final ServiceEvent event) {
@@ -1118,6 +1136,9 @@ public class ServiceTracker implements S
                 return;
             }
             final ServiceReference reference = event.getServiceReference();
+			if (debug) {
+				System.out.println("[ServiceTracker] " + debugKey + " T" + Thread.currentThread().getId() + " [serviceChangedIncludeAspects] " + reference.getProperty("service.ranking"));
+			}            
             if (DEBUG) {
                 System.out
                         .println("ServiceTracker.Tracked.serviceChanged["
@@ -1166,8 +1187,11 @@ public class ServiceTracker implements S
         private boolean isModifiedEndmatchSupported() {
         	return listenerFilter != null;
         }
+        
+        private AtomicInteger step = new AtomicInteger();
 		
 		public void serviceChangedHideAspects(final ServiceEvent event) {
+			int n = step.getAndIncrement();
 			/*
 			 * Check if we had a delayed call (which could happen when we
 			 * close).
@@ -1178,85 +1202,107 @@ public class ServiceTracker implements S
 			final ServiceReference reference = event.getServiceReference();
 			if (DEBUG) {
 				System.out
-						.println("ServiceTracker.Tracked.serviceChanged["
+						.println(n + " ServiceTracker.Tracked.serviceChanged["
 						+ event.getType() + "]: " + reference);  
 			}
 
 			long sid = ServiceUtil.getServiceId(reference);
-			switch (event.getType()) {
-				case ServiceEvent.REGISTERED :
-				case ServiceEvent.MODIFIED :
-				    ServiceReference higherRankedReference = null;
-				    ServiceReference lowerRankedReference = null;
-				    ServiceReference highestTrackedReference = highestTrackedCache(sid);
-				    if (highestTrackedReference != null) {
-				        int ranking = ServiceUtil.getRanking(reference);
-				        int highestTrackedRanking = ServiceUtil.getRanking(highestTrackedReference);
-				        if (ranking > highestTrackedRanking) {
-				            // found a higher ranked one!
-				            if (DEBUG) {
-				                System.out.println("ServiceTracker.Tracked.serviceChanged[" + event.getType() + "]: Found a higher ranked aspect: " + ServiceUtil.toString(reference) + " vs " + ServiceUtil.toString(highestTrackedReference));
-				            }
-				            higherRankedReference = highestTrackedReference;
-				        }
-				        else if (ranking < highestTrackedRanking) {
-				            // found lower ranked one!
-                            if (DEBUG) {
-                                System.out.println("ServiceTracker.Tracked.serviceChanged[" + event.getType() + "]: Found a lower ranked aspect: " + ServiceUtil.toString(reference) + " vs " + ServiceUtil.toString(highestTrackedReference));
-                            }
-				            lowerRankedReference = highestTrackedReference;
-				        }
-				    }
-				    
-					if (isModifiedEndmatchSupported()) { // either registered or modified
-					    registerOrUpdate(event, reference, higherRankedReference, lowerRankedReference);
-					}
-					else { // service listener added without filter
-						if (filter.match(reference)) {
-	                        registerOrUpdate(event, reference, higherRankedReference, lowerRankedReference);
+			AbstractCustomizerActionSet actionSet = null;
+			synchronized(this) {
+				switch (event.getType()) {
+					case ServiceEvent.REGISTERED :
+					case ServiceEvent.MODIFIED :
+					    ServiceReference higherRankedReference = null;
+					    ServiceReference lowerRankedReference = null;
+					    ServiceReference highestTrackedReference = highestTrackedCache(sid);
+					    if (highestTrackedReference != null) {
+					        int ranking = ServiceUtil.getRanking(reference);
+					        int highestTrackedRanking = ServiceUtil.getRanking(highestTrackedReference);
+					        if (ranking > highestTrackedRanking) {
+					            // found a higher ranked one!
+					            if (DEBUG) {
+					                System.out.println("ServiceTracker.Tracked.serviceChanged[" + event.getType() + "]: Found a higher ranked aspect: " + ServiceUtil.toString(reference) + " vs " + ServiceUtil.toString(highestTrackedReference));
+					            }
+					            higherRankedReference = highestTrackedReference;
+					        }
+					        else if (ranking < highestTrackedRanking) {
+					            // found lower ranked one!
+	                            if (DEBUG) {
+	                                System.out.println("ServiceTracker.Tracked.serviceChanged[" + event.getType() + "]: Found a lower ranked aspect: " + ServiceUtil.toString(reference) + " vs " + ServiceUtil.toString(highestTrackedReference));
+	                            }
+					            lowerRankedReference = highestTrackedReference;
+					        }
+					    }
+						if (isModifiedEndmatchSupported()) { // either registered or modified
+							actionSet = registerOrUpdate(event, reference, higherRankedReference, lowerRankedReference);
 						}
-						else {
-		                    unregister(event, reference, sid);
+						else { // service listener added without filter
+							if (filter.match(reference)) {
+		                        actionSet = registerOrUpdate(event, reference, higherRankedReference, lowerRankedReference);
+							}
+							else {
+			                    actionSet = unregister(event, reference, sid);
+							}
 						}
+						break;
+	                case 8 /* ServiceEvent.MODIFIED_ENDMATCH */ : // handle as unregister
+					case ServiceEvent.UNREGISTERING :
+						actionSet = unregister(event, reference, sid);
+						/*
+						 * If the customizer throws an unchecked exception, it is
+						 * safe to let it propagate
+						 */
+						break;
+				}
+				// schedule the actionset for execution. We'll use a serial executor to prevent the actions to
+				// be performed out of order.
+				final AbstractCustomizerActionSet commandActionSet = actionSet;
+				getExecutor().schedule(new Runnable() {
+
+					@Override
+					public void run() {
+						commandActionSet.execute();
 					}
-					break;
-                case 8 /* ServiceEvent.MODIFIED_ENDMATCH */ : // handle as unregister
-				case ServiceEvent.UNREGISTERING :
-					unregister(event, reference, sid);
-					/*
-					 * If the customizer throws an unchecked exception, it is
-					 * safe to let it propagate
-					 */
-					break;
+					
+				});
 			}
+			getExecutor().execute();
 		}
-
-		private void registerOrUpdate(final ServiceEvent event,
+		
+		private AbstractCustomizerActionSet registerOrUpdate(final ServiceEvent event,
 				final ServiceReference reference, ServiceReference higher,
 				ServiceReference lower) {
+			if (debug) {
+//				System.out.println("[ServiceTracker] " + debugKey + " T" + Thread.currentThread().getId() + " [registerOrUpdate] lower: " + lower + ", higher: " + higher);
+			}
+			AbstractCustomizerActionSet actionSet = null;
 			if (lower != null) {
 			    hide(reference);
 			}
 			else {
-				AbstractCustomizerActionSet actionSet = track(reference, event);
+				actionSet = track(reference, event);
 		        if (higher != null) {
 	                actionSet.appendActionSet(untrack(higher, null));
 	                hide(higher);
 			    }
-		        actionSet.execute();
 			}
 			/*
 			 * If the customizer throws an unchecked exception, it
 			 * is safe to let it propagate
 			 */
+			return actionSet;
 		}
 
-		private void unregister(final ServiceEvent event,
+		private AbstractCustomizerActionSet unregister(final ServiceEvent event,
 				final ServiceReference reference, long sid) {
+			if (debug) {
+				System.out.println("[ServiceTracker] " + debugKey + " T" + Thread.currentThread().getId() + " [unregister] " + reference.getProperty("service.ranking"));
+			}
+			AbstractCustomizerActionSet actionSet = null;
 			ServiceReference ht = highestTrackedCache(sid);
 			if (reference.equals(ht)) {
 		        ServiceReference hh = highestHiddenCache(sid);
-		        AbstractCustomizerActionSet actionSet = null;
+		        
 		        if (hh != null) {
 		            unhide(hh);
 		            actionSet = track(hh, null);
@@ -1266,11 +1312,11 @@ public class ServiceTracker implements S
 		        } else {
 		        	actionSet.appendActionSet(untrack(reference, event));
 		        }
-		        actionSet.execute();
 			}
 			else {
 			    unhide(reference);
 			}
+			return actionSet;
 		}
 		
 		
@@ -1360,14 +1406,49 @@ public class ServiceTracker implements S
 			// This actions set deliberately postpones invocation of the customizer methods to be able to combine added and removed
 			// into a single swap call.
 			return new AbstractCustomizerActionSet() {
+				
+				@Override
+				public void addCustomizerAdded(Object item, Object related,
+						Object object) {
+					if (debug) {
+//						System.out.println("[ServiceTracker] " + debugKey + " T" + Thread.currentThread().getId() + " addCustomizerAdded " + object);
+					}
+					super.addCustomizerAdded(item, related, object);
+				}
+				
+				@Override
+				public void addCustomizerModified(Object item, Object related,
+						Object object) {
+					if (debug) {
+//						System.out.println("[ServiceTracker] " + debugKey + " T" + Thread.currentThread().getId() + " addCustomizerModified " + object);
+					}					
+					super.addCustomizerModified(item, related, object);
+				}
+				
+				@Override
+				public void addCustomizerRemoved(Object item, Object related,
+						Object object) {
+					if (debug) {
+//						System.out.println("[ServiceTracker] " + debugKey + " T" + Thread.currentThread().getId() + " addCustomizerRemoved " + object);
+					}					
+					super.addCustomizerRemoved(item, related, object);
+				}
+				
 				@Override
 				void execute() {
 					// inspect the actions and check whether we should perform a swap
 					List<CustomizerAction> actions = getActions();
+					if (actions.size() > 2) {
+						throw new IllegalStateException("Unexpected action count: " + actions.size());
+					}
 					if (actions.size() == 2 && actions.get(0).getType() == Type.ADDED && actions.get(1).getType() == Type.REMOVED) {
 						// ignore related
 						// item = ServiceReference
 						// object = service
+						debug("swapped");
+						if (debug) {
+							System.out.println("[ServiceTracker] " + debugKey + " T" + Thread.currentThread().getId() + " swapping " + actions.get(1).getObject() + " with " + actions.get(0).getObject());
+						}
 						customizer.swappedService((ServiceReference)actions.get(1).getItem(), actions.get(1).getObject(), (ServiceReference)actions.get(0).getItem(), actions.get(0).getObject());
 					} else {
 						// just sequentially call the customizer methods
@@ -1375,12 +1456,21 @@ public class ServiceTracker implements S
 							try {
 								switch (action.getType()) {
 									case ADDED: 
+										debug(Thread.currentThread().getId() + " added");
+										if (debug) {
+											System.out.println("[ServiceTracker] " + debugKey + " T" + Thread.currentThread().getId() + " adding " + action.getObject());
+										}
 										customizerAdded(action.getItem(), action.getRelated(), action.getObject());
 										break;
 									case MODIFIED:
+										debug("modified");
 										customizerModified(action.getItem(), action.getRelated(), action.getObject());
 										break;
 									case REMOVED:
+										debug("removed");
+										if (debug) {
+											System.out.println("[ServiceTracker] " + debugKey + " T" + Thread.currentThread().getId() + " removing " + action.getObject());
+										}
 										customizerRemoved(action.getItem(), action.getRelated(), action.getObject());
 								}
 							} catch (Exception e) {
@@ -1393,6 +1483,12 @@ public class ServiceTracker implements S
 		}
 
 	}
+	
+	private void debug(String message) {
+		if (customizer.toString().equals("ServiceDependency[interface dm.it.AspectRaceTest$S (&(!(org.apache.felix.dependencymanager.aspect=*))(id=1))]")) {
+//			System.out.println(message);
+		}
+	}
 
 	/**
 	 * Subclass of Tracked which implements the AllServiceListener interface.