You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by pa...@apache.org on 2019/04/30 16:21:30 UTC

svn commit: r1858443 [2/3] - in /felix/trunk/connect: ./ src/main/java/org/apache/felix/connect/ src/main/java/org/apache/felix/connect/felix/framework/ src/main/java/org/apache/felix/connect/felix/framework/capabilityset/ src/main/java/org/apache/feli...

Modified: felix/trunk/connect/src/main/java/org/apache/felix/connect/felix/framework/ServiceRegistry.java
URL: http://svn.apache.org/viewvc/felix/trunk/connect/src/main/java/org/apache/felix/connect/felix/framework/ServiceRegistry.java?rev=1858443&r1=1858442&r2=1858443&view=diff
==============================================================================
--- felix/trunk/connect/src/main/java/org/apache/felix/connect/felix/framework/ServiceRegistry.java (original)
+++ felix/trunk/connect/src/main/java/org/apache/felix/connect/felix/framework/ServiceRegistry.java Tue Apr 30 16:21:29 2019
@@ -7,7 +7,7 @@
  * "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
+ *   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
@@ -22,80 +22,73 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Dictionary;
-import java.util.HashMap;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
-import java.util.SortedSet;
-import java.util.TreeSet;
-import java.util.WeakHashMap;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.AtomicReference;
 
+import org.apache.felix.connect.felix.framework.capabilityset.CapabilitySet;
+import org.apache.felix.connect.felix.framework.capabilityset.SimpleFilter;
+import org.apache.felix.connect.felix.framework.wiring.BundleCapabilityImpl;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.Constants;
 import org.osgi.framework.ServiceEvent;
 import org.osgi.framework.ServiceException;
-import org.osgi.framework.ServiceFactory;
 import org.osgi.framework.ServiceReference;
 import org.osgi.framework.ServiceRegistration;
-import org.osgi.framework.wiring.BundleCapability;
-
-import org.apache.felix.connect.felix.framework.capabilityset.CapabilitySet;
-import org.apache.felix.connect.felix.framework.capabilityset.SimpleFilter;
+import org.osgi.resource.Capability;
 
 public class ServiceRegistry
 {
-    private long m_currentServiceId = 1L;
+    /** Counter for the service id */
+    private final AtomicLong m_currentServiceId = new AtomicLong(1);
+
     // Maps bundle to an array of service registrations.
-    private final Map<Bundle, ServiceRegistration[]> m_regsMap = Collections.synchronizedMap(new HashMap<Bundle, ServiceRegistration[]>());
+    private final ConcurrentMap<Bundle, List<ServiceRegistration<?>>> m_regsMap = new ConcurrentHashMap<Bundle, List<ServiceRegistration<?>>>();
+
     // Capability set for all service registrations.
-    private final CapabilitySet<ServiceRegistrationImpl.ServiceReferenceImpl> m_regCapSet;
-    // Maps registration to thread to keep track when a
-    // registration is in use, which will cause other
-    // threads to wait.
-    private final Map<ServiceRegistrationImpl, Thread> m_lockedRegsMap = new HashMap<ServiceRegistrationImpl, Thread>();
+    private final CapabilitySet m_regCapSet = new CapabilitySet(Collections.singletonList(Constants.OBJECTCLASS), false);
+
     // Maps bundle to an array of usage counts.
-    private final Map<Bundle, UsageCount[]> m_inUseMap = new HashMap<Bundle, UsageCount[]>();
+    private final ConcurrentMap<Bundle, UsageCount[]> m_inUseMap = new ConcurrentHashMap<Bundle, UsageCount[]>();
+
     private final ServiceRegistryCallbacks m_callbacks;
-    private final WeakHashMap<ServiceReference, ServiceReference> m_blackList =
-            new WeakHashMap<ServiceReference, ServiceReference>();
-    private final static Class<?>[] m_hookClasses =
-            {
-                    org.osgi.framework.hooks.bundle.FindHook.class,
-                    org.osgi.framework.hooks.bundle.EventHook.class,
-                    org.osgi.framework.hooks.service.EventHook.class,
-                    org.osgi.framework.hooks.service.EventListenerHook.class,
-                    org.osgi.framework.hooks.service.FindHook.class,
-                    org.osgi.framework.hooks.service.ListenerHook.class,
-                    org.osgi.framework.hooks.weaving.WeavingHook.class,
-                    org.osgi.framework.hooks.resolver.ResolverHookFactory.class,
-                    org.osgi.service.url.URLStreamHandlerService.class,
-                    java.net.ContentHandler.class
-            };
-    private final Map<Class<?>, Set<ServiceReference<?>>> m_allHooks =
-            new HashMap<Class<?>, Set<ServiceReference<?>>>();
 
-    public ServiceRegistry(ServiceRegistryCallbacks callbacks)
+    private final HookRegistry hookRegistry = new HookRegistry();
+
+    public ServiceRegistry(final ServiceRegistryCallbacks callbacks)
     {
         m_callbacks = callbacks;
-
-        m_regCapSet = new CapabilitySet(Collections.singletonList(Constants.OBJECTCLASS), false);
     }
 
-    public ServiceReference[] getRegisteredServices(Bundle bundle)
+    /**
+     * Get all service references for a bundle
+     * @param bundle
+     * @return List with all valid service references or {@code null}.
+     */
+    public ServiceReference<?>[] getRegisteredServices(final Bundle bundle)
     {
-        ServiceRegistration[] regs = m_regsMap.get(bundle);
+        final List<ServiceRegistration<?>> regs = m_regsMap.get(bundle);
         if (regs != null)
         {
-            List<ServiceReference> refs = new ArrayList<ServiceReference>(regs.length);
-            for (ServiceRegistration reg : regs)
+            final List<ServiceReference<?>> refs = new ArrayList<ServiceReference<?>>(regs.size());
+            // this is a per bundle list, therefore synchronizing this should be fine
+            synchronized ( regs )
             {
-                try
+                for (final ServiceRegistration<?> reg : regs)
                 {
-                    refs.add(reg.getReference());
-                }
-                catch (IllegalStateException ex)
-                {
-                    // Don't include the reference as it is not valid anymore
+                    try
+                    {
+                        refs.add(reg.getReference());
+                    }
+                    catch (final IllegalStateException ex)
+                    {
+                        // Don't include the reference as it is not valid anymore
+                    }
                 }
             }
             return refs.toArray(new ServiceReference[refs.size()]);
@@ -103,93 +96,123 @@ public class ServiceRegistry
         return null;
     }
 
-    // Caller is expected to fire REGISTERED event.
-    public ServiceRegistration registerService(
-            Bundle bundle, String[] classNames, Object svcObj, Dictionary dict)
-    {
-        ServiceRegistrationImpl reg = null;
-
-        synchronized (this)
+    /**
+     * Register a new service
+     *
+     * Caller must fire service event as this method is not doing it!
+     *
+     * @param bundle The bundle registering the service
+     * @param classNames The service class names
+     * @param svcObj The service object
+     * @param dict Optional service properties
+     * @return Service registration
+     */
+    public ServiceRegistration<?> registerService(
+        final Bundle bundle,
+        final String[] classNames,
+        final Object svcObj,
+        final Dictionary<?,?> dict)
+    {
+        // Create the service registration.
+        final ServiceRegistrationImpl reg = new ServiceRegistrationImpl(
+            this, bundle, classNames, m_currentServiceId.getAndIncrement(), svcObj, dict);
+
+        // Keep track of registered hooks.
+        this.hookRegistry.addHooks(classNames, svcObj, reg.getReference());
+
+        // Get the bundles current registered services.
+        final List<ServiceRegistration<?>> newRegs = new ArrayList<ServiceRegistration<?>>();
+        List<ServiceRegistration<?>> regs = m_regsMap.putIfAbsent(bundle, newRegs);
+        if (regs == null)
         {
-            // Create the service registration.
-            reg = new ServiceRegistrationImpl(
-                    this, bundle, classNames, m_currentServiceId++, svcObj, dict);
-
-            // Keep track of registered hooks.
-            addHooks(classNames, svcObj, reg.getReference());
-
-            // Get the bundles current registered services.
-            ServiceRegistration[] regs = (ServiceRegistration[]) m_regsMap.get(bundle);
-            m_regsMap.put(bundle, addServiceRegistration(regs, reg));
-            m_regCapSet.addCapability(reg.getReference());
+            regs = newRegs;
         }
-
-        // Notify callback objects about registered service.
-        if (m_callbacks != null)
+        // this is a per bundle list, therefore synchronizing this should be fine
+        synchronized ( regs )
         {
-            m_callbacks.serviceChanged(new ServiceEvent(
-                    ServiceEvent.REGISTERED, reg.getReference()), null);
+            regs.add(reg);
         }
+        m_regCapSet.addCapability((BundleCapabilityImpl) reg.getReference());
+
         return reg;
     }
 
-    public void unregisterService(Bundle bundle, ServiceRegistrationImpl reg)
+    /**
+     * Unregister a service
+     * @param bundle The bundle unregistering the service
+     * @param reg The service registration
+     */
+    public void unregisterService(
+            final Bundle bundle,
+            final ServiceRegistration<?> reg)
     {
         // If this is a hook, it should be removed.
-        removeHook(reg.getReference());
+        this.hookRegistry.removeHooks(reg.getReference());
 
-        synchronized (this)
+        // Now remove the registered service.
+        final List<ServiceRegistration<?>> regs = m_regsMap.get(bundle);
+        if (regs != null)
         {
-            // Note that we don't lock the service registration here using
-            // the m_lockedRegsMap because we want to allow bundles to get
-            // the service during the unregistration process. However, since
-            // we do remove the registration from the service registry, no
-            // new bundles will be able to look up the service.
-
-            // Now remove the registered service.
-            ServiceRegistration[] regs = m_regsMap.get(bundle);
-            m_regsMap.put(bundle, removeServiceRegistration(regs, reg));
-            m_regCapSet.removeCapability(reg.getReference());
+            // this is a per bundle list, therefore synchronizing this should be fine
+            synchronized ( regs )
+            {
+                regs.remove(reg);
+            }
         }
+        m_regCapSet.removeCapability((BundleCapabilityImpl) reg.getReference());
 
         // Notify callback objects about unregistering service.
         if (m_callbacks != null)
         {
             m_callbacks.serviceChanged(
-                    new ServiceEvent(ServiceEvent.UNREGISTERING, reg.getReference()), null);
+                new ServiceEvent(ServiceEvent.UNREGISTERING, reg.getReference()), null);
         }
 
         // Now forcibly unget the service object for all stubborn clients.
-        synchronized (this)
+        final ServiceReference<?> ref = reg.getReference();
+        ungetServices(ref);
+
+        // Invalidate registration
+        ((ServiceRegistrationImpl) reg).invalidate();
+
+        // Bundles are allowed to get a reference while unregistering
+        // get fresh set of bundles (should be empty, but this is a sanity check)
+        ungetServices(ref);
+
+        // Now flush all usage counts as the registration is invalid
+        for (Bundle usingBundle : m_inUseMap.keySet())
         {
-            Bundle[] clients = getUsingBundles(reg.getReference());
-            for (int i = 0; (clients != null) && (i < clients.length); i++)
+            flushUsageCount(usingBundle, ref, null);
+        }
+    }
+
+    private void ungetServices(final ServiceReference<?> ref)
+    {
+        final Bundle[] clients = getUsingBundles(ref);
+        for (int i = 0; (clients != null) && (i < clients.length); i++)
+        {
+            final UsageCount[] usages = m_inUseMap.get(clients[i]);
+            for (int x = 0; (usages != null) && (x < usages.length); x++)
             {
-                while (ungetService(clients[i], reg.getReference()))
+                if (usages[x].m_ref.equals(ref))
                 {
-                    ; // Keep removing until it is no longer possible
+                    ungetService(clients[i], ref, (usages[x].m_prototype ? usages[x].getService() : null));
                 }
             }
-            ((ServiceRegistrationImpl) reg).invalidate();
         }
     }
 
     /**
-     * This method retrieves all services registrations for the specified bundle
-     * and invokes <tt>ServiceRegistration.unregister()</tt> on each one. This
-     * method is only called be the framework to clean up after a stopped
-     * bundle.
-     *
+     * This method retrieves all services registrations for the specified
+     * bundle and invokes <tt>ServiceRegistration.unregister()</tt> on each
+     * one. This method is only called be the framework to clean up after
+     * a stopped bundle.
      * @param bundle the bundle whose services should be unregistered.
-     */
-    public void unregisterServices(Bundle bundle)
+     **/
+    public void unregisterServices(final Bundle bundle)
     {
         // Simply remove all service registrations for the bundle.
-        ServiceRegistration[] regs = null;
-        synchronized (this)
-        {
-            regs = m_regsMap.get(bundle);
-        }
+        final List<ServiceRegistration<?>> regs = m_regsMap.remove(bundle);
 
         // Note, there is no race condition here with respect to the
         // bundle registering more services, because its bundle context
@@ -197,27 +220,38 @@ public class ServiceRegistry
         // be able to register more services.
 
         // Unregister each service.
-        for (int i = 0; (regs != null) && (i < regs.length); i++)
+        if (regs != null)
         {
-            if (((ServiceRegistrationImpl) regs[i]).isValid())
+            final List<ServiceRegistration<?>> copyRefs;
+            // there shouldn't be a need to sync, but just to be safe
+            // we create a copy array and use that for iterating
+            synchronized ( regs )
             {
-                regs[i].unregister();
+                copyRefs = new ArrayList<ServiceRegistration<?>>(regs);
+            }
+            for (final ServiceRegistration<?> reg : copyRefs)
+            {
+                if (((ServiceRegistrationImpl) reg).isValid())
+                {
+                    try
+                    {
+                        reg.unregister();
+                    }
+                    catch (final IllegalStateException e)
+                    {
+                        // Ignore exception if the service has already been unregistered
+                    }
+                }
             }
-        }
-
-        // Now remove the bundle itself.
-        synchronized (this)
-        {
-            m_regsMap.remove(bundle);
         }
     }
 
-    public synchronized Collection<ServiceReference<?>> getServiceReferences(String className, SimpleFilter filter)
+    public Collection<Capability> getServiceReferences(final String className, SimpleFilter filter)
     {
         if ((className == null) && (filter == null))
         {
             // Return all services.
-            filter = new SimpleFilter(Constants.OBJECTCLASS, "*", SimpleFilter.PRESENT);
+            filter = new SimpleFilter(null, null, SimpleFilter.MATCH_ALL);
         }
         else if ((className != null) && (filter == null))
         {
@@ -227,24 +261,22 @@ public class ServiceRegistry
         else if ((className != null) && (filter != null))
         {
             // Return services matching the class name and filter.
-            List<Object> filters = new ArrayList<Object>(2);
+            final List<SimpleFilter> filters = new ArrayList<SimpleFilter>(2);
             filters.add(new SimpleFilter(Constants.OBJECTCLASS, className, SimpleFilter.EQ));
             filters.add(filter);
             filter = new SimpleFilter(null, filters, SimpleFilter.AND);
         }
         // else just use the specified filter.
 
-        Set<ServiceRegistrationImpl.ServiceReferenceImpl> matches = m_regCapSet.match(filter, false);
-
-        return (Collection) matches;
+        return m_regCapSet.match(filter, false);
     }
 
-    public synchronized ServiceReference[] getServicesInUse(Bundle bundle)
+    public ServiceReference<?>[] getServicesInUse(final Bundle bundle)
     {
-        UsageCount[] usages = (UsageCount[]) m_inUseMap.get(bundle);
+        final UsageCount[] usages = m_inUseMap.get(bundle);
         if (usages != null)
         {
-            ServiceReference[] refs = new ServiceReference[usages.length];
+            final ServiceReference<?>[] refs = new ServiceReference[usages.length];
             for (int i = 0; i < refs.length; i++)
             {
                 refs[i] = usages[i].m_ref;
@@ -254,199 +286,266 @@ public class ServiceRegistry
         return null;
     }
 
-    public <S> S getService(Bundle bundle, ServiceReference<S> ref)
+    @SuppressWarnings("unchecked")
+    public <S> S getService(final Bundle bundle, final ServiceReference<S> ref, final boolean isServiceObjects)
     {
+        // prototype scope is only possible if called from ServiceObjects
+        final boolean isPrototype = isServiceObjects && ref.getProperty(Constants.SERVICE_SCOPE) == Constants.SCOPE_PROTOTYPE;
         UsageCount usage = null;
         Object svcObj = null;
 
         // Get the service registration.
-        ServiceRegistrationImpl reg =
-                ((ServiceRegistrationImpl.ServiceReferenceImpl) ref).getRegistration();
+        final ServiceRegistrationImpl reg =
+            ((ServiceRegistrationImpl.ServiceReferenceImpl) ref).getRegistration();
 
-        synchronized (this)
+        // We don't allow cycles when we call out to the service factory.
+        if ( reg.currentThreadMarked() )
         {
-            // First make sure that no existing operation is currently
-            // being performed by another thread on the service registration.
-            for (Object o = m_lockedRegsMap.get(reg); (o != null); o = m_lockedRegsMap.get(reg))
-            {
-                // We don't allow cycles when we call out to the service factory.
-                if (o.equals(Thread.currentThread()))
-                {
-                    throw new ServiceException(
-                            "ServiceFactory.getService() resulted in a cycle.",
-                            ServiceException.FACTORY_ERROR,
-                            null);
-                }
-
-                // Otherwise, wait for it to be freed.
-                try
-                {
-                    wait();
-                }
-                catch (InterruptedException ex)
-                {
-                }
-            }
+            throw new ServiceException(
+                    "ServiceFactory.getService() resulted in a cycle.",
+                    ServiceException.FACTORY_ERROR,
+                    null);
+        }
 
-            // Lock the service registration.
-            m_lockedRegsMap.put(reg, Thread.currentThread());
+        try
+        {
+            reg.markCurrentThread();
 
             // Make sure the service registration is still valid.
             if (reg.isValid())
             {
-                // Get the usage count, if any.
-                usage = getUsageCount(bundle, ref);
+                // Get the usage count, or create a new one. If this is a
+                // prototype, the we'll alway create a new one.
+                usage = obtainUsageCount(bundle, ref, null, isPrototype);
 
-                // If we don't have a usage count, then create one and
-                // since the spec says we increment usage count before
-                // actually getting the service object.
-                if (usage == null)
+                // Increment the usage count and grab the already retrieved
+                // service object, if one exists.
+                incrementToPositiveValue(usage.m_count);
+                svcObj = usage.getService();
+
+                if ( isServiceObjects )
                 {
-                    usage = addUsageCount(bundle, ref);
+                    incrementToPositiveValue(usage.m_serviceObjectsCount);
                 }
 
-                // Increment the usage count and grab the already retrieved
-                // service object, if one exists.
-                usage.m_count++;
-                svcObj = usage.m_svcObj;
-            }
-        }
+                // If we have a usage count, but no service object, then we haven't
+                // cached the service object yet, so we need to create one.
+                if (usage != null)
+                {
+                    ServiceHolder holder = null;
 
-        // If we have a usage count, but no service object, then we haven't
-        // cached the service object yet, so we need to create one now without
-        // holding the lock, since we will potentially call out to a service
-        // factory.
-        try
-        {
-            if ((usage != null) && (svcObj == null))
-            {
-                svcObj = reg.getService(bundle);
+                    // There is a possibility that the holder is unset between the compareAndSet() and the get()
+                    // below. If that happens get() returns null and we may have to set a new holder. This is
+                    // why the below section is in a loop.
+                    while (holder == null)
+                    {
+                        ServiceHolder h = new ServiceHolder();
+                        if (usage.m_svcHolderRef.compareAndSet(null, h))
+                        {
+                            holder = h;
+                            try {
+                                svcObj = reg.getService(bundle);
+                                holder.m_service = svcObj;
+                            } finally {
+                                holder.m_latch.countDown();
+                            }
+                        }
+                        else
+                        {
+                            holder = usage.m_svcHolderRef.get();
+                            if (holder != null)
+                            {
+                                boolean interrupted = false;
+                                do
+                                {
+                                    try
+                                    {
+                                        // Need to ensure that the other thread has obtained
+                                        // the service.
+                                        holder.m_latch.await();
+                                        if (interrupted)
+                                        {
+                                            Thread.currentThread().interrupt();
+                                        }
+                                        interrupted = false;
+                                    }
+                                    catch (InterruptedException e)
+                                    {
+                                        interrupted = true;
+                                        Thread.interrupted();
+                                    }
+                                }
+                                while (interrupted);
+                                svcObj = holder.m_service;
+                            }
+                        }
+
+                        // if someone concurrently changed the holder, loop again
+                        if (holder != usage.m_svcHolderRef.get())
+                            holder = null;
+                    }
+                    if (svcObj != null && isPrototype)
+                    {
+                        UsageCount existingUsage = obtainUsageCount(bundle, ref, svcObj, null);
+                        if (existingUsage != null && existingUsage != usage)
+                        {
+                            flushUsageCount(bundle, ref, usage);
+                            usage = existingUsage;
+                            incrementToPositiveValue(usage.m_count);
+                            if ( isServiceObjects )
+                            {
+                                incrementToPositiveValue(usage.m_serviceObjectsCount);
+                            }
+                        }
+                    }
+                }
             }
         }
         finally
         {
-            // If we successfully retrieved a service object, then we should
-            // cache it in the usage count. If not, we should flush the usage
-            // count. Either way, we need to unlock the service registration
-            // so that any threads waiting for it can continue.
-            synchronized (this)
-            {
-                // Before caching the service object, double check to see if
-                // the registration is still valid, since it may have been
-                // unregistered while we didn't hold the lock.
-                if (!reg.isValid() || (svcObj == null))
-                {
-                    flushUsageCount(bundle, ref);
-                }
-                else
-                {
-                    usage.m_svcObj = svcObj;
-                }
-                m_lockedRegsMap.remove(reg);
-                notifyAll();
+            reg.unmarkCurrentThread();
+
+            if (!reg.isValid() || (svcObj == null))
+            {
+                flushUsageCount(bundle, ref, usage);
             }
         }
 
         return (S) svcObj;
     }
 
-    public boolean ungetService(Bundle bundle, ServiceReference ref)
+    // Increment the Atomic Long by 1, and ensure the result is at least 1.
+    // This method uses a loop, optimistic algorithm to do this in a threadsafe
+    // way without locks.
+    private void incrementToPositiveValue(AtomicLong al)
     {
-        UsageCount usage = null;
-        ServiceRegistrationImpl reg =
-                ((ServiceRegistrationImpl.ServiceReferenceImpl) ref).getRegistration();
+        boolean success = false;
 
-        synchronized (this)
+        while (!success)
         {
-            // First make sure that no existing operation is currently
-            // being performed by another thread on the service registration.
-            for (Object o = m_lockedRegsMap.get(reg); (o != null); o = m_lockedRegsMap.get(reg))
-            {
-                // We don't allow cycles when we call out to the service factory.
-                if (o.equals(Thread.currentThread()))
-                {
-                    throw new IllegalStateException(
-                            "ServiceFactory.ungetService() resulted in a cycle.");
-                }
+            long oldVal = al.get();
+            long newVal = Math.max(oldVal + 1L, 1L);
+            checkCountOverflow(newVal);
 
-                // Otherwise, wait for it to be freed.
-                try
-                {
-                    wait();
-                }
-                catch (InterruptedException ex)
-                {
-                }
-            }
+            success = al.compareAndSet(oldVal, newVal);
+        }
+    }
 
-            // Get the usage count.
-            usage = getUsageCount(bundle, ref);
-            // If there is no cached services, then just return immediately.
-            if (usage == null)
-            {
-                return false;
-            }
+    private void checkCountOverflow(long c)
+    {
+        if (c == Long.MAX_VALUE)
+        {
+            throw new ServiceException(
+                    "The use count for the service overflowed.",
+                    ServiceException.UNSPECIFIED,
+                    null);
+        }
+    }
+
+    public boolean ungetService(final Bundle bundle, final ServiceReference<?> ref, final Object svcObj)
+    {
+        final ServiceRegistrationImpl reg =
+            ((ServiceRegistrationImpl.ServiceReferenceImpl) ref).getRegistration();
 
-            // Lock the service registration.
-            m_lockedRegsMap.put(reg, Thread.currentThread());
+        if ( reg.currentThreadMarked() )
+        {
+            throw new IllegalStateException(
+                    "ServiceFactory.ungetService() resulted in a cycle.");
         }
 
-        // If usage count will go to zero, then unget the service
-        // from the registration; we do this outside the lock
-        // since this might call out to the service factory.
         try
         {
-            if (usage.m_count == 1)
+            // Mark the current thread to avoid cycles
+            reg.markCurrentThread();
+
+            // Get the usage count.
+            UsageCount usage = obtainUsageCount(bundle, ref, svcObj, null);
+            // If there are no cached services, then just return immediately.
+            if (usage == null)
             {
-                // Remove reference from usages array.
-                ((ServiceRegistrationImpl.ServiceReferenceImpl) ref)
-                        .getRegistration().ungetService(bundle, usage.m_svcObj);
+                return false;
             }
-        }
-        finally
-        {
-            // Finally, decrement usage count and flush if it goes to zero or
-            // the registration became invalid while we were not holding the
-            // lock. Either way, unlock the service registration so that any
-            // threads waiting for it can continue.
-            synchronized (this)
+            // if this is a call from service objects and the service was not fetched from
+            // there, return false
+            if ( svcObj != null )
             {
-                // Decrement usage count, which spec says should happen after
-                // ungetting the service object.
-                usage.m_count--;
+                if (usage.m_serviceObjectsCount.decrementAndGet() < 0)
+                {
+                    return false;
+                }
+            }
 
-                // If the registration is invalid or the usage count has reached
-                // zero, then flush it.
-                if (!reg.isValid() || (usage.m_count <= 0))
+            // If usage count will go to zero, then unget the service
+            // from the registration.
+            long count = usage.m_count.decrementAndGet();
+            try
+            {
+                if (count <= 0)
                 {
-                    usage.m_svcObj = null;
-                    flushUsageCount(bundle, ref);
+                    ServiceHolder holder = usage.m_svcHolderRef.get();
+                    Object svc = holder != null ? holder.m_service : null;
+
+                    if (svc != null)
+                    {
+                        // Check the count again to ensure that nobody else has just
+                        // obtained the service again
+                        if (usage.m_count.get() <= 0)
+                        {
+                            if (usage.m_svcHolderRef.compareAndSet(holder, null))
+                            {
+                                // Temporarily increase the usage again so that the 
+                                // service factory still sees the usage in the unget
+                                usage.m_count.incrementAndGet();
+                                try
+                                {
+                                    // Remove reference from usages array.
+                                    reg.ungetService(bundle, svc);
+                                }
+                                finally 
+                                {
+                                    // now we can decrease the usage again
+                                    usage.m_count.decrementAndGet();
+                                }
+
+                            }
+                        }
+                    }
                 }
 
-                // Release the registration lock so any waiting threads can
-                // continue.
-                m_lockedRegsMap.remove(reg);
-                notifyAll();
+                return count >= 0;
             }
-        }
+            finally
+            {
+                if (!reg.isValid())
+                {
+                    usage.m_svcHolderRef.set(null);
+                }
 
-        return true;
+                // If the registration is invalid or the usage count for a prototype
+                // reached zero, then flush it. Non-prototype services are not flushed
+                // on ungetService() when they reach 0 as this introduces a race
+                // condition of concurrently the same service is obtained via getService()
+                if (!reg.isValid() || (count <= 0 && svcObj != null))
+                {
+                    flushUsageCount(bundle, ref, usage);
+                }
+            }
+        }
+        finally
+        {
+            reg.unmarkCurrentThread();
+        }
     }
 
+
     /**
-     * This is a utility method to release all services being used by the
-     * specified bundle.
-     *
+     * This is a utility method to release all services being
+     * used by the specified bundle.
      * @param bundle the bundle whose services are to be released.
-     */
-    public void ungetServices(Bundle bundle)
+    **/
+    public void ungetServices(final Bundle bundle)
     {
-        UsageCount[] usages;
-        synchronized (this)
-        {
-            usages = m_inUseMap.get(bundle);
-        }
-
+        UsageCount[] usages = m_inUseMap.get(bundle);
         if (usages == null)
         {
             return;
@@ -459,31 +558,35 @@ public class ServiceRegistry
 
         // Remove each service object from the
         // service cache.
-        for (UsageCount usage : usages)
+        for (int i = 0; i < usages.length; i++)
         {
+            if (usages[i].m_svcHolderRef.get() == null)
+                continue;
+
             // Keep ungetting until all usage count is zero.
-            while (ungetService(bundle, usage.m_ref))
+            while (ungetService(bundle, usages[i].m_ref, usages[i].m_prototype ? usages[i].getService() : null))
             {
                 // Empty loop body.
             }
         }
     }
 
-    public synchronized Bundle[] getUsingBundles(ServiceReference ref)
+    public Bundle[] getUsingBundles(ServiceReference<?> ref)
     {
         Bundle[] bundles = null;
-        for (Map.Entry<Bundle, UsageCount[]> entry : m_inUseMap.entrySet())
+        for (Iterator<Map.Entry<Bundle, UsageCount[]>> iter = m_inUseMap.entrySet().iterator(); iter.hasNext(); )
         {
+            Map.Entry<Bundle, UsageCount[]> entry = iter.next();
             Bundle bundle = entry.getKey();
             UsageCount[] usages = entry.getValue();
-            for (UsageCount usage : usages)
+            for (int useIdx = 0; useIdx < usages.length; useIdx++)
             {
-                if (usage.m_ref.equals(ref))
+                if (usages[useIdx].m_ref.equals(ref) && usages[useIdx].m_count.get() > 0)
                 {
                     // Add the bundle to the array to be returned.
                     if (bundles == null)
                     {
-                        bundles = new Bundle[]{bundle};
+                        bundles = new Bundle[] { bundle };
                     }
                     else
                     {
@@ -498,312 +601,175 @@ public class ServiceRegistry
         return bundles;
     }
 
-    void servicePropertiesModified(ServiceRegistration reg, Dictionary oldProps)
+    void servicePropertiesModified(ServiceRegistration<?> reg, Dictionary<?,?> oldProps)
     {
-        updateHook(reg.getReference());
+        this.hookRegistry.updateHooks(reg.getReference());
         if (m_callbacks != null)
         {
             m_callbacks.serviceChanged(
-                    new ServiceEvent(ServiceEvent.MODIFIED, reg.getReference()), oldProps);
+                new ServiceEvent(ServiceEvent.MODIFIED, reg.getReference()), oldProps);
         }
     }
 
-    private static ServiceRegistration[] addServiceRegistration(
-            ServiceRegistration[] regs, ServiceRegistration reg)
+    /**
+     * Obtain a UsageCount object, by looking for an existing one or creating a new one (if possible).
+     * This method tries to find a UsageCount object in the {@code m_inUseMap}. If one is found then
+     * this is returned, otherwise a UsageCount object will be created, but this can only be done if
+     * the {@code isPrototype} parameter is not {@code null}. If {@code isPrototype} is {@code TRUE}
+     * then a new UsageCount object will always be created.
+     * @param bundle The bundle using the service.
+     * @param ref The Service Reference.
+     * @param svcObj A Service Object, if applicable.
+     * @param isPrototype {@code TRUE} if we know that this is a prototype, {@ FALSE} if we know that
+     * it isn't. There are cases where we don't know (the pure lookup case), in that case use {@code null}.
+     * @return The UsageCount object if it could be obtained, or {@code null} otherwise.
+     */
+    UsageCount obtainUsageCount(Bundle bundle, ServiceReference<?> ref, Object svcObj, Boolean isPrototype)
     {
-        if (regs == null)
-        {
-            regs = new ServiceRegistration[]
-                    {
-                            reg
-                    };
-        }
-        else
-        {
-            ServiceRegistration[] newRegs = new ServiceRegistration[regs.length + 1];
-            System.arraycopy(regs, 0, newRegs, 0, regs.length);
-            newRegs[regs.length] = reg;
-            regs = newRegs;
-        }
-        return regs;
-    }
+        UsageCount usage = null;
 
-    private static ServiceRegistration[] removeServiceRegistration(
-            ServiceRegistration[] regs, ServiceRegistration reg)
-    {
-        for (int i = 0; (regs != null) && (i < regs.length); i++)
+        // This method uses an optimistic concurrency mechanism with a conditional put/replace
+        // on the m_inUseMap. If this fails (because another thread made changes) this thread
+        // retries the operation. This is the purpose of the while loop.
+        boolean success = false;
+        while (!success)
         {
-            if (regs[i].equals(reg))
+            UsageCount[] usages = m_inUseMap.get(bundle);
+
+            // If we know it's a prototype, then we always need to create a new usage count
+            if (!Boolean.TRUE.equals(isPrototype))
             {
-                // If this is the only usage, then point to empty list.
-                if ((regs.length - 1) == 0)
+                for (int i = 0; (usages != null) && (i < usages.length); i++)
                 {
-                    regs = new ServiceRegistration[0];
-                }
-                // Otherwise, we need to do some array copying.
-                else
-                {
-                    ServiceRegistration[] newRegs = new ServiceRegistration[regs.length - 1];
-                    System.arraycopy(regs, 0, newRegs, 0, i);
-                    if (i < newRegs.length)
+                    if (usages[i].m_ref.equals(ref)
+                       && ((svcObj == null && !usages[i].m_prototype) || usages[i].getService() == svcObj))
                     {
-                        System.arraycopy(
-                                regs, i + 1, newRegs, i, newRegs.length - i);
+                        return usages[i];
                     }
-                    regs = newRegs;
                 }
             }
-        }
-        return regs;
-    }
 
-    /**
-     * Utility method to retrieve the specified bundle's usage count for the
-     * specified service reference.
-     *
-     * @param bundle The bundle whose usage counts are being searched.
-     * @param ref    The service reference to find in the bundle's usage counts.
-     * @return The associated usage count or null if not found.
-     */
-    private UsageCount getUsageCount(Bundle bundle, ServiceReference ref)
-    {
-        UsageCount[] usages = (UsageCount[]) m_inUseMap.get(bundle);
-        for (int i = 0; (usages != null) && (i < usages.length); i++)
-        {
-            if (usages[i].m_ref.equals(ref))
+            // We haven't found an existing usage count object so we need to create on. For this we need to
+            // know whether this is a prototype or not.
+            if (isPrototype == null)
             {
-                return usages[i];
+                // If this parameter isn't passed in we can't create a usage count.
+                return null;
             }
-        }
-        return null;
-    }
-
-    /**
-     * Utility method to update the specified bundle's usage count array to
-     * include the specified service. This method should only be called to add a
-     * usage count for a previously unreferenced service. If the service already
-     * has a usage count, then the existing usage count counter simply needs to
-     * be incremented.
-     *
-     * @param bundle The bundle acquiring the service.
-     * @param ref    The service reference of the acquired service.
-     */
-    private UsageCount addUsageCount(Bundle bundle, ServiceReference ref)
-    {
-        UsageCount[] usages = m_inUseMap.get(bundle);
 
-        UsageCount usage = new UsageCount();
-        usage.m_ref = ref;
-
-        if (usages == null)
-        {
-            usages = new UsageCount[]
-                    {
-                            usage
-                    };
-        }
-        else
-        {
-            UsageCount[] newUsages = new UsageCount[usages.length + 1];
-            System.arraycopy(usages, 0, newUsages, 0, usages.length);
-            newUsages[usages.length] = usage;
-            usages = newUsages;
+            // Add a new Usage Count.
+            usage = new UsageCount(ref, isPrototype);
+            if (usages == null)
+            {
+                UsageCount[] newUsages = new UsageCount[] { usage };
+                success = m_inUseMap.putIfAbsent(bundle, newUsages) == null;
+            }
+            else
+            {
+                UsageCount[] newUsages = new UsageCount[usages.length + 1];
+                System.arraycopy(usages, 0, newUsages, 0, usages.length);
+                newUsages[usages.length] = usage;
+                success = m_inUseMap.replace(bundle, usages, newUsages);
+            }
         }
-
-        m_inUseMap.put(bundle, usages);
-
         return usage;
     }
 
     /**
      * Utility method to flush the specified bundle's usage count for the
-     * specified service reference. This should be called to completely remove
-     * the associated usage count object for the specified service reference. If
-     * the goal is to simply decrement the usage, then get the usage count and
-     * decrement its counter. This method will also remove the specified bundle
-     * from the "in use" map if it has no more usage counts after removing the
-     * usage count for the specified service reference.
-     *
+     * specified service reference. This should be called to completely
+     * remove the associated usage count object for the specified service
+     * reference. If the goal is to simply decrement the usage, then get
+     * the usage count and decrement its counter. This method will also
+     * remove the specified bundle from the "in use" map if it has no more
+     * usage counts after removing the usage count for the specified service
+     * reference.
      * @param bundle The bundle whose usage count should be removed.
-     * @param ref    The service reference whose usage count should be removed.
-     */
-    private void flushUsageCount(Bundle bundle, ServiceReference ref)
-    {
-        UsageCount[] usages = (UsageCount[]) m_inUseMap.get(bundle);
-        for (int i = 0; (usages != null) && (i < usages.length); i++)
-        {
-            if (usages[i].m_ref.equals(ref))
+     * @param ref The service reference whose usage count should be removed.
+    **/
+    void flushUsageCount(Bundle bundle, ServiceReference<?> ref, UsageCount uc)
+    {
+        // This method uses an optimistic concurrency mechanism with conditional modifications
+        // on the m_inUseMap. If this fails (because another thread made changes) this thread
+        // retries the operation. This is the purpose of the while loop.
+        boolean success = false;
+        while (!success)
+        {
+            UsageCount[] usages = m_inUseMap.get(bundle);
+            final UsageCount[] orgUsages = usages;
+            for (int i = 0; (usages != null) && (i < usages.length); i++)
             {
-                // If this is the only usage, then point to empty list.
-                if ((usages.length - 1) == 0)
+                if ((uc == null && usages[i].m_ref.equals(ref)) || (uc == usages[i]))
                 {
-                    usages = null;
-                }
-                // Otherwise, we need to do some array copying.
-                else
-                {
-                    UsageCount[] newUsages = new UsageCount[usages.length - 1];
-                    System.arraycopy(usages, 0, newUsages, 0, i);
-                    if (i < newUsages.length)
+                    // If this is the only usage, then point to empty list.
+                    if ((usages.length - 1) == 0)
                     {
-                        System.arraycopy(
+                        usages = null;
+                    }
+                    // Otherwise, we need to do some array copying.
+                    else
+                    {
+                        UsageCount[] newUsages = new UsageCount[usages.length - 1];
+                        System.arraycopy(usages, 0, newUsages, 0, i);
+                        if (i < newUsages.length)
+                        {
+                            System.arraycopy(
                                 usages, i + 1, newUsages, i, newUsages.length - i);
+                        }
+                        usages = newUsages;
+                        i--;
                     }
-                    usages = newUsages;
                 }
             }
-        }
-
-        if (usages != null)
-        {
-            m_inUseMap.put(bundle, usages);
-        }
-        else
-        {
-            m_inUseMap.remove(bundle);
-        }
-    }
 
-    //
-    // Hook-related methods.
-    //
-    boolean isHookBlackListed(ServiceReference sr)
-    {
-        return m_blackList.containsKey(sr);
-    }
+            if (usages == orgUsages)
+                return; // no change in map
 
-    void blackListHook(ServiceReference sr)
-    {
-        m_blackList.put(sr, sr);
-    }
-
-    static boolean isHook(String[] classNames, Class<?> hookClass, Object svcObj)
-    {
-        // For a service factory, we can only match names.
-        if (svcObj instanceof ServiceFactory)
-        {
-            for (String className : classNames)
-            {
-                if (className.equals(hookClass.getName()))
-                {
-                    return true;
-                }
-            }
-        }
-
-        // For a service object, check if its class matches.
-        if (hookClass.isAssignableFrom(svcObj.getClass()))
-        {
-            // But still only if it is registered under that interface.
-            String hookName = hookClass.getName();
-            for (String className : classNames)
+            if (orgUsages != null)
             {
-                if (className.equals(hookName))
-                {
-                    return true;
-                }
+                if (usages != null)
+                    success = m_inUseMap.replace(bundle, orgUsages, usages);
+                else
+                    success = m_inUseMap.remove(bundle, orgUsages);
             }
         }
-        return false;
     }
 
-    private void addHooks(String[] classNames, Object svcObj, ServiceReference<?> ref)
+    public HookRegistry getHookRegistry()
     {
-        for (Class<?> hookClass : m_hookClasses)
-        {
-            if (isHook(classNames, hookClass, svcObj))
-            {
-                synchronized (m_allHooks)
-                {
-                    Set<ServiceReference<?>> hooks = m_allHooks.get(hookClass);
-                    if (hooks == null)
-                    {
-                        hooks = new TreeSet<ServiceReference<?>>(Collections.reverseOrder());
-                        m_allHooks.put(hookClass, hooks);
-                    }
-                    hooks.add(ref);
-                }
-            }
-        }
+        return this.hookRegistry;
     }
 
-    private void updateHook(ServiceReference ref)
+    static class UsageCount
     {
-        // We maintain the hooks sorted, so if ranking has changed for example,
-        // we need to ensure the order remains correct by resorting the hooks.
-        Object svcObj = ((ServiceRegistrationImpl.ServiceReferenceImpl) ref)
-                .getRegistration().getService();
-        String[] classNames = (String[]) ref.getProperty(Constants.OBJECTCLASS);
-
-        for (Class<?> hookClass : m_hookClasses)
-        {
-            if (isHook(classNames, hookClass, svcObj))
-            {
-                synchronized (m_allHooks)
-                {
-                    Set<ServiceReference<?>> hooks = m_allHooks.get(hookClass);
-                    if (hooks != null)
-                    {
-                        List<ServiceReference<?>> refs = new ArrayList<ServiceReference<?>>(hooks);
-                        hooks.clear();
-                        hooks.addAll(refs);
-                    }
-                }
-            }
-        }
-    }
+        final ServiceReference<?> m_ref;
+        final boolean m_prototype;
 
-    private void removeHook(ServiceReference ref)
-    {
-        Object svcObj = ((ServiceRegistrationImpl.ServiceReferenceImpl) ref)
-                .getRegistration().getService();
-        String[] classNames = (String[]) ref.getProperty(Constants.OBJECTCLASS);
+        final AtomicLong m_count = new AtomicLong();
+        final AtomicLong m_serviceObjectsCount = new AtomicLong();
+        final AtomicReference<ServiceHolder> m_svcHolderRef = new AtomicReference<ServiceHolder>();
 
-        for (Class<?> hookClass : m_hookClasses)
+        UsageCount(final ServiceReference<?> ref, final boolean isPrototype)
         {
-            if (isHook(classNames, hookClass, svcObj))
-            {
-                synchronized (m_allHooks)
-                {
-                    Set<ServiceReference<?>> hooks = m_allHooks.get(hookClass);
-                    if (hooks != null)
-                    {
-                        hooks.remove(ref);
-                        if (hooks.isEmpty())
-                        {
-                            m_allHooks.remove(hookClass);
-                        }
-                    }
-                }
-            }
+            m_ref = ref;
+            m_prototype = isPrototype;
         }
-    }
 
-    public <S> Set<ServiceReference<S>> getHooks(Class<S> hookClass)
-    {
-        synchronized (m_allHooks)
+        Object getService()
         {
-            @SuppressWarnings("unchecked")
-            Set<ServiceReference<S>> hooks = (Set) m_allHooks.get(hookClass);
-            if (hooks != null)
-            {
-                SortedSet<ServiceReference<S>> sorted = new TreeSet<ServiceReference<S>>(Collections.reverseOrder());
-                sorted.addAll(hooks);
-                return sorted;
-            }
-            return Collections.emptySet();
+            ServiceHolder sh = m_svcHolderRef.get();
+            return sh == null ? null : sh.m_service;
         }
     }
 
-    private static class UsageCount
+    static class ServiceHolder
     {
-        public int m_count = 0;
-        public ServiceReference m_ref = null;
-        public Object m_svcObj = null;
+        final CountDownLatch m_latch = new CountDownLatch(1);
+        volatile Object m_service;
     }
 
     public interface ServiceRegistryCallbacks
     {
-        void serviceChanged(ServiceEvent event, Dictionary<String, ?> oldProps);
+        void serviceChanged(ServiceEvent event, Dictionary<?,?> oldProps);
     }
-}
\ No newline at end of file
+}

Modified: felix/trunk/connect/src/main/java/org/apache/felix/connect/felix/framework/capabilityset/CapabilitySet.java
URL: http://svn.apache.org/viewvc/felix/trunk/connect/src/main/java/org/apache/felix/connect/felix/framework/capabilityset/CapabilitySet.java?rev=1858443&r1=1858442&r2=1858443&view=diff
==============================================================================
--- felix/trunk/connect/src/main/java/org/apache/felix/connect/felix/framework/capabilityset/CapabilitySet.java (original)
+++ felix/trunk/connect/src/main/java/org/apache/felix/connect/felix/framework/capabilityset/CapabilitySet.java Tue Apr 30 16:21:29 2019
@@ -7,7 +7,7 @@
  * "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
+ *   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
@@ -20,45 +20,53 @@ package org.apache.felix.connect.felix.f
 
 import java.lang.reflect.Array;
 import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
+import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
-import java.util.TreeMap;
-
-import org.osgi.resource.Capability;
+import java.util.SortedMap;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ConcurrentSkipListMap;
 
 import org.apache.felix.connect.felix.framework.util.StringComparator;
+import org.apache.felix.connect.felix.framework.wiring.BundleCapabilityImpl;
+import org.osgi.framework.Version;
+import org.osgi.framework.VersionRange;
+import org.osgi.framework.wiring.BundleCapability;
+import org.osgi.resource.Capability;
 
-public class CapabilitySet<T extends Capability>
+public class CapabilitySet
 {
-    private final Map<String, Map<Object, Set<T>>> m_indices;
-    private final Set<T> m_capSet = new HashSet<T>();
+    private final SortedMap<String, Map<Object, Set<BundleCapability>>> m_indices; // Should also be concurrent!
+    private final Set<Capability> m_capSet = Collections.newSetFromMap(new ConcurrentHashMap<Capability, Boolean>());
 
-    public CapabilitySet(List<String> indexProps, boolean caseSensitive)
+
+    public CapabilitySet(final List<String> indexProps, final boolean caseSensitive)
     {
         m_indices = (caseSensitive)
-                ? new TreeMap<String, Map<Object, Set<T>>>()
-                : new TreeMap<String, Map<Object, Set<T>>>(
-                new StringComparator(false));
+            ? new ConcurrentSkipListMap<String, Map<Object, Set<BundleCapability>>>()
+            : new ConcurrentSkipListMap<String, Map<Object, Set<BundleCapability>>>(
+                StringComparator.COMPARATOR);
         for (int i = 0; (indexProps != null) && (i < indexProps.size()); i++)
         {
             m_indices.put(
-                    indexProps.get(i), new HashMap<Object, Set<T>>());
+                indexProps.get(i), new ConcurrentHashMap<Object, Set<BundleCapability>>());
         }
     }
 
-    public void addCapability(T cap)
+    public void addCapability(final BundleCapability cap)
     {
         m_capSet.add(cap);
 
         // Index capability.
-        for (Entry<String, Map<Object, Set<T>>> entry : m_indices.entrySet())
+        for (Entry<String, Map<Object, Set<BundleCapability>>> entry : m_indices.entrySet())
         {
             Object value = cap.getAttributes().get(entry.getKey());
             if (value != null)
@@ -68,7 +76,8 @@ public class CapabilitySet<T extends Cap
                     value = convertArrayToList(value);
                 }
 
-                Map<Object, Set<T>> index = entry.getValue();
+                ConcurrentMap<Object, Set<BundleCapability>> index =
+                        (ConcurrentMap<Object, Set<BundleCapability>>) entry.getValue();
 
                 if (value instanceof Collection)
                 {
@@ -87,22 +96,20 @@ public class CapabilitySet<T extends Cap
     }
 
     private void indexCapability(
-            Map<Object, Set<T>> index, T cap, Object capValue)
+        ConcurrentMap<Object, Set<BundleCapability>> index, BundleCapability cap, Object capValue)
     {
-        Set<T> caps = index.get(capValue);
-        if (caps == null)
-        {
-            caps = new HashSet<T>();
-            index.put(capValue, caps);
-        }
+        Set<BundleCapability> caps = Collections.newSetFromMap(new ConcurrentHashMap<BundleCapability, Boolean>());
+        Set<BundleCapability> prevval = index.putIfAbsent(capValue, caps);
+        if (prevval != null)
+            caps = prevval;
         caps.add(cap);
     }
 
-    public void removeCapability(T cap)
+    public void removeCapability(final BundleCapability cap)
     {
         if (m_capSet.remove(cap))
         {
-            for (Entry<String, Map<Object, Set<T>>> entry : m_indices.entrySet())
+            for (Entry<String, Map<Object, Set<BundleCapability>>> entry : m_indices.entrySet())
             {
                 Object value = cap.getAttributes().get(entry.getKey());
                 if (value != null)
@@ -112,7 +119,7 @@ public class CapabilitySet<T extends Cap
                         value = convertArrayToList(value);
                     }
 
-                    Map<Object, Set<T>> index = entry.getValue();
+                    Map<Object, Set<BundleCapability>> index = entry.getValue();
 
                     if (value instanceof Collection)
                     {
@@ -132,9 +139,9 @@ public class CapabilitySet<T extends Cap
     }
 
     private void deindexCapability(
-            Map<Object, Set<T>> index, T cap, Object value)
+        Map<Object, Set<BundleCapability>> index, BundleCapability cap, Object value)
     {
-        Set<T> caps = index.get(value);
+        Set<BundleCapability> caps = index.get(value);
         if (caps != null)
         {
             caps.remove(cap);
@@ -145,18 +152,17 @@ public class CapabilitySet<T extends Cap
         }
     }
 
-    public Set<T> match(SimpleFilter sf, boolean obeyMandatory)
+    public Set<Capability> match(final SimpleFilter sf, final boolean obeyMandatory)
     {
-        Set<T> matches = match(m_capSet, sf);
-        return /* (obeyMandatory)
+        final Set<Capability> matches = match(m_capSet, sf);
+        return (obeyMandatory)
             ? matchMandatory(matches, sf)
-            : */ matches;
+            : matches;
     }
 
-    @SuppressWarnings("unchecked")
-    private Set<T> match(Set<T> caps, SimpleFilter sf)
+    private Set<Capability> match(Set<Capability> caps, final SimpleFilter sf)
     {
-        Set<T> matches = new HashSet<T>();
+        Set<Capability> matches = Collections.newSetFromMap(new ConcurrentHashMap<Capability, Boolean>());
 
         if (sf.getOperation() == SimpleFilter.MATCH_ALL)
         {
@@ -168,7 +174,7 @@ public class CapabilitySet<T extends Cap
             // For AND we calculate the intersection of each subfilter.
             // We can short-circuit the AND operation if there are no
             // remaining capabilities.
-            List<SimpleFilter> sfs = (List<SimpleFilter>) sf.getValue();
+            final List<SimpleFilter> sfs = (List<SimpleFilter>) sf.getValue();
             for (int i = 0; (caps.size() > 0) && (i < sfs.size()); i++)
             {
                 matches = match(caps, sfs.get(i));
@@ -180,9 +186,9 @@ public class CapabilitySet<T extends Cap
             // Evaluate each subfilter against the remaining capabilities.
             // For OR we calculate the union of each subfilter.
             List<SimpleFilter> sfs = (List<SimpleFilter>) sf.getValue();
-            for (SimpleFilter sf1 : sfs)
+            for (int i = 0; i < sfs.size(); i++)
             {
-                matches.addAll(match(caps, sf1));
+                matches.addAll(match(caps, sfs.get(i)));
             }
         }
         else if (sf.getOperation() == SimpleFilter.NOT)
@@ -191,27 +197,31 @@ public class CapabilitySet<T extends Cap
             // For OR we calculate the union of each subfilter.
             matches.addAll(caps);
             List<SimpleFilter> sfs = (List<SimpleFilter>) sf.getValue();
-            for (SimpleFilter sf1 : sfs)
+            for (int i = 0; i < sfs.size(); i++)
             {
-                matches.removeAll(match(caps, sf1));
+                matches.removeAll(match(caps, sfs.get(i)));
             }
         }
         else
         {
-            Map<Object, Set<T>> index = m_indices.get(sf.getName());
+            Map<Object, Set<BundleCapability>> index = m_indices.get(sf.getName());
             if ((sf.getOperation() == SimpleFilter.EQ) && (index != null))
             {
-                Set<T> existingCaps = index.get(sf.getValue());
+                Set<BundleCapability> existingCaps = index.get(sf.getValue());
                 if (existingCaps != null)
                 {
                     matches.addAll(existingCaps);
-                    matches.retainAll(caps);
+                    if (caps != m_capSet)
+                    {
+                        matches.retainAll(caps);
+                    }
                 }
             }
             else
             {
-                for (T cap : caps)
+                for (Iterator<Capability> it = caps.iterator(); it.hasNext(); )
                 {
+                    Capability cap = it.next();
                     Object lhs = cap.getAttributes().get(sf.getName());
                     if (lhs != null)
                     {
@@ -227,13 +237,12 @@ public class CapabilitySet<T extends Cap
         return matches;
     }
 
-    /*  public static boolean matches(BundleCapability cap, SimpleFilter sf)
-      {
-          return matchesInternal(cap, sf) && matchMandatory(cap, sf);
-      }
-  */
-    @SuppressWarnings("unchecked")
-    private boolean matchesInternal(T cap, SimpleFilter sf)
+    public static boolean matches(Capability cap, SimpleFilter sf)
+    {
+        return matchesInternal(cap, sf) && matchMandatory(cap, sf);
+    }
+
+    private static boolean matchesInternal(Capability cap, SimpleFilter sf)
     {
         boolean matched = true;
 
@@ -269,9 +278,9 @@ public class CapabilitySet<T extends Cap
             // Evaluate each subfilter against the remaining capabilities.
             // For OR we calculate the union of each subfilter.
             List<SimpleFilter> sfs = (List<SimpleFilter>) sf.getValue();
-            for (SimpleFilter sf1 : sfs)
+            for (int i = 0; i < sfs.size(); i++)
             {
-                matched = !(matchesInternal(cap, sf1));
+                matched = !(matchesInternal(cap, sfs.get(i)));
             }
         }
         else
@@ -287,13 +296,12 @@ public class CapabilitySet<T extends Cap
         return matched;
     }
 
-    /*
-    private Set<T> matchMandatory(
-        Set<T> caps, SimpleFilter sf)
+    private static Set<Capability> matchMandatory(
+        Set<Capability> caps, SimpleFilter sf)
     {
-        for (Iterator<T> it = caps.iterator(); it.hasNext(); )
+        for (Iterator<Capability> it = caps.iterator(); it.hasNext(); )
         {
-            T cap = it.next();
+            Capability cap = it.next();
             if (!matchMandatory(cap, sf))
             {
                 it.remove();
@@ -302,13 +310,13 @@ public class CapabilitySet<T extends Cap
         return caps;
     }
 
-    private boolean matchMandatory(T cap, SimpleFilter sf)
+    private static boolean matchMandatory(Capability cap, SimpleFilter sf)
     {
         Map<String, Object> attrs = cap.getAttributes();
         for (Entry<String, Object> entry : attrs.entrySet())
         {
-            if (((T) cap).isAttributeMandatory(entry.getKey())
-                && !matchMandatoryAttrbute(entry.getKey(), sf))
+            if (((BundleCapabilityImpl) cap).isAttributeMandatory(entry.getKey())
+                && !matchMandatoryAttribute(entry.getKey(), sf))
             {
                 return false;
             }
@@ -316,7 +324,7 @@ public class CapabilitySet<T extends Cap
         return true;
     }
 
-    private boolean matchMandatoryAttrbute(String attrName, SimpleFilter sf)
+    private static boolean matchMandatoryAttribute(String attrName, SimpleFilter sf)
     {
         if ((sf.getName() != null) && sf.getName().equals(attrName))
         {
@@ -336,11 +344,11 @@ public class CapabilitySet<T extends Cap
             }
         }
         return false;
-    }*/
+    }
 
-    private static final Class<?>[] STRING_CLASS = new Class[]{String.class};
+    private static final Class<?>[] STRING_CLASS = new Class[] { String.class };
+    private static final String VALUE_OF_METHOD_NAME = "valueOf";
 
-    @SuppressWarnings("unchecked")
     private static boolean compare(Object lhs, Object rhsUnknown, int op)
     {
         if (lhs == null)
@@ -355,6 +363,26 @@ public class CapabilitySet<T extends Cap
             return true;
         }
 
+        //Need a special case here when lhs is a Version and rhs is a VersionRange
+        //Version is comparable so we need to check this first
+        if(lhs instanceof Version && op == SimpleFilter.EQ)
+        {
+            Object rhs = null;
+            try
+            {
+                rhs = coerceType(lhs, (String) rhsUnknown);
+            }
+            catch (Exception ex)
+            {
+                //Do nothing will check later if rhs is null
+            }
+
+            if(rhs != null && rhs instanceof VersionRange)
+            {
+                return ((VersionRange)rhs).includes((Version)lhs);
+            }
+        }
+
         // If the type is comparable, then we can just return the
         // result immediately.
         if (lhs instanceof Comparable)
@@ -384,43 +412,67 @@ public class CapabilitySet<T extends Cap
 
             switch (op)
             {
-            case SimpleFilter.EQ:
-                try
-                {
-                    return (((Comparable) lhs).compareTo(rhs) == 0);
-                }
-                catch (Exception ex)
-                {
-                    return false;
-                }
-            case SimpleFilter.GTE:
-                try
-                {
-                    return (((Comparable) lhs).compareTo(rhs) >= 0);
-                }
-                catch (Exception ex)
-                {
-                    return false;
-                }
-            case SimpleFilter.LTE:
-                try
-                {
-                    return (((Comparable) lhs).compareTo(rhs) <= 0);
-                }
-                catch (Exception ex)
-                {
-                    return false;
-                }
-            case SimpleFilter.APPROX:
-                return compareApproximate(lhs, rhs);
-            case SimpleFilter.SUBSTRING:
-                return SimpleFilter.compareSubstring((List<String>) rhs, (String) lhs);
-            default:
-                throw new RuntimeException(
+                case SimpleFilter.EQ :
+                    try
+                    {
+                        return (((Comparable) lhs).compareTo(rhs) == 0);
+                    }
+                    catch (Exception ex)
+                    {
+                        return false;
+                    }
+                case SimpleFilter.GTE :
+                    try
+                    {
+                        return (((Comparable) lhs).compareTo(rhs) >= 0);
+                    }
+                    catch (Exception ex)
+                    {
+                        return false;
+                    }
+                case SimpleFilter.LTE :
+                    try
+                    {
+                        return (((Comparable) lhs).compareTo(rhs) <= 0);
+                    }
+                    catch (Exception ex)
+                    {
+                        return false;
+                    }
+                case SimpleFilter.APPROX :
+                    return compareApproximate(lhs, rhs);
+                case SimpleFilter.SUBSTRING :
+                    return SimpleFilter.compareSubstring((List<String>) rhs, (String) lhs);
+                default:
+                    throw new RuntimeException(
                         "Unknown comparison operator: " + op);
             }
         }
+        // Booleans do not implement comparable, so special case them.
+        else if (lhs instanceof Boolean)
+        {
+            Object rhs;
+            try
+            {
+                rhs = coerceType(lhs, (String) rhsUnknown);
+            }
+            catch (Exception ex)
+            {
+                return false;
+            }
 
+            switch (op)
+            {
+                case SimpleFilter.EQ :
+                case SimpleFilter.GTE :
+                case SimpleFilter.LTE :
+                case SimpleFilter.APPROX :
+                    return (lhs.equals(rhs));
+                default:
+                    throw new RuntimeException(
+                        "Unknown comparison operator: " + op);
+            }
+        }
 
         // If the LHS is not a comparable or boolean, check if it is an
         // array. If so, convert it to a list so we can treat it as a
@@ -434,9 +486,9 @@ public class CapabilitySet<T extends Cap
         // of the collection until a match is found.
         if (lhs instanceof Collection)
         {
-            for (Object o : ((Collection) lhs))
+            for (Iterator iter = ((Collection) lhs).iterator(); iter.hasNext(); )
             {
-                if (compare(o, rhsUnknown, op))
+                if (compare(iter.next(), rhsUnknown, op))
                 {
                     return true;
                 }
@@ -446,7 +498,7 @@ public class CapabilitySet<T extends Cap
         }
 
         // Spec says SUBSTRING is false for all types other than string.
-        if (op == SimpleFilter.SUBSTRING)
+        if ((op == SimpleFilter.SUBSTRING) && !(lhs instanceof String))
         {
             return false;
         }
@@ -468,12 +520,12 @@ public class CapabilitySet<T extends Cap
         if (rhs instanceof String)
         {
             return removeWhitespace((String) lhs)
-                    .equalsIgnoreCase(removeWhitespace((String) rhs));
+                .equalsIgnoreCase(removeWhitespace((String) rhs));
         }
         else if (rhs instanceof Character)
         {
             return Character.toLowerCase(((Character) lhs))
-                    == Character.toLowerCase(((Character) rhs));
+                == Character.toLowerCase(((Character) rhs));
         }
         return lhs.equals(rhs);
     }
@@ -502,14 +554,18 @@ public class CapabilitySet<T extends Cap
 
         // Try to convert the RHS type to the LHS type by using
         // the string constructor of the LHS class, if it has one.
-        Object rhs;
+        Object rhs = null;
         try
         {
             // The Character class is a special case, since its constructor
             // does not take a string, so handle it separately.
             if (lhs instanceof Character)
             {
-                rhs = rhsString.charAt(0);
+                rhs = new Character(rhsString.charAt(0));
+            }
+            else if(lhs instanceof Version && rhsString.indexOf(',') >= 0)
+            {
+                rhs = new VersionRange(rhsString);
             }
             else
             {
@@ -518,39 +574,60 @@ public class CapabilitySet<T extends Cap
                 {
                     rhsString = rhsString.trim();
                 }
-                Constructor ctor = lhs.getClass().getConstructor(STRING_CLASS);
-                ctor.setAccessible(true);
-                rhs = ctor.newInstance(rhsString);
+
+                try
+                {
+                    // Try to find a suitable static valueOf method
+                    Method valueOfMethod = lhs.getClass().getDeclaredMethod(
+                        VALUE_OF_METHOD_NAME, STRING_CLASS);
+                    if (valueOfMethod.getReturnType().isAssignableFrom(lhs.getClass())
+                        && ((valueOfMethod.getModifiers() & Modifier.STATIC) > 0))
+                    {
+                        valueOfMethod.setAccessible(true);
+                        rhs = valueOfMethod.invoke(null, new Object[] { rhsString });
+                    }
+                }
+                catch (Exception ex)
+                {
+                    // Static valueOf fails, try the next conversion mechanism
+                }
+
+                if (rhs == null)
+                {
+                    Constructor ctor = lhs.getClass().getConstructor(STRING_CLASS);
+                    ctor.setAccessible(true);
+                    rhs = ctor.newInstance(new Object[] { rhsString });
+                }
             }
         }
         catch (Exception ex)
         {
             throw new Exception(
-                    "Could not instantiate class "
-                            + lhs.getClass().getName()
-                            + " from string constructor with argument '"
-                            + rhsString + "' because " + ex);
+                "Could not instantiate class "
+                    + lhs.getClass().getName()
+                    + " from string constructor with argument '"
+                    + rhsString + "' because " + ex);
         }
 
         return rhs;
     }
 
     /**
-     * This is an ugly utility method to convert an array of primitives to an
-     * array of primitive wrapper objects. This method simplifies processing
-     * LDAP filters since the special case of primitive arrays can be ignored.
-     *
+     * This is an ugly utility method to convert an array of primitives
+     * to an array of primitive wrapper objects. This method simplifies
+     * processing LDAP filters since the special case of primitive arrays
+     * can be ignored.
      * @param array An array of primitive types.
      * @return An corresponding array using pritive wrapper objects.
-     */
-    private static List<Object> convertArrayToList(Object array)
+    **/
+    private static List convertArrayToList(Object array)
     {
         int len = Array.getLength(array);
-        List<Object> list = new ArrayList<Object>(len);
+        List list = new ArrayList(len);
         for (int i = 0; i < len; i++)
         {
             list.add(Array.get(array, i));
         }
         return list;
     }
-}
\ No newline at end of file
+}