Added several performance improvements, most of them still experimental, plus started to prepare for a release.

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1094895 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/dependencymanager/core/DEPENDENCIES b/dependencymanager/core/DEPENDENCIES
new file mode 100644
index 0000000..b4594f1
--- /dev/null
+++ b/dependencymanager/core/DEPENDENCIES
@@ -0,0 +1,25 @@
+Apache Felix Dependency Manager
+Copyright 2011 The Apache Software Foundation
+
+
+I. Included Third-Party Software
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+Licensed under the Apache License 2.0.
+
+This product includes software developed at
+The OSGi Alliance (http://www.osgi.org/).
+Copyright (c) OSGi Alliance (2000, 2009).
+Licensed under the Apache License 2.0.
+
+II. Used Third-Party Software
+
+This product uses software developed at
+The OSGi Alliance (http://www.osgi.org/).
+Copyright (c) OSGi Alliance (2000, 2009).
+Licensed under the Apache License 2.0.
+
+III. Overall License Summary
+
+- Apache License 2.0
diff --git a/dependencymanager/core/NOTICE b/dependencymanager/core/NOTICE
index cfc3633..754b974 100644
--- a/dependencymanager/core/NOTICE
+++ b/dependencymanager/core/NOTICE
@@ -1,26 +1,7 @@
 Apache Felix Dependency Manager
-Copyright 2009 The Apache Software Foundation
+Copyright 2011 The Apache Software Foundation
 
 
-I. Included Software
-
 This product includes software developed at
 The Apache Software Foundation (http://www.apache.org/).
 Licensed under the Apache License 2.0.
-
-This product includes software developed at
-The OSGi Alliance (http://www.osgi.org/).
-Copyright (c) OSGi Alliance (2000, 2007).
-Licensed under the Apache License 2.0.
-
-
-II. Used Software
-
-This product uses software developed at
-The OSGi Alliance (http://www.osgi.org/).
-Copyright (c) OSGi Alliance (2000, 2007).
-Licensed under the Apache License 2.0.
-
-
-III. License Summary
-- Apache License 2.0
diff --git a/dependencymanager/core/pom.xml b/dependencymanager/core/pom.xml
index e6bae60..d5cc3ec 100644
--- a/dependencymanager/core/pom.xml
+++ b/dependencymanager/core/pom.xml
@@ -19,28 +19,66 @@
 	<modelVersion>4.0.0</modelVersion>
 
 	<parent>
-		<groupId>org.apache.felix</groupId>
-		<artifactId>dependencymanager-reactor</artifactId>
-		<version>3.0.0-SNAPSHOT</version>
+	    <groupId>org.apache.felix</groupId>
+	    <artifactId>felix-parent</artifactId>
+	    <version>1.2.0</version>
+	    <relativePath>../../pom/pom.xml</relativePath>
 	</parent>
 
+	<properties>
+		<felix.dependencymanager.version>3.0.0-SNAPSHOT</felix.dependencymanager.version>
+		<osgi.version>4.2.0</osgi.version>
+		<pax.exam.version>1.2.0</pax.exam.version>
+	</properties>
+
 	<name>Apache Felix Dependency Manager</name>
 	<artifactId>org.apache.felix.dependencymanager</artifactId>
+	<version>${felix.dependencymanager.version}</version>
 	<packaging>bundle</packaging>
 
 	<dependencies>
 		<dependency>
 			<groupId>org.osgi</groupId>
 			<artifactId>org.osgi.core</artifactId>
+			<version>${osgi.version}</version>
 		</dependency>
 		<dependency>
 			<groupId>org.osgi</groupId>
 			<artifactId>org.osgi.compendium</artifactId>
+			<version>${osgi.version}</version>
 		</dependency>
+		
+		<!-- please remove me again -->
+        <dependency>
+            <groupId>org.eclipse.equinox</groupId>
+            <artifactId>osgi</artifactId>
+			<version>3.1.1</version>
+            <scope>provided</scope>
+        </dependency>
+		
 	</dependencies>
 
 	<build>
+		<pluginManagement>
+			<plugins>
+				<plugin>
+					<groupId>org.apache.felix</groupId>
+					<artifactId>maven-bundle-plugin</artifactId>
+					<version>1.4.0</version>
+					<extensions>true</extensions>
+				</plugin>
+			</plugins>
+		</pluginManagement>
+
 		<plugins>
+			<!-- by default the dependency manager will run on any OSGi execution environment -->
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-compiler-plugin</artifactId>
+				<configuration>
+					<target>1.3</target>
+				</configuration>
+			</plugin>
 			<plugin>
 				<groupId>org.apache.felix</groupId>
 				<artifactId>maven-bundle-plugin</artifactId>
@@ -52,7 +90,7 @@
 						<Bundle-Vendor>The Apache Software Foundation</Bundle-Vendor>
 						<Export-Package>org.apache.felix.dm;version="3.0.0",org.apache.felix.dm.tracker;version="3.0.0"</Export-Package>
 						<Import-Package>!org.apache.felix.dm,*</Import-Package>
-						<Private-Package>org.apache.felix.dm.impl,org.apache.felix.dm.impl.dependencies,org.apache.felix.dm.impl.tracker,org.apache.felix.dm.impl.metatype</Private-Package>
+						<Private-Package>org.apache.felix.dm.impl, org.apache.felix.dm.impl.*, org.apache.felix.dm.index</Private-Package>
 						<!-- Uncomment this line to include source code in the bundle.
 						<Include-Resource>src/main/java</Include-Resource>
                         -->
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dm/DependencyManager.java b/dependencymanager/core/src/main/java/org/apache/felix/dm/DependencyManager.java
index 62910bc..5ac4974 100644
--- a/dependencymanager/core/src/main/java/org/apache/felix/dm/DependencyManager.java
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dm/DependencyManager.java
@@ -35,7 +35,10 @@
 import org.apache.felix.dm.impl.dependencies.ServiceDependencyImpl;
 import org.apache.felix.dm.impl.dependencies.TemporalServiceDependencyImpl;
 import org.apache.felix.dm.impl.metatype.PropertyMetaDataImpl;
+import org.apache.felix.dm.index.MultiPropertyExactFilter;
+import org.apache.felix.dm.index.ServiceRegistryCache;
 import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkUtil;
 
 /**
  * The dependency manager manages all components and their dependencies. Using 
@@ -49,6 +52,7 @@
  */
 public class DependencyManager {
     public static final String ASPECT = "org.apache.felix.dependencymanager.aspect";
+    public static final String SERVICEREGISTRY_CACHE_INDICES = "dm.index"; // TODO rename
     private final BundleContext m_context;
     private final Logger m_logger;
     private List m_services = Collections.synchronizedList(new ArrayList());
@@ -66,10 +70,74 @@
     }
     
     DependencyManager(BundleContext context, Logger logger) {
-        m_context = context;
+        m_context = createContext(context);
         m_logger = logger;
     }
 
+    // service registry cache
+    private static ServiceRegistryCache m_serviceRegistryCache;
+    static {
+        String index = System.getProperty(SERVICEREGISTRY_CACHE_INDICES);
+        m_serviceRegistryCache = new ServiceRegistryCache(FrameworkUtil.getBundle(DependencyManager.class).getBundleContext());
+        m_serviceRegistryCache.open(); // TODO close it somewhere
+        if (index != null) {
+            String[] props = index.split(";");
+            for (int i = 0; i < props.length; i++) {
+                String[] propList = props[i].split(",");
+                m_serviceRegistryCache.addFilterIndex(new MultiPropertyExactFilter(propList));
+//                System.out.println("DM: Creating index on " + props[i]);
+            }
+        }
+        else {
+//            System.out.println("DM: Property 'dm.index' not found, not setting indices.");
+        }
+
+    }
+    
+    private BundleContext createContext(BundleContext context) {
+        if (m_serviceRegistryCache != null) {
+//            System.out.println("DM: Enabling bundle context interceptor for bundle #" + context.getBundle().getBundleId());
+            return m_serviceRegistryCache.createBundleContextInterceptor(context);
+        }
+        else {
+            return context;
+        }
+    }
+
+//    private BundleContext createContextX(BundleContext context) {
+//        System.out.println("DM: Enabling bundle context interceptor for bundle #" + context.getBundle().getBundleId());
+//        BundleContextInterceptor result = new BundleContextInterceptor(context);
+//        if (BundleContextInterceptor.indices() == 0) {
+//            String index = System.getProperty("dm.index");
+//            if (index != null) {
+//                String[] props = index.split(";");
+//                for (int i = 0; i < props.length; i++) {
+//                    String[] propList = props[i].split(",");
+//                    MultiPropertyExactFilter filter;
+//                    try {
+//                        filter = new MultiPropertyExactFilter(result, propList);
+//                        m_filterIndices.add(filter);
+//                        System.out.println("DM: Creating index on " + props[i]);
+//                    }
+//                    catch (InvalidSyntaxException e) {
+//                        // TODO Auto-generated catch block
+//                        e.printStackTrace();
+//                    }
+//                }
+//            }
+//            else {
+//                System.out.println("DM: Property 'dm.index' not found, not setting indices.");
+//            }
+//        }
+//        System.out.println("DM: Indices created, setting them on the interceptor.");
+//        Iterator iterator = m_filterIndices.iterator();
+//        while (iterator.hasNext()) {
+//            FilterIndex filter = (FilterIndex) iterator.next();
+//            result.addFilterIndex(filter);
+//        }
+//        return result;
+//    }
+
     /**
      * Adds a new service to the dependency manager. After the service was added
      * it will be started immediately.
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dm/InvocationUtil.java b/dependencymanager/core/src/main/java/org/apache/felix/dm/InvocationUtil.java
index 64b54c1..aae4cea 100644
--- a/dependencymanager/core/src/main/java/org/apache/felix/dm/InvocationUtil.java
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dm/InvocationUtil.java
@@ -4,8 +4,12 @@
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
 import java.lang.reflect.Proxy;
+import java.util.Arrays;
+import java.util.HashMap;
 
 public class InvocationUtil {
+    private static final HashMap m_methodCache = new HashMap();
+    
     public static Object invokeCallbackMethod(Object instance, String methodName, Class[][] signatures, Object[][] parameters) throws NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {
         Class currentClazz = instance.getClass();
         while (currentClazz != null) {
@@ -38,17 +42,83 @@
         Method m = null;
         for (int i = 0; i < signatures.length; i++) {
             Class[] signature = signatures[i];
-            try {
-                m = clazz.getDeclaredMethod(name, signature);
-                if (!(isSuper && Modifier.isPrivate(m.getModifiers()))) {
-                    m.setAccessible(true);
-                    return m.invoke(object, parameters[i]);
-                }
-            }
-            catch (NoSuchMethodException e) {
-                // ignore this and keep looking
+            m = getDeclaredMethod(clazz, name, signature, isSuper);
+            if (m != null) {
+                return m.invoke(object, parameters[i]);
             }
         }
         throw new NoSuchMethodException(name);
     }
+    
+    private static Method getDeclaredMethod(Class clazz, String name, Class[] signature, boolean isSuper) {
+        // first check our cache
+        Key key = new Key(clazz, name, signature);
+        Method m = null;
+        synchronized (m_methodCache) {
+            if (m_methodCache.containsKey(key)) {
+                m = (Method) m_methodCache.get(key);
+                return m;
+            }
+        }
+        // then do a lookup
+        try {
+            m = clazz.getDeclaredMethod(name, signature);
+            if (!(isSuper && Modifier.isPrivate(m.getModifiers()))) {
+                m.setAccessible(true);
+            }
+        }
+        catch (NoSuchMethodException e) {
+            // ignore
+        }
+        synchronized (m_methodCache) {
+            m_methodCache.put(key, m);
+        }
+        return m;
+    }
+    
+    public static class Key {
+        private final Class m_clazz;
+        private final String m_name;
+        private final Class[] m_signature;
+
+        public Key(Class clazz, String name, Class[] signature) {
+            m_clazz = clazz;
+            m_name = name;
+            m_signature = signature;
+        }
+
+        public int hashCode() {
+            final int prime = 31;
+            int result = 1;
+            result = prime * result + ((m_clazz == null) ? 0 : m_clazz.hashCode());
+            result = prime * result + ((m_name == null) ? 0 : m_name.hashCode());
+            result = prime * result + Arrays.hashCode(m_signature);
+            return result;
+        }
+
+        public boolean equals(Object obj) {
+            if (this == obj)
+                return true;
+            if (obj == null)
+                return false;
+            if (getClass() != obj.getClass())
+                return false;
+            Key other = (Key) obj;
+            if (m_clazz == null) {
+                if (other.m_clazz != null)
+                    return false;
+            }
+            else if (!m_clazz.equals(other.m_clazz))
+                return false;
+            if (m_name == null) {
+                if (other.m_name != null)
+                    return false;
+            }
+            else if (!m_name.equals(other.m_name))
+                return false;
+            if (!Arrays.equals(m_signature, other.m_signature))
+                return false;
+            return true;
+        }
+    }
 }
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dm/ServiceUtil.java b/dependencymanager/core/src/main/java/org/apache/felix/dm/ServiceUtil.java
index 8ae49de..d5bf333 100644
--- a/dependencymanager/core/src/main/java/org/apache/felix/dm/ServiceUtil.java
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dm/ServiceUtil.java
@@ -51,13 +51,17 @@
      * @return the service ID
      */
     public static long getServiceId(ServiceReference ref) {
-        Long sid = (Long) ref.getProperty(Constants.SERVICE_ID);
+        return getServiceIdObject(ref).longValue();
+    }
+    
+    public static Long getServiceIdObject(ServiceReference ref) {
         Long aid = (Long) ref.getProperty(DependencyManager.ASPECT);
         if (aid != null) {
-            return aid.longValue();
+            return aid;
         }
+        Long sid = (Long) ref.getProperty(Constants.SERVICE_ID);
         if (sid != null) {
-            return sid.longValue();
+            return sid;
         }
         throw new IllegalArgumentException("Invalid service reference, no service ID found");
     }
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dm/index/BundleContextInterceptor.java b/dependencymanager/core/src/main/java/org/apache/felix/dm/index/BundleContextInterceptor.java
new file mode 100644
index 0000000..79e236e
--- /dev/null
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dm/index/BundleContextInterceptor.java
@@ -0,0 +1,97 @@
+package org.apache.felix.dm.index;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
+
+public class BundleContextInterceptor extends BundleContextInterceptorBase {
+    private final ServiceRegistryCache m_cache;
+
+    public BundleContextInterceptor(ServiceRegistryCache cache, BundleContext context) {
+        super(context);
+        m_cache = cache;
+    }
+
+    public void addServiceListener(ServiceListener listener, String filter) throws InvalidSyntaxException {
+        FilterIndex filterIndex = m_cache.hasFilterIndexFor(null, filter);
+        if (filterIndex != null) {
+            filterIndex.addServiceListener(listener, filter);
+        }
+        else {
+            m_context.addServiceListener(listener, filter);
+//            super.addServiceListener(listener, filter);
+        }
+    }
+
+    public void addServiceListener(ServiceListener listener) {
+        FilterIndex filterIndex = m_cache.hasFilterIndexFor(null, null);
+        if (filterIndex != null) {
+            filterIndex.addServiceListener(listener, null);
+        }
+        else {
+            m_context.addServiceListener(listener);
+//            super.addServiceListener(listener);
+        }
+    }
+
+    public void removeServiceListener(ServiceListener listener) {
+        FilterIndex filterIndex = m_cache.hasFilterIndexFor(null, null);
+        if (filterIndex != null) {
+            filterIndex.removeServiceListener(listener);
+        }
+        else {
+            m_context.removeServiceListener(listener);
+//            super.removeServiceListener(listener);
+        }
+    }
+
+    public ServiceReference[] getServiceReferences(String clazz, String filter) throws InvalidSyntaxException {
+        // first we ask the cache if there is an index for our request (class and filter combination)
+        FilterIndex filterIndex = m_cache.hasFilterIndexFor(clazz, filter);
+        if (filterIndex != null) {
+            ServiceReference[] result = filterIndex.getAllServiceReferences(clazz, filter);
+            // TODO filter for assignability
+            return result;
+        }
+        else {
+            // if they don't know, we ask the real bundle context instead
+            return m_context.getServiceReferences(clazz, filter);
+        }
+        
+    }
+
+    public ServiceReference[] getAllServiceReferences(String clazz, String filter) throws InvalidSyntaxException {
+        // TODO implement
+        return m_context.getAllServiceReferences(clazz, filter);
+    }
+
+    public ServiceReference getServiceReference(String clazz) {
+        // TODO implement
+        return m_context.getServiceReference(clazz);
+    }
+
+    public void serviceChanged(ServiceEvent event) {
+        m_cache.serviceChangedForFilterIndices(event);
+//        Entry[] entries = synchronizeCollection();
+//        for (int i = 0; i < entries.length; i++) {
+//            Entry serviceListenerFilterEntry = entries[i];
+//            ServiceListener serviceListener = (ServiceListener) serviceListenerFilterEntry.getKey();
+//            String filter = (String) serviceListenerFilterEntry.getValue();
+//            if (filter == null) {
+//                serviceListener.serviceChanged(event);
+//            }
+//            else {
+//                try {
+//                    if (m_context.createFilter(filter).match(event.getServiceReference())) {
+//                        serviceListener.serviceChanged(event);
+//                    }
+//                }
+//                catch (InvalidSyntaxException e) {
+//                    e.printStackTrace();
+//                }
+//            }
+//        }
+    }
+}
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dm/index/BundleContextInterceptorBase.java b/dependencymanager/core/src/main/java/org/apache/felix/dm/index/BundleContextInterceptorBase.java
new file mode 100644
index 0000000..598a676
--- /dev/null
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dm/index/BundleContextInterceptorBase.java
@@ -0,0 +1,141 @@
+package org.apache.felix.dm.index;
+
+import java.io.File;
+import java.io.InputStream;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.BundleListener;
+import org.osgi.framework.Filter;
+import org.osgi.framework.FrameworkListener;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+
+/** Base class for bundle context interceptors that keep track of service listeners and delegate incoming changes to them. */
+public abstract class BundleContextInterceptorBase implements BundleContext, ServiceListener {
+    protected final BundleContext m_context;
+    /** Keeps track of all service listeners and their optional filters. */
+    private final Map /* <ServiceListener, String> */m_serviceListenerFilterMap = new HashMap();
+    private long m_currentVersion = 0;
+    private long m_entryVersion = -1;
+    private Entry[] m_serviceListenerFilterMapEntries;
+
+    public BundleContextInterceptorBase(BundleContext context) {
+        m_context = context;
+    }
+
+    public String getProperty(String key) {
+        return m_context.getProperty(key);
+    }
+
+    public Bundle getBundle() {
+        return m_context.getBundle();
+    }
+
+    public Bundle installBundle(String location) throws BundleException {
+        return m_context.installBundle(location);
+    }
+
+    public Bundle installBundle(String location, InputStream input) throws BundleException {
+        return m_context.installBundle(location, input);
+    }
+
+    public Bundle getBundle(long id) {
+        return m_context.getBundle(id);
+    }
+
+    public Bundle[] getBundles() {
+        return m_context.getBundles();
+    }
+
+    public void addServiceListener(ServiceListener listener, String filter) throws InvalidSyntaxException {
+        synchronized (m_serviceListenerFilterMap) {
+            m_serviceListenerFilterMap.put(listener, filter);
+            m_currentVersion++;
+        }
+    }
+
+    public void addServiceListener(ServiceListener listener) {
+        synchronized (m_serviceListenerFilterMap) {
+            m_serviceListenerFilterMap.put(listener, null);
+            m_currentVersion++;
+        }
+    }
+
+    public void removeServiceListener(ServiceListener listener) {
+        synchronized (m_serviceListenerFilterMap) {
+            m_serviceListenerFilterMap.remove(listener);
+            m_currentVersion++;
+        }
+    }
+
+    public void addBundleListener(BundleListener listener) {
+        m_context.addBundleListener(listener);
+    }
+
+    public void removeBundleListener(BundleListener listener) {
+        m_context.removeBundleListener(listener);
+    }
+
+    public void addFrameworkListener(FrameworkListener listener) {
+        m_context.addFrameworkListener(listener);
+    }
+
+    public void removeFrameworkListener(FrameworkListener listener) {
+        m_context.removeFrameworkListener(listener);
+    }
+
+    public ServiceRegistration registerService(String[] clazzes, Object service, Dictionary properties) {
+        return m_context.registerService(clazzes, service, properties);
+    }
+
+    public ServiceRegistration registerService(String clazz, Object service, Dictionary properties) {
+        return m_context.registerService(clazz, service, properties);
+    }
+
+    public ServiceReference[] getServiceReferences(String clazz, String filter) throws InvalidSyntaxException {
+        return m_context.getServiceReferences(clazz, filter);
+    }
+
+    public ServiceReference[] getAllServiceReferences(String clazz, String filter) throws InvalidSyntaxException {
+        return m_context.getAllServiceReferences(clazz, filter);
+    }
+
+    public ServiceReference getServiceReference(String clazz) {
+        return m_context.getServiceReference(clazz);
+    }
+
+    public Object getService(ServiceReference reference) {
+        return m_context.getService(reference);
+    }
+
+    public boolean ungetService(ServiceReference reference) {
+        return m_context.ungetService(reference);
+    }
+
+    public File getDataFile(String filename) {
+        return m_context.getDataFile(filename);
+    }
+
+    public Filter createFilter(String filter) throws InvalidSyntaxException {
+        return m_context.createFilter(filter);
+    }
+
+    protected Entry[] synchronizeCollection() {
+        // lazy copy on write: we make a new copy only if writes have changed the collection
+        synchronized (m_serviceListenerFilterMap) {
+            if (m_currentVersion != m_entryVersion) {
+                m_serviceListenerFilterMapEntries = (Entry[]) m_serviceListenerFilterMap.entrySet().toArray(new Entry[m_serviceListenerFilterMap.size()]);
+                m_entryVersion = m_currentVersion;
+            }
+        }
+        return m_serviceListenerFilterMapEntries;
+    }
+}
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dm/index/FilterIndex.java b/dependencymanager/core/src/main/java/org/apache/felix/dm/index/FilterIndex.java
new file mode 100644
index 0000000..b306c34
--- /dev/null
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dm/index/FilterIndex.java
@@ -0,0 +1,26 @@
+package org.apache.felix.dm.index;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * A filter index is an interface you can implement to create your own, optimized index for specific filter expressions.
+ */
+public interface FilterIndex {
+    /** Opens this filter index. */
+    public void open(BundleContext context);
+    /** Closes this filter index. */
+    public void close();
+    /** Determines if the combination of class and filter is applicable for this filter index. */
+    public boolean isApplicable(String clazz, String filter);
+    /** Returns all service references that match the specified class and filter. */
+    public ServiceReference[] getAllServiceReferences(String clazz, String filter);
+    /** Invoked whenever a service event occurs. */
+    public void serviceChanged(ServiceEvent event);
+    /** Adds a service listener to this filter index. */
+    public void addServiceListener(ServiceListener listener, String filter);
+    /** Removes a service listener from this filter index. */
+    public void removeServiceListener(ServiceListener listener);
+}
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dm/index/FilterIndexBundleContext.java b/dependencymanager/core/src/main/java/org/apache/felix/dm/index/FilterIndexBundleContext.java
new file mode 100644
index 0000000..5e4baa8
--- /dev/null
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dm/index/FilterIndexBundleContext.java
@@ -0,0 +1,43 @@
+package org.apache.felix.dm.index;
+
+import java.util.Map.Entry;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceListener;
+
+public class FilterIndexBundleContext extends BundleContextInterceptorBase {
+    public FilterIndexBundleContext(BundleContext context) {
+        super(context);
+    }
+
+    public void serviceChanged(ServiceEvent event) {
+        Entry[] entries = synchronizeCollection();
+        for (int i = 0; i < entries.length; i++) {
+            Entry serviceListenerFilterEntry = entries[i];
+            ServiceListener serviceListener = (ServiceListener) serviceListenerFilterEntry.getKey();
+            String filter = (String) serviceListenerFilterEntry.getValue();
+            if (filter == null) {
+                serviceListener.serviceChanged(event);
+            }
+            else {
+                // call service changed on the listener if the filter matches the event
+                // TODO review if we can be smarter here
+                try {
+                    if ("(objectClass=*)".equals(filter)) {
+                        serviceListener.serviceChanged(event);
+                    }
+                    else {
+                        if (m_context.createFilter(filter).match(event.getServiceReference())) {
+                            serviceListener.serviceChanged(event);
+                        }
+                    }
+                }
+                catch (InvalidSyntaxException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+    }
+}
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dm/index/MultiPropertyExactFilter.java b/dependencymanager/core/src/main/java/org/apache/felix/dm/index/MultiPropertyExactFilter.java
new file mode 100644
index 0000000..276ba5f
--- /dev/null
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dm/index/MultiPropertyExactFilter.java
@@ -0,0 +1,382 @@
+package org.apache.felix.dm.index;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import org.apache.felix.dm.tracker.ServiceTracker;
+import org.apache.felix.dm.tracker.ServiceTrackerCustomizer;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
+
+public class MultiPropertyExactFilter implements FilterIndex, ServiceTrackerCustomizer {
+    private final Object m_lock = new Object();
+    private ServiceTracker m_tracker;
+    private BundleContext m_context;
+    private final List /* <String> */ m_propertyKeys;
+    private final Map /* <String, List<ServiceReference>> */ m_keyToServiceReferencesMap = new HashMap();
+    private final Map /* <String, List<ServiceListener>> */ m_keyToListenersMap = new HashMap();
+    private final Map /* <ServiceListener, String> */ m_listenerToFilterMap = new HashMap();
+
+    public MultiPropertyExactFilter(String[] propertyKeys) {
+        String[] keys = (String[]) Arrays.copyOf(propertyKeys, propertyKeys.length);
+        Arrays.sort(keys);
+        m_propertyKeys = Arrays.asList(keys);
+    }
+    
+    public void open(BundleContext context) {
+        synchronized (m_lock) {
+            if (m_context != null) {
+                throw new IllegalStateException("Filter already open.");
+            }
+            try {
+                m_tracker = new ServiceTracker(context, context.createFilter("(" + Constants.OBJECTCLASS + "=*)"), this);
+            }
+            catch (InvalidSyntaxException e) {
+                throw new Error();
+            }
+            m_context = context;
+        }
+        m_tracker.open(true);
+    }
+
+    public void close() {
+        ServiceTracker tracker;
+        synchronized (m_lock) {
+            if (m_context == null) {
+                throw new IllegalStateException("Filter already closed.");
+            }
+            tracker = m_tracker;
+            m_tracker = null;
+            m_context = null;
+        }
+        tracker.close();
+    }
+
+    public ServiceReference[] getAllServiceReferences(String clazz, String filter) {
+        List /* <ServiceReference> */ result = new ArrayList();
+        List keys = createKeysFromFilter(clazz, filter);
+        Iterator iterator = keys.iterator();
+        while (iterator.hasNext()) {
+            String key = (String) iterator.next();
+            ServiceReference reference;
+            synchronized (m_keyToServiceReferencesMap) {
+                List references = (List) m_keyToServiceReferencesMap.get(key);
+                if (references != null) {
+                    result.addAll(references);
+                }
+            }
+        }
+        if (result.size() == 0) {
+            return null;
+        }
+        else {
+            return (ServiceReference[]) result.toArray(new ServiceReference[result.size()]);
+        }
+    }
+
+    public Object addingService(ServiceReference reference) {
+        BundleContext context;
+        synchronized (m_lock) {
+            context = m_context;
+        }
+        if (context != null) {
+            return context.getService(reference);
+        }
+        else {
+            throw new IllegalStateException("No valid bundle context.");
+        }
+    }
+
+    public void addedService(ServiceReference reference, Object service) {
+        if (isApplicable(reference.getPropertyKeys())) {
+            update(reference);
+        }
+    }
+
+    public void modifiedService(ServiceReference reference, Object service) {
+        if (isApplicable(reference.getPropertyKeys())) {
+            update(reference);
+        }
+    }
+
+    public void removedService(ServiceReference reference, Object service) {
+        if (isApplicable(reference.getPropertyKeys())) {
+            remove(reference);
+        }
+    }
+    
+    public void update(ServiceReference reference) {
+        List /* <String> */ keys = createKeys(reference);
+        synchronized (m_keyToServiceReferencesMap) {
+            // TODO any 'old' references that are still in the index need to be removed in case of an update
+            for (int i = 0; i < keys.size(); i++) {
+                List /* <ServiceReference> */ references = (List) m_keyToServiceReferencesMap.get(keys.get(i));
+                if (references == null) {
+                    references = new ArrayList();
+                    m_keyToServiceReferencesMap.put(keys.get(i), references);
+                }
+                references.add(reference);
+            }
+        }
+    }
+
+    public void remove(ServiceReference reference) {
+        List /* <String> */ keys = createKeys(reference);
+        synchronized (m_keyToServiceReferencesMap) {
+            for (int i = 0; i < keys.size(); i++) {
+                List /* <ServiceReference> */ references = (List) m_keyToServiceReferencesMap.get(keys.get(i));
+                if (references != null) {
+                    references.remove(reference);
+                }
+            }
+        }
+    }
+
+    public boolean isApplicable(String[] propertyKeys) {
+        List list = Arrays.asList(propertyKeys);
+        Iterator iterator = m_propertyKeys.iterator();
+        while (iterator.hasNext()) {
+            String item = (String) iterator.next();
+            if (!(list.contains(item))) {
+                return false;
+            }
+        }
+        return true;
+    }
+    
+    public static void main(String[] args) {
+        
+        System.out.println("" + (new MultiPropertyExactFilter(new String[] { "objectClass", "repository", "path", "name" })).isApplicable(null, "(objectClass=abc)"));
+    }
+    
+    public boolean isApplicable(String clazz, String filter) {
+        // "(&(a=b)(c=d))"
+        // "(&(&(a=b)(c=d))(objC=aaa))"
+        // startsWith "(&" en split in "(x=y)" -> elke x bestaat in m_propertykeys
+        
+        // (&(objectClass=xyz)(&(a=x)(b=y)))
+        
+        Set /* <String> */found = new HashSet();
+        if (filter != null && filter.startsWith("(&(objectClass=") && filter.contains(")(&(") && filter.endsWith(")))")) {
+            int i1 = filter.indexOf(")(&(");
+            String className = filter.substring("(&(objectClass=".length(), i1);
+            if (!m_propertyKeys.contains("objectClass")) {
+                return false;
+            }
+            else {
+                found.add("objectClass");
+            }
+            String[] parts = filter.substring(i1 + ")(&(".length(), filter.length() - ")))".length()).split("\\)\\(");
+            for (int i = 0; i < parts.length; i++) {
+                String part = parts[i];
+                String[] tuple = part.split("=");
+                if (!m_propertyKeys.contains(tuple[0])) {
+                    return false;
+                }
+                else {
+                    found.add(tuple[0]);
+                }
+                // TODO check value tuple[1]
+            }
+            return (found.size() == m_propertyKeys.size());
+        }
+        else if (filter != null && filter.startsWith("(&(") && filter.endsWith("))")) {
+            String[] parts = filter.substring(3, filter.length() - 2).split("\\)\\(");
+            for (int i = 0; i < parts.length; i++) {
+                String part = parts[i];
+                String[] tuple = part.split("=");
+                if (!m_propertyKeys.contains(tuple[0])) {
+                    return false;
+                }
+                else {
+                    found.add(tuple[0]);
+                }
+                // TODO check value tuple[1]
+            }
+            return (found.size() == m_propertyKeys.size());
+        }
+        else if (filter != null && filter.startsWith("(") && filter.endsWith(")") && m_propertyKeys.size() == 1) { // TODO quick hack
+            String part = filter.substring(1, filter.length() - 1);
+            String[] tuple = part.split("=");
+            if (!m_propertyKeys.contains(tuple[0])) {
+                return false;
+            }
+            else {
+                return true;
+            }
+        }
+        else if (clazz != null && filter == null && m_propertyKeys.size() == 1 && m_propertyKeys.get(0).equals(Constants.OBJECTCLASS)) {
+            return true;
+        }
+        return false;
+    }
+    
+    private List /* <String> */ createKeys(ServiceReference reference) {
+        List /* <String> */ results = new ArrayList();
+        
+        results.add("");
+        
+        String[] keys = reference.getPropertyKeys();
+        Arrays.sort(keys);
+        StringBuffer result = new StringBuffer();
+        for (int i = 0; i < keys.length; i++) {
+            String key = keys[i];
+            if (m_propertyKeys.contains(key)) {
+                Object value = reference.getProperty(key);
+                if (value instanceof String[]) {
+                    String[] values = (String[]) value;
+                    List newResults = new ArrayList();
+                    for (int j = 0; j < values.length; j++) {
+                        String val = values[j];
+                        for (int k = 0; k < results.size(); k++) {
+                            String head = (String) results.get(k);
+                            if (head != null && head.length() > 0) {
+                                head = head + ";";
+                            }
+                            newResults.add(head + key + "=" + val);
+                        }
+                    }
+                    results = newResults;
+                }
+                else {
+                    for (int k = 0; k < results.size(); k++) {
+                        String head = (String) results.get(k);
+                        if (head != null && head.length() > 0) {
+                            head = head + ";";
+                        }
+                        results.set(k, head + key + "=" + value);
+                    }
+                }
+            }
+        }
+        return results;
+    }
+    
+    private List /* <String> */ createKeysFromFilter(String clazz, String filter) {
+        // TODO array support
+        List result = new ArrayList();
+        StringBuffer index = new StringBuffer();
+        Iterator iterator = m_propertyKeys.iterator();
+        while (iterator.hasNext()) {
+            String key = (String) iterator.next();
+            if (index.length() > 0) {
+                index.append(';');
+            }
+            index.append(key);
+            index.append('=');
+            String value = null;
+            if (clazz != null && Constants.OBJECTCLASS.equals(key)) {
+                value = clazz;
+            } // (&(obC=a)(&(a=b)(c=d)))
+            if (filter != null) {
+                String startString = "(" + key + "=";
+                int i1 = filter.indexOf(startString);
+                if (i1 != -1) {
+                    int i2 = filter.indexOf(")(", i1);
+                    if (i2 == -1) {
+                        if (filter.endsWith(")))")) {
+                            i2 = filter.length() - 3;
+                        }
+                        else if (filter.endsWith("))")) {
+                            i2 = filter.length() - 2;
+                        }
+                        else {
+                            i2 = filter.length() - 1;
+                        }
+                    }
+                    String value2 = filter.substring(i1 + startString.length(), i2);
+                    if (value != null && !value.equals(value2)) {
+                        // corner case: someone specified a clazz and
+                        // also a filter containing a different clazz
+                        return result;
+                    }
+                    value = value2;
+                }
+            }
+            index.append(value);
+        }
+        result.add(index.toString());
+        return result;
+    }
+
+    public void serviceChanged(ServiceEvent event) {
+        if (isApplicable(event.getServiceReference().getPropertyKeys())) {
+            List /* <String> */ keys = createKeys(event.getServiceReference());
+            List list = new ArrayList();
+            synchronized (m_keyToListenersMap) {
+                for (int i = 0; i < keys.size(); i++) {
+                    String key = (String) keys.get(i);
+                    List listeners = (List) m_keyToListenersMap.get(key);
+                    if (listeners != null) {
+                        list.addAll(listeners);
+                    }
+                }
+            }
+            if (list != null) {
+                Iterator iterator = list.iterator();
+                while (iterator.hasNext()) {
+                    ServiceListener listener = (ServiceListener) iterator.next();
+                    listener.serviceChanged(event);
+                }
+            }
+        }
+    }
+
+    public void addServiceListener(ServiceListener listener, String filter) {
+        List keys = createKeysFromFilter(null, filter);
+        Iterator iterator = keys.iterator();
+        while (iterator.hasNext()) {
+            String key = (String) iterator.next();
+            synchronized (m_keyToListenersMap) {
+                List /* <ServiceListener> */ listeners = (List) m_keyToListenersMap.get(key);
+                if (listeners == null) {
+                    listeners = new CopyOnWriteArrayList();
+                    m_keyToListenersMap.put(key, listeners);
+                }
+                listeners.add(listener);
+                m_listenerToFilterMap.put(listener, filter);
+            }
+        }
+    }
+
+    public void removeServiceListener(ServiceListener listener) {
+        synchronized (m_keyToListenersMap) {
+            String filter = (String) m_listenerToFilterMap.remove(listener);
+            List keys = createKeysFromFilter(null, filter);
+            Iterator iterator = keys.iterator();
+            while (iterator.hasNext()) {
+                String key = (String) iterator.next();
+                
+                boolean result = filter != null;
+                if (result) {
+                    List /* <ServiceListener> */ listeners = (List) m_keyToListenersMap.get(key);
+                    if (listeners != null) {
+                        listeners.remove(listener);
+                    }
+                    // TODO actually, if listeners == null that would be strange....
+                }
+            }
+        }
+    }
+    
+    public String toString() {
+        StringBuffer sb = new StringBuffer();
+        sb.append("MultiPropertyExactFilter[");
+        sb.append("K2L: " + m_keyToListenersMap.size());
+        sb.append(", K2SR: " + m_keyToServiceReferencesMap.size());
+        sb.append(", L2F: " + m_listenerToFilterMap.size());
+        sb.append("]");
+        return sb.toString();
+    }
+}
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dm/index/ServiceRegistryCache.java b/dependencymanager/core/src/main/java/org/apache/felix/dm/index/ServiceRegistryCache.java
new file mode 100644
index 0000000..b7df3aa
--- /dev/null
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dm/index/ServiceRegistryCache.java
@@ -0,0 +1,160 @@
+package org.apache.felix.dm.index;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import org.eclipse.osgi.framework.console.CommandInterpreter;
+import org.eclipse.osgi.framework.console.CommandProvider;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+
+public class ServiceRegistryCache implements ServiceListener, CommandProvider {
+    private final List /* <FilterIndex> */ m_filterIndexList = new CopyOnWriteArrayList();
+    private final BundleContext m_context;
+    private final FilterIndexBundleContext m_filterIndexBundleContext;
+    private final Map /* <BundleContext, BundleContextInterceptor> */ m_bundleContextInterceptorMap = new HashMap();
+    private long m_currentVersion = 0;
+    private long m_arrayVersion = -1;
+    private BundleContextInterceptor[] m_interceptors = null;
+    private ServiceRegistration m_registration;
+
+    
+    public ServiceRegistryCache(BundleContext context) {
+        m_context = context;
+        m_filterIndexBundleContext = new FilterIndexBundleContext(m_context);
+    }
+    
+    public void open() {
+        m_context.addServiceListener(this);
+        m_registration = m_context.registerService(CommandProvider.class.getName(), this, null);
+    }
+    
+    public void close() {
+        m_registration.unregister();
+        m_context.removeServiceListener(this);
+    }
+    
+    public void addFilterIndex(FilterIndex index) {
+        m_filterIndexList.add(index);
+        index.open(m_filterIndexBundleContext);
+    }
+    
+    public void removeFilterIndex(FilterIndex index) {
+        index.close();
+        m_filterIndexList.remove(index);
+    }
+
+    public void serviceChanged(ServiceEvent event) {
+        // any incoming event is first dispatched to the list of filter indices
+        m_filterIndexBundleContext.serviceChanged(event);
+        // and then all the other listeners can access it
+        synchronized (m_bundleContextInterceptorMap) {
+            if (m_currentVersion != m_arrayVersion) {
+                // if our copy is out of date, we make a new one
+                m_interceptors = (BundleContextInterceptor[]) m_bundleContextInterceptorMap.values().toArray(new BundleContextInterceptor[m_bundleContextInterceptorMap.size()]);
+                m_arrayVersion = m_currentVersion;
+            }
+        }
+        for (int i = 0; i < m_interceptors.length; i++) {
+            BundleContextInterceptor bundleContextInterceptor = m_interceptors[i];
+            bundleContextInterceptor.serviceChanged(event);
+        }
+    }
+    
+    /** Creates an interceptor for a bundle context that uses our cache. */
+    public BundleContext createBundleContextInterceptor(BundleContext context) {
+        synchronized (m_bundleContextInterceptorMap) {
+            BundleContextInterceptor bundleContextInterceptor = (BundleContextInterceptor) m_bundleContextInterceptorMap.get(context);
+            if (bundleContextInterceptor == null) {
+                bundleContextInterceptor = new BundleContextInterceptor(this, context);
+                m_bundleContextInterceptorMap.put(context, bundleContextInterceptor);
+                m_currentVersion++;
+                // TODO figure out a good way to clean up bundle contexts that are no longer valid so they can be garbage collected
+            }
+            return bundleContextInterceptor;
+        }
+    }
+
+    public FilterIndex hasFilterIndexFor(String clazz, String filter) {
+        Iterator iterator = m_filterIndexList.iterator();
+        while (iterator.hasNext()) {
+            FilterIndex filterIndex = (FilterIndex) iterator.next();
+            if (filterIndex.isApplicable(clazz, filter)) {
+                return filterIndex;
+            }
+        }
+        return null;
+    }
+
+    public void serviceChangedForFilterIndices(ServiceEvent event) {
+        Iterator iterator = m_filterIndexList.iterator();
+        while (iterator.hasNext()) {
+            FilterIndex filterIndex = (FilterIndex) iterator.next();
+            filterIndex.serviceChanged(event);
+        }
+    }
+
+    public void _sc(CommandInterpreter ci) {
+        ci.println(toString());
+    }
+    
+    public void _fi(CommandInterpreter ci) {
+        String arg = ci.nextArgument();
+        if (arg != null) {
+            int x = Integer.parseInt(arg);
+            FilterIndex filterIndex = (FilterIndex) m_filterIndexList.get(x);
+            String a1 = ci.nextArgument();
+            String a2 = null;
+            if (a1 != null) {
+                if ("-".equals(a1)) {
+                    a1 = null;
+                }
+                a2 = ci.nextArgument();
+            }
+            if (filterIndex.isApplicable(a1, a2)) {
+                ServiceReference[] references = filterIndex.getAllServiceReferences(a1, a2);
+                if (references == null) {
+                    ci.println("No results.");
+                }
+                else {
+                    ci.println("Found " + references.length + " references:");
+                    for (int i = 0; i < references.length; i++) {
+                        ci.println("" + i + " - " + references[i]);
+                    }
+                }
+            }
+            else {
+                ci.println("Filter not applicable.");
+            }
+        }
+        else {
+            ci.println("FilterIndices:");
+            Iterator iterator = m_filterIndexList.iterator();
+            int index = 0;
+            while (iterator.hasNext()) {
+                FilterIndex filterIndex = (FilterIndex) iterator.next();
+                ci.println("" + index + " " + filterIndex);
+                index++;
+            }
+        }
+    }
+    
+    public String getHelp() {
+        return "I'm not going to help you!";
+    }
+    
+    public String toString() {
+        StringBuffer sb = new StringBuffer();
+        sb.append("ServiceRegistryCache[");
+        sb.append("FilterIndices: " + m_filterIndexList.size());
+        sb.append(", BundleContexts intercepted: " + m_bundleContextInterceptorMap.size());
+        sb.append("]");
+        return sb.toString();
+    }
+}
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dm/tracker/AbstractTracked.java b/dependencymanager/core/src/main/java/org/apache/felix/dm/tracker/AbstractTracked.java
index a312b51..db9bc17 100644
--- a/dependencymanager/core/src/main/java/org/apache/felix/dm/tracker/AbstractTracked.java
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dm/tracker/AbstractTracked.java
@@ -42,7 +42,7 @@
 	 * 
 	 * @GuardedBy this
 	 */
-	private final Map			tracked;
+	private Map			tracked;
 
 	/**
 	 * Modification count. This field is initialized to zero and incremented by
@@ -99,11 +99,15 @@
 	 * AbstractTracked constructor.
 	 */
 	AbstractTracked() {
-		tracked = new HashMap();
-		trackingCount = 0;
-		adding = new ArrayList(6);
-		initial = new LinkedList();
-		closed = false;
+	    this.tracked = new HashMap();
+	    trackingCount = 0;
+	    adding = new ArrayList(6);
+	    initial = new LinkedList();
+	    closed = false;
+	}
+	
+	void setTracked(HashMap map) {
+	    this.tracked = map;
 	}
 
 	/**
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dm/tracker/ServiceTracker.java b/dependencymanager/core/src/main/java/org/apache/felix/dm/tracker/ServiceTracker.java
index 6a373a5..c51f8f8 100644
--- a/dependencymanager/core/src/main/java/org/apache/felix/dm/tracker/ServiceTracker.java
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dm/tracker/ServiceTracker.java
@@ -18,7 +18,11 @@
 import java.security.AccessController;
 import java.security.PrivilegedAction;
 import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
+import java.util.TreeSet;
 
 import org.apache.felix.dm.DependencyManager;
 import org.apache.felix.dm.ServiceUtil;
@@ -883,6 +887,73 @@
                 return result;
             }
         }
+        
+        private final HashMap m_highestTrackedCache = new HashMap();
+        
+        private ServiceReference highestTrackedCache(long serviceId) {
+            Long sid = Long.valueOf(serviceId);
+            TreeSet services = (TreeSet) m_highestTrackedCache.get(sid);
+            if (services != null && services.size() > 0) {
+                ServiceReference result = (ServiceReference) services.last();
+                return result;
+            }
+            return null;
+        }
+        
+        private void addHighestTrackedCache(ServiceReference reference) {
+            Long serviceId = ServiceUtil.getServiceIdObject(reference);
+            TreeSet services = (TreeSet) m_highestTrackedCache.get(serviceId);
+            if (services == null) {
+                services = new TreeSet();
+                m_highestTrackedCache.put(serviceId, services);
+            }
+            services.add(reference);
+        }
+        
+        private void removeHighestTrackedCache(ServiceReference reference) {
+            Long serviceId = ServiceUtil.getServiceIdObject(reference);
+            TreeSet services = (TreeSet) m_highestTrackedCache.get(serviceId);
+            if (services != null) {
+                services.remove(reference);
+            }
+        }
+        
+        private void clearHighestTrackedCache() {
+            m_highestTrackedCache.clear();
+        }
+        
+        private final HashMap m_highestHiddenCache = new HashMap();
+        
+        private ServiceReference highestHiddenCache(long serviceId) {
+            Long sid = Long.valueOf(serviceId);
+            TreeSet services = (TreeSet) m_highestHiddenCache.get(sid);
+            if (services != null && services.size() > 0) {
+                ServiceReference result = (ServiceReference) services.last();
+//                System.out.println("getH " + ServiceUtil.toString(result));
+                return result;
+            }
+            return null;
+        }
+        
+        private void addHighestHiddenCache(ServiceReference reference) {
+//            System.out.println("addH " + ServiceUtil.toString(reference));
+            Long serviceId = ServiceUtil.getServiceIdObject(reference);
+            TreeSet services = (TreeSet) m_highestHiddenCache.get(serviceId);
+            if (services == null) {
+                services = new TreeSet();
+                m_highestHiddenCache.put(serviceId, services);
+            }
+            services.add(reference);
+        }
+        
+        private void removeHighestHiddenCache(ServiceReference reference) {
+//            System.out.println("remH " + ServiceUtil.toString(reference));
+            Long serviceId = ServiceUtil.getServiceIdObject(reference);
+            TreeSet services = (TreeSet) m_highestHiddenCache.get(serviceId);
+            if (services != null) {
+                services.remove(reference);
+            }
+        }
 
         /**
          * Hide a service reference, placing it in the list of hidden services.
@@ -890,11 +961,12 @@
          * @param ref the service reference to add to the hidden list
          */
         private void hide(ServiceReference ref) {
-            if (DEBUG) { System.out.println("ServiceTracker.Tracked.hide " + ServiceUtil.toString(ref)); }
-            synchronized (this) {
-                if (DEBUG) { if (m_hidden.contains(ref)) { System.out.println("ServiceTracker.Tracked.hide ERROR: " + ServiceUtil.toString(ref)); }};
-                m_hidden.add(ref);
-            }
+            addHighestHiddenCache(ref);
+//            if (DEBUG) { System.out.println("ServiceTracker.Tracked.hide " + ServiceUtil.toString(ref)); }
+//            synchronized (this) {
+//                if (DEBUG) { if (m_hidden.contains(ref)) { System.out.println("ServiceTracker.Tracked.hide ERROR: " + ServiceUtil.toString(ref)); }};
+//                m_hidden.add(ref);
+//            }
         }
         
         /**
@@ -903,11 +975,12 @@
          * @param ref the service reference to remove from the hidden list
          */
         private void unhide(ServiceReference ref) {
-            if (DEBUG) { System.out.println("ServiceTracker.Tracked.unhide " + ServiceUtil.toString(ref)); }
-            synchronized (this) {
-                if (DEBUG) { if (!m_hidden.contains(ref)) { System.out.println("ServiceTracker.Tracked.unhide ERROR: " + ServiceUtil.toString(ref)); }};
-                m_hidden.remove(ref);
-            }
+            removeHighestHiddenCache(ref);
+//            if (DEBUG) { System.out.println("ServiceTracker.Tracked.unhide " + ServiceUtil.toString(ref)); }
+//            synchronized (this) {
+//                if (DEBUG) { if (!m_hidden.contains(ref)) { System.out.println("ServiceTracker.Tracked.unhide ERROR: " + ServiceUtil.toString(ref)); }};
+//                m_hidden.remove(ref);
+//            }
         }
 	    
 		/**
@@ -915,6 +988,7 @@
 		 */
 		Tracked() {
 			super();
+			setTracked(new HashMapCache());
 		}
 		
 		void setInitial(Object[] list) {
@@ -927,13 +1001,13 @@
 		    for (int i = 0; i < list.length; i++) {
 		        ServiceReference sr = (ServiceReference) list[i];
 		        if (sr != null) {
-		            for (int j = 0; j < list.length; j++) {
+		            long sid = ServiceUtil.getServiceId(sr);
+		            long r = ServiceUtil.getRanking(sr);
+		            for (int j = i + 1; j < list.length; j++) {
 		                ServiceReference sr2 = (ServiceReference) list[j];
-		                if (sr2 != null && j != i) {
-		                    long sid = ServiceUtil.getServiceId(sr);
+		                if (sr2 != null /* && j != i */) {
                             long sid2 = ServiceUtil.getServiceId(sr2);
                             if (sid == sid2) {
-                                long r = ServiceUtil.getRanking(sr);
                                 long r2 = ServiceUtil.getRanking(sr2);
                                 if (r > r2) {
                                     if (DEBUG) { System.out.println("ServiceTracker.Tracked.setInitial: hiding " + ServiceUtil.toString(sr2)); }
@@ -997,7 +1071,7 @@
 				case ServiceEvent.MODIFIED :
 				    ServiceReference higher = null;
 				    ServiceReference lower = null;
-				    ServiceReference sr = highestTracked(sid);
+				    ServiceReference sr = highestTrackedCache(sid);
 				    if (sr != null) {
 				        int ranking = ServiceUtil.getRanking(reference);
 				        int trackedRanking = ServiceUtil.getRanking(sr);
@@ -1067,10 +1141,10 @@
 							 */
 						}
 						else {
-		                    ServiceReference ht = highestTracked(sid);
-		                    ServiceReference hh = highestHidden(sid);
+		                    ServiceReference ht = highestTrackedCache(sid);
 		                    if (reference.equals(ht)) {
 		                        try {
+		                            ServiceReference hh = highestHiddenCache(sid);
 		                            if (hh != null) {
 		                                unhide(hh);
 		                                track(hh, null);
@@ -1092,10 +1166,10 @@
 					break;
                 case 8 /* ServiceEvent.MODIFIED_ENDMATCH */ :
 				case ServiceEvent.UNREGISTERING :
-				    ServiceReference ht = highestTracked(sid);
-				    ServiceReference hh = highestHidden(sid);
+				    ServiceReference ht = highestTrackedCache(sid);
 				    if (reference.equals(ht)) {
 				        try {
+				            ServiceReference hh = highestHiddenCache(sid);
     				        if (hh != null) {
     				            unhide(hh);
     				            track(hh, null);
@@ -1170,6 +1244,31 @@
 				final Object related, final Object object) {
 			customizer.removedService((ServiceReference) item, object);
 		}
+		
+		class HashMapCache extends HashMap {
+		    public Object put(Object key, Object value) {
+		        addHighestTrackedCache((ServiceReference) key);
+		        return super.put(key, value);
+		    }
+
+		    public void putAll(Map m) {
+		        Iterator i = m.keySet().iterator();
+		        while (i.hasNext()) {
+		            addHighestTrackedCache((ServiceReference) i.next());
+		        }
+		        super.putAll(m);
+		    }
+
+		    public Object remove(Object key) {
+		        removeHighestTrackedCache((ServiceReference) key);
+		        return super.remove(key);
+		    }
+
+		    public void clear() {
+		        clearHighestTrackedCache();
+		        super.clear();
+		    }
+		}
 	}
 
 	/**
@@ -1185,6 +1284,7 @@
 		 */
 		AllTracked() {
 			super();
+            setTracked(new HashMapCache());
 		}
 	}
 }