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/03/23 20:59:01 UTC

svn commit: r1580585 - in /felix/sandbox/pderop/dependencymanager-prototype: cnf/localrepo/ cnf/localrepo/org.mockito.mockito-all/ dm.it/src/dm/it/ dm/ dm/src/dm/impl/ dm/src/dm/impl/index/ dm/src/dm/impl/index/multiproperty/ dm/src/tracker/ dm/test/tr...

Author: uiterlix
Date: Sun Mar 23 19:59:00 2014
New Revision: 1580585

URL: http://svn.apache.org/r1580585
Log:
Added customizerSwapped support to ServiceTracker for use by ServiceDependency when a swap callback is specified.

Added:
    felix/sandbox/pderop/dependencymanager-prototype/cnf/localrepo/org.mockito.mockito-all/
    felix/sandbox/pderop/dependencymanager-prototype/cnf/localrepo/org.mockito.mockito-all/org.mockito.mockito-all-1.9.5.jar   (with props)
    felix/sandbox/pderop/dependencymanager-prototype/dm.it/src/dm/it/AspectServiceDependencyTest.java
    felix/sandbox/pderop/dependencymanager-prototype/dm/src/tracker/AbstractCustomizerActionSet.java
    felix/sandbox/pderop/dependencymanager-prototype/dm/test/tracker/
    felix/sandbox/pderop/dependencymanager-prototype/dm/test/tracker/TrackedTest.java
Modified:
    felix/sandbox/pderop/dependencymanager-prototype/cnf/localrepo/index.xml
    felix/sandbox/pderop/dependencymanager-prototype/dm/bnd.bnd
    felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/ServiceDependencyImpl.java
    felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/index/AbstractFactoryFilterIndex.java
    felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/index/multiproperty/MultiPropertyFilterIndex.java
    felix/sandbox/pderop/dependencymanager-prototype/dm/src/tracker/AbstractTracked.java
    felix/sandbox/pderop/dependencymanager-prototype/dm/src/tracker/BundleTracker.java
    felix/sandbox/pderop/dependencymanager-prototype/dm/src/tracker/ServiceTracker.java
    felix/sandbox/pderop/dependencymanager-prototype/dm/src/tracker/ServiceTrackerCustomizer.java

Modified: felix/sandbox/pderop/dependencymanager-prototype/cnf/localrepo/index.xml
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/cnf/localrepo/index.xml?rev=1580585&r1=1580584&r2=1580585&view=diff
==============================================================================
Binary files - no diff available.

Added: felix/sandbox/pderop/dependencymanager-prototype/cnf/localrepo/org.mockito.mockito-all/org.mockito.mockito-all-1.9.5.jar
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/cnf/localrepo/org.mockito.mockito-all/org.mockito.mockito-all-1.9.5.jar?rev=1580585&view=auto
==============================================================================
Binary file - no diff available.

Propchange: felix/sandbox/pderop/dependencymanager-prototype/cnf/localrepo/org.mockito.mockito-all/org.mockito.mockito-all-1.9.5.jar
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream

Added: felix/sandbox/pderop/dependencymanager-prototype/dm.it/src/dm/it/AspectServiceDependencyTest.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm.it/src/dm/it/AspectServiceDependencyTest.java?rev=1580585&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm.it/src/dm/it/AspectServiceDependencyTest.java (added)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm.it/src/dm/it/AspectServiceDependencyTest.java Sun Mar 23 19:59:00 2014
@@ -0,0 +1,90 @@
+/*
+ * 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.it;
+
+import dm.Component;
+import dm.DependencyManager;
+
+
+public class AspectServiceDependencyTest extends TestBase {
+    public void testServiceRegistrationAndConsumption() {
+        DependencyManager m = new DependencyManager(context);
+        // helper class that ensures certain steps get executed in sequence
+        Ensure e = new Ensure();
+        // create a service provider and consumer
+        Component sp = m.createComponent().setImplementation(new ServiceProvider(e)).setInterface(ServiceInterface.class.getName(), null);
+        Component sc = m.createComponent().setImplementation(new ServiceConsumer(e)).add(m.createServiceDependency().setService(ServiceInterface.class).setRequired(true));
+        m.add(sp);
+        m.add(sc);
+        m.remove(sc);
+        m.remove(sp);
+        
+        // TODO: Further implementation when aspect services have been implemented.
+        
+        // ensure we executed all steps inside the component instance
+        e.step(4);
+    }
+    
+    static interface ServiceInterface {
+        public void invoke();
+    }
+
+    static class ServiceProvider implements ServiceInterface {
+        private final Ensure m_ensure;
+        public ServiceProvider(Ensure e) {
+            m_ensure = e;
+        }
+        public void invoke() {
+            m_ensure.step(2);
+        }
+    }
+
+    static class ServiceConsumer {
+        private volatile ServiceInterface m_service;
+        private final Ensure m_ensure;
+
+        public ServiceConsumer(Ensure e) {
+            m_ensure = e;
+        }
+        
+        public void init() {
+            m_ensure.step(1);
+            m_service.invoke();
+        }
+        
+        public void destroy() {
+            m_ensure.step(3);
+        }
+    }
+
+    static class ServiceConsumerCallbacks {
+        private final Ensure m_ensure;
+
+        public ServiceConsumerCallbacks(Ensure e) {
+            m_ensure = e;
+        }
+        
+        public void add(ServiceInterface service) {
+            m_ensure.step(4);
+        }
+        public void remove(ServiceInterface service) {
+            m_ensure.step(5);
+        }
+    }
+}

Modified: felix/sandbox/pderop/dependencymanager-prototype/dm/bnd.bnd
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm/bnd.bnd?rev=1580585&r1=1580584&r2=1580585&view=diff
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm/bnd.bnd (original)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm/bnd.bnd Sun Mar 23 19:59:00 2014
@@ -1,6 +1,6 @@
--buildpath: \
-	osgi.cmpn,\
-	osgi.core;version=4.2
+-buildpath: osgi.cmpn,\
+	osgi.core;version=4.2,\
+	org.mockito.mockito-all
 Private-Package: \
 	dm.impl,\
 	dm.context,\

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=1580585&r1=1580584&r2=1580585&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 Sun Mar 23 19:59:00 2014
@@ -25,6 +25,7 @@ import dm.context.Event;
 public class ServiceDependencyImpl extends DependencyImpl<ServiceDependency> implements ServiceDependency, ServiceTrackerCustomizer {
 	protected volatile ServiceTracker m_tracker;
     private final Logger m_logger;
+    protected String m_swap;
     protected volatile Class<?> m_trackedServiceName;
     private volatile String m_trackedServiceFilter;
     private volatile String m_trackedServiceFilterUnmodified;
@@ -130,13 +131,13 @@ public class ServiceDependencyImpl exten
     			
     public ServiceDependency setCallbacks(Object instance, String added, String changed, String removed, String swapped) {
         setCallbacks(instance, added, changed, removed);
-        // TODO handle the swapped parameter
+        m_swap = swapped;
         return this;
     }
     
     public ServiceDependency setCallbacks(String added, String changed, String removed, String swapped) {
         setCallbacks(added, changed, removed);
-        // TODO handle the swapped parameter
+        m_swap = swapped;
         return this;
     }
 		
@@ -423,4 +424,17 @@ public class ServiceDependencyImpl exten
         }
         return m_defaultImplementationInstance;
     }
+
+	@Override
+	public void swappedService(ServiceReference reference, Object service,
+			ServiceReference newReference, Object newService) {
+		System.out.println("### SWAPPED");
+		if (m_swap != null) {
+			// TODO: invoke swap callback
+		} else {
+			addedService(newReference, newService);
+			removedService(newReference, newService);
+		}
+	}
+
 }

Modified: felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/index/AbstractFactoryFilterIndex.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/index/AbstractFactoryFilterIndex.java?rev=1580585&r1=1580584&r2=1580585&view=diff
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/index/AbstractFactoryFilterIndex.java (original)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/index/AbstractFactoryFilterIndex.java Sun Mar 23 19:59:00 2014
@@ -49,6 +49,12 @@ public abstract class AbstractFactoryFil
         remove(reference);
     }
     
+	public void swappedService(ServiceReference reference, Object service,
+			ServiceReference newReference, Object newService) {
+		addedService(newReference, newService);
+		removedService(reference, service);
+	}
+    
     public void add(ServiceReference reference) {
         Long sid = ServiceUtil.getServiceIdObject(reference);
         synchronized (m_sidToServiceReferencesMap) {

Modified: felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/index/multiproperty/MultiPropertyFilterIndex.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/index/multiproperty/MultiPropertyFilterIndex.java?rev=1580585&r1=1580584&r2=1580585&view=diff
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/index/multiproperty/MultiPropertyFilterIndex.java (original)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/index/multiproperty/MultiPropertyFilterIndex.java Sun Mar 23 19:59:00 2014
@@ -484,4 +484,11 @@ public class MultiPropertyFilterIndex im
         sb.append("]");
         return sb.toString();
     }
+
+	@Override
+	public void swappedService(ServiceReference reference, Object service,
+			ServiceReference newReference, Object newService) {
+		addedService(newReference, newService);
+		removedService(reference, service);
+	}
 }

Added: felix/sandbox/pderop/dependencymanager-prototype/dm/src/tracker/AbstractCustomizerActionSet.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm/src/tracker/AbstractCustomizerActionSet.java?rev=1580585&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm/src/tracker/AbstractCustomizerActionSet.java (added)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm/src/tracker/AbstractCustomizerActionSet.java Sun Mar 23 19:59:00 2014
@@ -0,0 +1,95 @@
+/*
+ * 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 tracker;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public abstract class AbstractCustomizerActionSet {
+
+	enum Type { ADDED, MODIFIED, REMOVED };
+
+	final List<CustomizerAction> m_actions = new ArrayList<>();
+
+	public void addCustomizerAdded(Object item, Object related, Object object) {
+		m_actions.add(new CustomizerAction(Type.ADDED, item, related, object));
+	}
+	
+	public void addCustomizerModified(Object item, Object related, Object object) {
+		m_actions.add(new CustomizerAction(Type.MODIFIED, item, related, object));
+	}
+	
+	public void addCustomizerRemoved(Object item, Object related, Object object) {
+		m_actions.add(new CustomizerAction(Type.REMOVED, item, related, object));
+	}
+	
+	public void appendActionSet(AbstractCustomizerActionSet actionSet) {
+		for (CustomizerAction action : actionSet.getActions()) {
+			m_actions.add(action);
+		}
+	}
+	
+	abstract void execute();
+	
+	public List<CustomizerAction> getActions() {
+		return m_actions;
+	}
+	
+	@Override
+	public String toString() {
+		return "AbstractCustomizerActionSet [m_actions=" + m_actions + "]";
+	}
+
+	static class CustomizerAction {
+		private final Type m_type;
+		private final Object m_item;
+		private final Object m_related;
+		private final Object m_object;
+		
+		public CustomizerAction(Type type, Object item, Object related, Object object) {
+			m_type = type;
+			m_item = item;
+			m_related = related;
+			m_object = object;
+		}
+		
+		public Type getType() {
+			return m_type;
+		}
+		
+		public Object getItem() {
+			return m_item;
+		}
+		
+		public Object getRelated() {
+			return m_related;
+		}
+		
+		public Object getObject() {
+			return m_object;
+		}
+
+		@Override
+		public String toString() {
+			return "CustomizerAction [m_type=" + m_type + ", m_item=" + m_item
+					+ ", m_related=" + m_related + ", m_object=" + m_object
+					+ "]";
+		}
+	}
+}

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=1580585&r1=1580584&r2=1580585&view=diff
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm/src/tracker/AbstractTracked.java (original)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm/src/tracker/AbstractTracked.java Sun Mar 23 19:59:00 2014
@@ -184,7 +184,7 @@ abstract class AbstractTracked {
 			if (DEBUG) {
 				System.out.println("AbstractTracked.trackInitial: " + item); //$NON-NLS-1$
 			}
-			trackAdding(item, null); /*
+			trackAdding(item, null).execute(); /*
 									 * Begin tracking it. We call trackAdding
 									 * since we have already put the item in the
 									 * adding list.
@@ -199,17 +199,20 @@ abstract class AbstractTracked {
 		closed = true;
 	}
 
+	abstract AbstractCustomizerActionSet createCustomizerActionSet();
+	
 	/**
 	 * Begin to track an item.
 	 * 
 	 * @param item Item to be tracked.
 	 * @param related Action related object.
 	 */
-	void track(final Object item, final Object related) {
+	AbstractCustomizerActionSet track(final Object item, final Object related) {
 		final Object object;
+		final AbstractCustomizerActionSet actionSet = createCustomizerActionSet();
 		synchronized (this) {
 			if (closed) {
-				return;
+				return actionSet;
 			}
 			object = tracked.get(item);
 			if (object == null) { /* we are not tracking the item */
@@ -219,7 +222,7 @@ abstract class AbstractTracked {
 						System.out
 								.println("AbstractTracked.track[already adding]: " + item); //$NON-NLS-1$
 					}
-					return;
+					return actionSet;
 				}
 				adding.add(item); /* mark this item is being added */
 			}
@@ -233,16 +236,17 @@ abstract class AbstractTracked {
 		}
 
 		if (object == null) { /* we are not tracking the item */
-			trackAdding(item, related);
+			actionSet.appendActionSet(trackAdding(item, related));
 		}
 		else {
 			/* Call customizer outside of synchronized region */
-			customizerModified(item, related, object);
+			actionSet.addCustomizerModified(item, related, object);
 			/*
 			 * If the customizer throws an unchecked exception, it is safe to
 			 * let it propagate
 			 */
 		}
+		return actionSet;
 	}
 
 	/**
@@ -253,7 +257,8 @@ abstract class AbstractTracked {
 	 * @param item Item to be tracked.
 	 * @param related Action related object.
 	 */
-	private void trackAdding(final Object item, final Object related) {
+	private AbstractCustomizerActionSet trackAdding(final Object item, final Object related) {
+		final AbstractCustomizerActionSet actionSet = createCustomizerActionSet();
 		if (DEBUG) {
 			System.out.println("AbstractTracked.trackAdding: " + item); //$NON-NLS-1$
 		}
@@ -287,7 +292,7 @@ abstract class AbstractTracked {
 				}
 			}
 			if (needToCallback) {
-				customizerAdded(item, related, object);
+				actionSet.addCustomizerAdded(item, related, object);
 			}
 		}
 		/*
@@ -299,12 +304,13 @@ abstract class AbstractTracked {
 						.println("AbstractTracked.trackAdding[removed]: " + item); //$NON-NLS-1$
 			}
 			/* Call customizer outside of synchronized region */
-			customizerRemoved(item, related, object);
+			actionSet.addCustomizerRemoved(item, related, object);
 			/*
 			 * If the customizer throws an unchecked exception, it is safe to
 			 * let it propagate
 			 */
 		}
+		return actionSet;
 	}
 
 	/**
@@ -313,7 +319,8 @@ abstract class AbstractTracked {
 	 * @param item Item to be untracked.
 	 * @param related Action related object.
 	 */
-	void untrack(final Object item, final Object related) {
+	AbstractCustomizerActionSet untrack(final Object item, final Object related) {
+		AbstractCustomizerActionSet actionSet = createCustomizerActionSet();
 		final Object object;
 		synchronized (this) {
 			if (initial.remove(item)) { /*
@@ -324,7 +331,7 @@ abstract class AbstractTracked {
 					System.out
 							.println("AbstractTracked.untrack[removed from initial]: " + item); //$NON-NLS-1$
 				}
-				return; /*
+				return actionSet; /*
 						 * we have removed it from the list and it will not be
 						 * processed
 						 */
@@ -338,7 +345,7 @@ abstract class AbstractTracked {
 					System.out
 							.println("AbstractTracked.untrack[being added]: " + item); //$NON-NLS-1$
 				}
-				return; /*
+				return actionSet; /*
 						 * in case the item is untracked while in the process of
 						 * adding
 						 */
@@ -348,7 +355,7 @@ abstract class AbstractTracked {
 											 * calling customizer callback
 											 */
 			if (object == null) { /* are we actually tracking the item */
-				return;
+				return actionSet;
 			}
 			modified(); /* increment modification count */
 		}
@@ -356,11 +363,12 @@ abstract class AbstractTracked {
 			System.out.println("AbstractTracked.untrack[removed]: " + item); //$NON-NLS-1$
 		}
 		/* Call customizer outside of synchronized region */
-		customizerRemoved(item, related, object);
+		actionSet.addCustomizerRemoved(item, related, object);
 		/*
 		 * If the customizer throws an unchecked exception, it is safe to let it
 		 * propagate
 		 */
+		return actionSet;
 	}
 
 	/**

Modified: felix/sandbox/pderop/dependencymanager-prototype/dm/src/tracker/BundleTracker.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm/src/tracker/BundleTracker.java?rev=1580585&r1=1580584&r2=1580585&view=diff
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm/src/tracker/BundleTracker.java (original)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm/src/tracker/BundleTracker.java Sun Mar 23 19:59:00 2014
@@ -474,5 +474,32 @@ public class BundleTracker implements Bu
 			customizer.removedBundle((Bundle) item, (BundleEvent) related,
 					object);
 		}
+
+		@Override
+		AbstractCustomizerActionSet createCustomizerActionSet() {
+			return new AbstractCustomizerActionSet() {
+				
+				@Override
+				public void addCustomizerAdded(Object item, Object related, Object object) {
+					customizerAdded(item, related, object);
+				}
+
+				@Override
+				public void addCustomizerModified(Object item, Object related,
+						Object object) {
+					customizerModified(item, related, object);
+				}
+				@Override
+				public void addCustomizerRemoved(Object item, Object related,
+						Object object) {
+					customizerRemoved(item, related, object);
+				}
+						
+				@Override
+				void execute() {
+					// nothing to be done here, since this actionSet executes the actions immediately.
+				}
+			};
+		}
 	}
 }

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=1580585&r1=1580584&r2=1580585&view=diff
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm/src/tracker/ServiceTracker.java (original)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm/src/tracker/ServiceTracker.java Sun Mar 23 19:59:00 2014
@@ -20,6 +20,7 @@ import java.security.PrivilegedAction;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Iterator;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
@@ -444,7 +445,7 @@ public class ServiceTracker implements S
 		}
 		if (references != null) {
 			for (int i = 0; i < references.length; i++) {
-				outgoing.untrack(references[i], null);
+				outgoing.untrack(references[i], null).execute();
 			}
 		}
 		if (DEBUG) {
@@ -797,7 +798,7 @@ public class ServiceTracker implements S
 		if (t == null) { /* if ServiceTracker is not open */
 			return;
 		}
-		t.untrack(reference, null);
+		t.untrack(reference, null).execute();
 	}
 
 	/**
@@ -1052,38 +1053,43 @@ public class ServiceTracker implements S
 		    if (list == null) {
 		        return;
 		    }
-		    Map highestRankedServiceMap = new HashMap(); // <Long, RankedService>
-		    for (int i = 0; i < list.length; i++) {
-		    	ServiceReference sr = (ServiceReference) list[i];
-		    	if (sr != null) {
-			    	Long serviceId = ServiceUtil.getServiceIdAsLong(sr);
-			    	int ranking = ServiceUtil.getRanking(sr);
-			    	
-			    	RankedService rs = (RankedService) highestRankedServiceMap.get(serviceId);
-			    	if (rs == null) {
-			    	    // the service did not exist yet in our map
-			    	    highestRankedServiceMap.put(serviceId, new RankedService(ranking, sr));
-			    	}
-			    	else if (ranking > rs.getRanking()) {
-                        // the service replaces a lower ranked one
-			    	    hide(rs.getServiceReference());
-			    	    rs.update(ranking, sr);
-			    	}
-			    	else {
-                        // the service does NOT replace a lower ranked one
-			    	    hide(sr);
+		    if (m_trackAllAspects) {
+		    	// not hiding aspects
+		    	super.setInitial(list);
+		    } else { 
+			    Map highestRankedServiceMap = new HashMap(); // <Long, RankedService>
+			    for (int i = 0; i < list.length; i++) {
+			    	ServiceReference sr = (ServiceReference) list[i];
+			    	if (sr != null) {
+				    	Long serviceId = ServiceUtil.getServiceIdAsLong(sr);
+				    	int ranking = ServiceUtil.getRanking(sr);
+				    	
+				    	RankedService rs = (RankedService) highestRankedServiceMap.get(serviceId);
+				    	if (rs == null) {
+				    	    // the service did not exist yet in our map
+				    	    highestRankedServiceMap.put(serviceId, new RankedService(ranking, sr));
+				    	}
+				    	else if (ranking > rs.getRanking()) {
+	                        // the service replaces a lower ranked one
+				    	    hide(rs.getServiceReference());
+				    	    rs.update(ranking, sr);
+				    	}
+				    	else {
+	                        // the service does NOT replace a lower ranked one
+				    	    hide(sr);
+				    	}
 			    	}
-		    	}
-		    }
-		    if (highestRankedServiceMap.size() > 0) {
-		        Object[] result = new Object[highestRankedServiceMap.size()];
-		        int index = 0;
-		        for(Iterator it = highestRankedServiceMap.entrySet().iterator(); it.hasNext(); ) {
-		        	Entry entry = (Entry) it.next();
-		        	result[index] = ((RankedService)entry.getValue()).getServiceReference();
-		        	index++;
-		        }
-		        super.setInitial(result);	    	
+			    }
+			    if (highestRankedServiceMap.size() > 0) {
+			        Object[] result = new Object[highestRankedServiceMap.size()];
+			        int index = 0;
+			        for(Iterator it = highestRankedServiceMap.entrySet().iterator(); it.hasNext(); ) {
+			        	Entry entry = (Entry) it.next();
+			        	result[index] = ((RankedService)entry.getValue()).getServiceReference();
+			        	index++;
+			        }
+			        super.setInitial(result);	    	
+			    }
 		    }
 		}
 
@@ -1123,7 +1129,7 @@ public class ServiceTracker implements S
                 case ServiceEvent.MODIFIED :
                     if (listenerFilter != null) { // service listener added with
                         // filter
-                        track(reference, event);
+                        track(reference, event).execute();
                         /*
                          * If the customizer throws an unchecked exception, it
                          * is safe to let it propagate
@@ -1131,14 +1137,14 @@ public class ServiceTracker implements S
                     }
                     else { // service listener added without filter
                         if (filter.match(reference)) {
-                            track(reference, event);
+                            track(reference, event).execute();
                             /*
                              * If the customizer throws an unchecked exception,
                              * it is safe to let it propagate
                              */
                         }
                         else {
-                            untrack(reference, event);
+                            untrack(reference, event).execute();
                             /*
                              * If the customizer throws an unchecked exception,
                              * it is safe to let it propagate
@@ -1148,7 +1154,7 @@ public class ServiceTracker implements S
                     break;
                 case 8 /* ServiceEvent.MODIFIED_ENDMATCH */ :
                 case ServiceEvent.UNREGISTERING :
-                    untrack(reference, event);
+                    untrack(reference, event).execute();
                     /*
                      * If the customizer throws an unchecked exception, it is
                      * safe to let it propagate
@@ -1156,6 +1162,10 @@ public class ServiceTracker implements S
                     break;
             }
         }
+        
+        private boolean isModifiedEndmatchSupported() {
+        	return listenerFilter != null;
+        }
 		
 		public void serviceChangedHideAspects(final ServiceEvent event) {
 			/*
@@ -1176,119 +1186,43 @@ public class ServiceTracker implements S
 			switch (event.getType()) {
 				case ServiceEvent.REGISTERED :
 				case ServiceEvent.MODIFIED :
-				    ServiceReference higher = null;
-				    ServiceReference lower = null;
-				    ServiceReference sr = highestTrackedCache(sid);
-				    if (sr != null) {
+				    ServiceReference higherRankedReference = null;
+				    ServiceReference lowerRankedReference = null;
+				    ServiceReference highestTrackedReference = highestTrackedCache(sid);
+				    if (highestTrackedReference != null) {
 				        int ranking = ServiceUtil.getRanking(reference);
-				        int trackedRanking = ServiceUtil.getRanking(sr);
-				        if (ranking > trackedRanking) {
+				        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(sr));
+				                System.out.println("ServiceTracker.Tracked.serviceChanged[" + event.getType() + "]: Found a higher ranked aspect: " + ServiceUtil.toString(reference) + " vs " + ServiceUtil.toString(highestTrackedReference));
 				            }
-				            higher = sr;
+				            higherRankedReference = highestTrackedReference;
 				        }
-				        else if (ranking < trackedRanking) {
+				        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(sr));
+                                System.out.println("ServiceTracker.Tracked.serviceChanged[" + event.getType() + "]: Found a lower ranked aspect: " + ServiceUtil.toString(reference) + " vs " + ServiceUtil.toString(highestTrackedReference));
                             }
-				            lower = sr;
+				            lowerRankedReference = highestTrackedReference;
 				        }
 				    }
 				    
-					if (listenerFilter != null) { // service listener added with filter
-					    if (lower != null) {
-					        hide(reference);
-					    }
-					    else {
-					        try {
-					            track(reference, event);
-					        }
-					        finally {
-    					        if (higher != null) {
-    					            try {
-    					                untrack(higher, null);
-    					            }
-    					            finally {
-    					                hide(higher);
-    					            }
-    					        }
-					        }
-					    }
-						/*
-						 * If the customizer throws an unchecked exception, it
-						 * is safe to let it propagate
-						 */
+					if (isModifiedEndmatchSupported()) { // either registered or modified
+					    registerOrUpdate(event, reference, higherRankedReference, lowerRankedReference);
 					}
 					else { // service listener added without filter
 						if (filter.match(reference)) {
-	                        if (lower != null) {
-	                            hide(reference);
-	                        }
-	                        else {
-	                            try {
-	                                track(reference, event);
-	                            }
-	                            finally {
-    	                            if (higher != null) {
-    	                                try {
-    	                                    untrack(higher, null);
-    	                                }
-    	                                finally {
-    	                                    hide(higher);
-    	                                }
-    	                            }
-	                            }
-	                        }
-							/*
-							 * If the customizer throws an unchecked exception,
-							 * it is safe to let it propagate
-							 */
+	                        registerOrUpdate(event, reference, higherRankedReference, lowerRankedReference);
 						}
 						else {
-		                    ServiceReference ht = highestTrackedCache(sid);
-		                    if (reference.equals(ht)) {
-		                        try {
-		                            ServiceReference hh = highestHiddenCache(sid);
-		                            if (hh != null) {
-		                                unhide(hh);
-		                                track(hh, null);
-		                            }
-		                        }
-		                        finally {
-		                            untrack(reference, event);
-		                        }
-		                    }
-		                    else {
-		                        unhide(reference);
-		                    }
-							/*
-							 * If the customizer throws an unchecked exception,
-							 * it is safe to let it propagate
-							 */
+		                    unregister(event, reference, sid);
 						}
 					}
 					break;
-                case 8 /* ServiceEvent.MODIFIED_ENDMATCH */ :
+                case 8 /* ServiceEvent.MODIFIED_ENDMATCH */ : // handle as unregister
 				case ServiceEvent.UNREGISTERING :
-				    ServiceReference ht = highestTrackedCache(sid);
-				    if (reference.equals(ht)) {
-				        try {
-				            ServiceReference hh = highestHiddenCache(sid);
-    				        if (hh != null) {
-    				            unhide(hh);
-    				            track(hh, null);
-    				        }
-				        }
-				        finally {
-				            untrack(reference, event);
-				        }
-				    }
-				    else {
-				        unhide(reference);
-				    }
+					unregister(event, reference, sid);
 					/*
 					 * If the customizer throws an unchecked exception, it is
 					 * safe to let it propagate
@@ -1297,6 +1231,50 @@ public class ServiceTracker implements S
 			}
 		}
 
+		private void registerOrUpdate(final ServiceEvent event,
+				final ServiceReference reference, ServiceReference higher,
+				ServiceReference lower) {
+			if (lower != null) {
+			    hide(reference);
+			}
+			else {
+				AbstractCustomizerActionSet 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
+			 */
+		}
+
+		private void unregister(final ServiceEvent event,
+				final ServiceReference reference, long sid) {
+			ServiceReference ht = highestTrackedCache(sid);
+			if (reference.equals(ht)) {
+		        ServiceReference hh = highestHiddenCache(sid);
+		        AbstractCustomizerActionSet actionSet = null;
+		        if (hh != null) {
+		            unhide(hh);
+		            actionSet = track(hh, null);
+		        }
+		        if (actionSet == null) {
+		        	actionSet = untrack(reference, event);
+		        } else {
+		        	actionSet.appendActionSet(untrack(reference, event));
+		        }
+		        actionSet.execute();
+			}
+			else {
+			    unhide(reference);
+			}
+		}
+		
+		
+
 		/**
 		 * Increment the tracking count and tell the tracker there was a
 		 * modification.
@@ -1352,7 +1330,7 @@ public class ServiceTracker implements S
 			customizer.removedService((ServiceReference) item, object);
 		}
 		
-		class HashMapCache extends HashMap {
+		class HashMapCache extends LinkedHashMap {
 		    public Object put(Object key, Object value) {
 		        addHighestTrackedCache((ServiceReference) key);
 		        return super.put(key, value);
@@ -1376,6 +1354,44 @@ public class ServiceTracker implements S
 		        super.clear();
 		    }
 		}
+
+		@Override
+		AbstractCustomizerActionSet createCustomizerActionSet() {
+			// 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
+				void execute() {
+					// inspect the actions and check whether we should perform a swap
+					List<CustomizerAction> actions = getActions();
+					if (actions.size() == 2 && actions.get(0).getType() == Type.ADDED && actions.get(1).getType() == Type.REMOVED) {
+						// ignore related
+						// item = ServiceReference
+						// object = service
+						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
+						for (CustomizerAction action : getActions()) {
+							try {
+								switch (action.getType()) {
+									case ADDED: 
+										customizerAdded(action.getItem(), action.getRelated(), action.getObject());
+										break;
+									case MODIFIED:
+										customizerModified(action.getItem(), action.getRelated(), action.getObject());
+										break;
+									case REMOVED:
+										customizerRemoved(action.getItem(), action.getRelated(), action.getObject());
+								}
+							} catch (Exception e) {
+								// just continue. log messages will be printed elsewhere.
+							}
+						}
+					}
+				}
+			};
+		}
+
 	}
 
 	/**
@@ -1420,4 +1436,15 @@ public class ServiceTracker implements S
 			return m_serviceReference;
 		}
 	}
+
+	@Override
+	public void swappedService(ServiceReference reference, Object service,
+			ServiceReference newReference, Object newService) {
+		
+	}
+
+	// Package private, used for unit testing Tracked
+	Tracked getTracked() {
+		return tracked;
+	}
 }

Modified: felix/sandbox/pderop/dependencymanager-prototype/dm/src/tracker/ServiceTrackerCustomizer.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm/src/tracker/ServiceTrackerCustomizer.java?rev=1580585&r1=1580584&r2=1580585&view=diff
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm/src/tracker/ServiceTrackerCustomizer.java (original)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm/src/tracker/ServiceTrackerCustomizer.java Sun Mar 23 19:59:00 2014
@@ -81,6 +81,25 @@ public interface ServiceTrackerCustomize
 	 * @param service The service object for the specified referenced service.
 	 */
 	public void modifiedService(ServiceReference reference, Object service);
+	
+	/**
+	 * A service tracked by the <code>ServiceTracker</code> has an aspect service 
+	 * added or removed for a tracked service.
+	 * 
+	 * <p>
+	 * This method is called when an aspect service has been either added or removed 
+	 * for a tracked service. This method will only be called when there's a new 
+	 * highest ranked service as result of adding or removal of the aspect service.
+	 * In this case the previously highest ranked service is 'swapped' for the new
+	 * highest ranked service ensuring the client always gets the highest ranked
+	 * aspect. 
+	 * 
+	 * @param reference The reference for the old highest ranked service.
+	 * @param service The service object for the old highest ranked service.
+	 * @param newReference The reference to the new highest ranked service.
+	 * @param newService The service object for the new highest ranked service.
+	 */
+	public void swappedService(ServiceReference reference, Object service, ServiceReference newReference, Object newService);
 
 	/**
 	 * A service tracked by the <code>ServiceTracker</code> has been removed.

Added: felix/sandbox/pderop/dependencymanager-prototype/dm/test/tracker/TrackedTest.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm/test/tracker/TrackedTest.java?rev=1580585&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm/test/tracker/TrackedTest.java (added)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm/test/tracker/TrackedTest.java Sun Mar 23 19:59:00 2014
@@ -0,0 +1,290 @@
+package tracker;
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+
+import org.junit.Test;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceReference;
+
+import tracker.ServiceTracker.Tracked;
+import dm.DependencyManager;
+
+public class TrackedTest {
+
+	@Test
+	public void testSetInitialHideAspects() {
+		System.out.println("testSetInitialHideAspects");
+		TestCustomizer customizer = new TestCustomizer();
+		
+		ServiceTracker tracker = new TestTracker(customizer);
+		tracker.open();
+		Tracked tracked = tracker.getTracked();
+
+		Object[] initialReferences = new Object[] {
+				createServiceReference(1L),
+				createServiceReference(2L, 1L, 10),
+				createServiceReference(3L),
+				createServiceReference(4L, 1L, 5),
+				createServiceReference(5L, 3L, 5),
+		};
+		tracked.setInitial(initialReferences);
+		tracked.trackInitial();
+		assertArrayEquals(new Long[] { 2L, 5L }, customizer.getServiceReferenceIds());
+	}
+	
+	@Test
+	public void testUnHideAspect() {
+		System.out.println("testUnhideAspect");
+		TestCustomizer customizer = new TestCustomizer();
+		
+		ServiceTracker tracker = new TestTracker(customizer);
+		tracker.open();
+		Tracked tracked = tracker.getTracked();
+		
+		ServiceReference[] initialReferences = new ServiceReference[] {
+				createServiceReference(1L),
+				createServiceReference(2L, 1L, 10),
+				createServiceReference(3L),
+				createServiceReference(4L, 1L, 5),
+				createServiceReference(5L, 3L, 5),
+		};
+		tracked.setInitial(initialReferences);
+		tracked.trackInitial();
+		assertArrayEquals(new Long[] { 2L, 5L }, customizer.getServiceReferenceIds());
+		
+		// create a service event that unregisters service with id 2, we would expect it to be swapped with 4.
+		ServiceEvent event = new ServiceEvent(ServiceEvent.UNREGISTERING, initialReferences[1]);
+		tracked.serviceChanged(event);
+		assertArrayEquals(new Long[] { 5L, 4L }, customizer.getServiceReferenceIds());
+		// create a service event that unregisters service with id 4, we would expect it to be swapped with 1.
+		event = new ServiceEvent(ServiceEvent.UNREGISTERING, initialReferences[3]);
+		tracked.serviceChanged(event);
+		assertArrayEquals(new Long[] { 5L, 1L }, customizer.getServiceReferenceIds());
+	}	
+	
+	@Test
+	public void testHideAspect() {
+		System.out.println("testHideAspect");
+		TestCustomizer customizer = new TestCustomizer();
+		
+		ServiceTracker tracker = new TestTracker(customizer);
+		tracker.open();
+		Tracked tracked = tracker.getTracked();
+		
+		ServiceReference[] initialReferences = new ServiceReference[] {
+				createServiceReference(1L),
+				createServiceReference(2L, 1L, 10),
+				createServiceReference(3L),
+				createServiceReference(4L, 1L, 5),
+				createServiceReference(5L, 3L, 5),
+		};
+		tracked.setInitial(initialReferences);
+		tracked.trackInitial();
+		assertArrayEquals(new Long[] { 2L, 5L }, customizer.getServiceReferenceIds());
+		
+		// create a service event that registers another but lower ranked aspect for service with id 1. 
+		ServiceReference newReference = createServiceReference(6L, 1L, 8);
+		ServiceEvent event = new ServiceEvent(ServiceEvent.REGISTERED, newReference);
+		tracked.serviceChanged(event);
+		assertArrayEquals(new Long[] { 2L, 5L }, customizer.getServiceReferenceIds());
+		
+		// create a service event that unregisters service with id 2, we would expect it to be swapped with 6.
+		event = new ServiceEvent(ServiceEvent.UNREGISTERING, initialReferences[1]);
+		tracked.serviceChanged(event);
+		assertArrayEquals(new Long[] { 5L, 6L }, customizer.getServiceReferenceIds());
+		
+		// create a service event that unregisters service with id 6, we would expect it to be swapped with 4.
+		event = new ServiceEvent(ServiceEvent.UNREGISTERING, newReference);
+		tracked.serviceChanged(event);
+		assertArrayEquals(new Long[] { 5L, 4L }, customizer.getServiceReferenceIds());	
+		
+		// create a service event that registers a higher ranked aspect for service with id 1.
+		ServiceReference higherRankedReference = createServiceReference(7L, 1L, 15);
+		ServiceEvent addHigherRankedEvent = new ServiceEvent(ServiceEvent.REGISTERED, higherRankedReference);
+		tracked.serviceChanged(addHigherRankedEvent);
+		assertArrayEquals(new Long[] { 5L, 7L }, customizer.getServiceReferenceIds());	
+	}		
+	
+	@Test
+	public void testSetInitialTrackAspects() {
+		System.out.println("testSetInitialTrackAspects");
+		TestCustomizer customizer = new TestCustomizer();
+		
+		ServiceTracker tracker = new TestTracker(customizer);
+		tracker.open(false, true);
+		Tracked tracked = tracker.getTracked();
+		
+		Object[] initialReferences = new Object[] {
+				createServiceReference(1L),
+				createServiceReference(2L, 1L, 10),
+				createServiceReference(3L, 1L, 5)
+		};
+		tracked.setInitial(initialReferences);
+		tracked.trackInitial();
+		assertArrayEquals(new Long[] { 1L, 2L, 3L }, customizer.getServiceReferenceIds());
+	}	
+	
+	private static BundleContext createBundleContext() {
+		BundleContext context = mock(BundleContext.class);
+		when(context.getProperty(Constants.FRAMEWORK_VERSION)).thenReturn(null);
+		return context;
+	}
+	
+	private ServiceReference createServiceReference(Long serviceId) {
+		return createServiceReference(serviceId, null, null);
+	}
+
+	private ServiceReference createServiceReference(Long serviceId, Long aspectId, Integer ranking) {
+		return new TestServiceReference(serviceId, aspectId, ranking);
+	}
+
+	class TestTracker extends ServiceTracker {
+
+		public TestTracker(ServiceTrackerCustomizer customizer) {
+			super(createBundleContext(), "(objectClass=*)", customizer);
+		}
+		
+	}
+	
+	class TestCustomizer implements ServiceTrackerCustomizer {
+		
+		List<ServiceReference> serviceReferences = new ArrayList<>();
+
+		@Override
+		public Object addingService(ServiceReference reference) {
+			System.out.println("adding service: " + reference);
+			return new Object();
+		}
+
+		@Override
+		public void addedService(ServiceReference reference, Object service) {
+			System.out.println("added service: " + reference);
+			serviceReferences.add(reference);
+		}
+
+		@Override
+		public void modifiedService(ServiceReference reference, Object service) {
+			System.out.println("modified service: " + reference);
+		}
+
+		@Override
+		public void swappedService(ServiceReference reference, Object service,
+				ServiceReference newReference, Object newService) {
+			System.out.println("swapped service: " + reference);
+			serviceReferences.remove(reference);
+			serviceReferences.add(newReference);
+		}
+
+		@Override
+		public void removedService(ServiceReference reference, Object service) {
+			System.out.println("removed service: " + reference);
+			serviceReferences.remove(reference);
+		}
+		
+		public Long[] getServiceReferenceIds() {
+			Long[] ids = new Long[serviceReferences.size()];
+			for (int i = 0; i < serviceReferences.size(); i++) {
+				ids[i] = (Long) serviceReferences.get(i).getProperty(Constants.SERVICE_ID);
+			}
+			return ids;
+		}
+		
+	}
+	
+	class TestServiceReference implements ServiceReference {
+		
+		Properties props = new Properties();
+
+		public TestServiceReference(Long serviceId, Long aspectId,
+				Integer ranking) {
+			props.put(Constants.SERVICE_ID, serviceId);
+			if (aspectId != null) {
+				props.put(DependencyManager.ASPECT, aspectId);
+			}
+			if (ranking != null) {
+				props.put(Constants.SERVICE_RANKING, ranking);
+			}
+		}
+
+		@Override
+		public Object getProperty(String key) {
+			return props.get(key);
+		}
+
+		@Override
+		public String[] getPropertyKeys() {
+			return props.keySet().toArray(new String[]{});
+		}
+
+		@Override
+		public Bundle getBundle() {
+			return null;
+		}
+
+		@Override
+		public Bundle[] getUsingBundles() {
+			return null;
+		}
+
+		@Override
+		public boolean isAssignableTo(Bundle bundle, String className) {
+			return false;
+		}
+
+		@Override
+		public int compareTo(Object reference) // Kindly borrowed from the Apache Felix ServiceRegistrationImpl.ServiceReferenceImpl
+        {
+            ServiceReference other = (ServiceReference) reference;
+
+            Long id = (Long) getProperty(Constants.SERVICE_ID);
+            Long otherId = (Long) other.getProperty(Constants.SERVICE_ID);
+
+            if (id.equals(otherId))
+            {
+                return 0; // same service
+            }
+
+            Object rankObj = getProperty(Constants.SERVICE_RANKING);
+            Object otherRankObj = other.getProperty(Constants.SERVICE_RANKING);
+
+            // If no rank, then spec says it defaults to zero.
+            rankObj = (rankObj == null) ? new Integer(0) : rankObj;
+            otherRankObj = (otherRankObj == null) ? new Integer(0) : otherRankObj;
+
+            // If rank is not Integer, then spec says it defaults to zero.
+            Integer rank = (rankObj instanceof Integer)
+                ? (Integer) rankObj : new Integer(0);
+            Integer otherRank = (otherRankObj instanceof Integer)
+                ? (Integer) otherRankObj : new Integer(0);
+
+            // Sort by rank in ascending order.
+            if (rank.compareTo(otherRank) < 0)
+            {
+                return -1; // lower rank
+            }
+            else if (rank.compareTo(otherRank) > 0)
+            {
+                return 1; // higher rank
+            }
+
+            // If ranks are equal, then sort by service id in descending order.
+            return (id.compareTo(otherId) < 0) ? 1 : -1;
+        }
+
+		@Override
+		public String toString() {
+			return "TestServiceReference [props=" + props + "]";
+		}
+
+	}
+	
+}