aries-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From hugh...@apache.org
Subject svn commit: r1623965 - in /aries/trunk/jndi/jndi-core/src/main/java/org/apache/aries/jndi: ContextHelper.java DirObjectFactoryHelper.java ObjectFactoryHelper.java startup/Activator.java tracker/ServiceTrackerCustomizers.java
Date Wed, 10 Sep 2014 10:52:58 GMT
Author: hughesj
Date: Wed Sep 10 10:52:57 2014
New Revision: 1623965

URL: http://svn.apache.org/r1623965
Log:
ARIES-1068: JNDI lookup performance bad when security enabled

Modified:
    aries/trunk/jndi/jndi-core/src/main/java/org/apache/aries/jndi/ContextHelper.java
    aries/trunk/jndi/jndi-core/src/main/java/org/apache/aries/jndi/DirObjectFactoryHelper.java
    aries/trunk/jndi/jndi-core/src/main/java/org/apache/aries/jndi/ObjectFactoryHelper.java
    aries/trunk/jndi/jndi-core/src/main/java/org/apache/aries/jndi/startup/Activator.java
    aries/trunk/jndi/jndi-core/src/main/java/org/apache/aries/jndi/tracker/ServiceTrackerCustomizers.java

Modified: aries/trunk/jndi/jndi-core/src/main/java/org/apache/aries/jndi/ContextHelper.java
URL: http://svn.apache.org/viewvc/aries/trunk/jndi/jndi-core/src/main/java/org/apache/aries/jndi/ContextHelper.java?rev=1623965&r1=1623964&r2=1623965&view=diff
==============================================================================
--- aries/trunk/jndi/jndi-core/src/main/java/org/apache/aries/jndi/ContextHelper.java (original)
+++ aries/trunk/jndi/jndi-core/src/main/java/org/apache/aries/jndi/ContextHelper.java Wed
Sep 10 10:52:57 2014
@@ -96,7 +96,7 @@ public final class ContextHelper {
         
         if (refs != null) {
           for (final ServiceReference finderRef : refs) {
-            URLObjectFactoryFinder finder = (URLObjectFactoryFinder) Utils.getServicePrivileged(ctx,
finderRef);
+            URLObjectFactoryFinder finder = (URLObjectFactoryFinder) ServiceTrackerCustomizers.URLOBJFACTORYFINDER_CACHE.getServiceFromRef(ctx,finderRef);
                 
             if (finder != null) {
               ObjectFactory f = finder.findFactory(urlScheme, environment);
@@ -104,8 +104,6 @@ public final class ContextHelper {
               if (f != null) {
                 result = new ServicePair<ObjectFactory>(ctx, finderRef, f);
                 break;
-              } else {
-                ctx.ungetService(finderRef);
               }
             }
           }
@@ -174,15 +172,11 @@ public final class ContextHelper {
                 if (references != null) {
                     Context initialContext = null;
                     for (ServiceReference reference : references) {
-                        InitialContextFactory factory = (InitialContextFactory) Utils.getServicePrivileged(context,
reference);
-                        try {
-                            initialContext = factory.getInitialContext(environment);
-                            if (initialContext != null) {
-                              provider = new SingleContextProvider(context, reference, initialContext);
-                              break;
-                          }
-                        } finally {
-                            if (provider == null) context.ungetService(reference);
+                        InitialContextFactory factory = (InitialContextFactory) ServiceTrackerCustomizers.ICF_CACHE.getServiceFromRef(context,
reference);
+                        initialContext = factory.getInitialContext(environment);
+                        if (initialContext != null) {
+                          provider = new SingleContextProvider(context, reference, initialContext);
+                          break;
                         }
                     }
                 }
@@ -192,14 +186,10 @@ public final class ContextHelper {
             
             if (ref != null) {
               Context initialContext = null;
-              InitialContextFactory factory = (InitialContextFactory) Utils.getServicePrivileged(context,
ref);
+              InitialContextFactory factory = (InitialContextFactory) ServiceTrackerCustomizers.ICF_CACHE.getServiceFromRef(context,
ref);
               if (factory != null) {
-                try {
-                    initialContext = factory.getInitialContext(environment);
-                    provider = new SingleContextProvider(context, ref, initialContext);
-                } finally {
-                    if (provider == null) context.ungetService(ref);
-                }
+                initialContext = factory.getInitialContext(environment);
+                provider = new SingleContextProvider(context, ref, initialContext);
               }
             }
             
@@ -228,7 +218,7 @@ public final class ContextHelper {
         if (refs != null) {
             InitialContextFactory factory = null;
             for (ServiceReference ref : refs) {                    
-                InitialContextFactoryBuilder builder = (InitialContextFactoryBuilder) Utils.getServicePrivileged(context,
ref);
+                InitialContextFactoryBuilder builder = (InitialContextFactoryBuilder) ServiceTrackerCustomizers.ICFB_CACHE.getServiceFromRef(context,
ref);
                 try {
                   factory = builder.createInitialContextFactory(environment);
                 } catch (NamingException ne) {
@@ -240,14 +230,8 @@ public final class ContextHelper {
                 }
                 
                 if (factory != null) {
-                  try {
-                    provider = new SingleContextProvider(context, ref, factory.getInitialContext(environment));
-                  } finally {
-                    if (provider == null) context.ungetService(ref); // we didn't get something
back, so this was no good.
-                  }
+                  provider = new SingleContextProvider(context, ref, factory.getInitialContext(environment));
                   break;
-                } else {
-                  context.ungetService(ref); // we didn't get something back, so this was
no good.
                 }
             }
         }

Modified: aries/trunk/jndi/jndi-core/src/main/java/org/apache/aries/jndi/DirObjectFactoryHelper.java
URL: http://svn.apache.org/viewvc/aries/trunk/jndi/jndi-core/src/main/java/org/apache/aries/jndi/DirObjectFactoryHelper.java?rev=1623965&r1=1623964&r2=1623965&view=diff
==============================================================================
--- aries/trunk/jndi/jndi-core/src/main/java/org/apache/aries/jndi/DirObjectFactoryHelper.java
(original)
+++ aries/trunk/jndi/jndi-core/src/main/java/org/apache/aries/jndi/DirObjectFactoryHelper.java
Wed Sep 10 10:52:57 2014
@@ -31,13 +31,18 @@ import javax.naming.spi.DirObjectFactory
 import javax.naming.spi.ObjectFactory;
 import javax.naming.spi.ObjectFactoryBuilder;
 
+import org.apache.aries.jndi.tracker.ServiceTrackerCustomizers;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.ServiceReference;
 
 public class DirObjectFactoryHelper extends ObjectFactoryHelper implements DirObjectFactory
{
 
+    private ServiceTrackerCustomizers.ContextServiceTrackerCustomizer dirObjFactorySTC =
null;
+
+  
     public DirObjectFactoryHelper(BundleContext defaultContext, BundleContext callerContext)
{
         super(defaultContext, callerContext);
+        dirObjFactorySTC = ServiceTrackerCustomizers.getOrRegisterServiceTracker(callerContext,
DirObjectFactory.class.getName());
     }
     
     public Object getObjectInstance(Object obj,
@@ -96,20 +101,15 @@ public class DirObjectFactoryHelper exte
         throws Exception {
         
         Object result = null;
-        ServiceReference[] refs = Utils.getReferencesPrivileged(callerContext, DirObjectFactory.class);
+        ServiceReference[] refs = dirObjFactorySTC.getServiceRefs(); 
         if (refs != null) {
             Arrays.sort(refs, Utils.SERVICE_REFERENCE_COMPARATOR);
             for (ServiceReference ref : refs) {
               
                 if (canCallObjectFactory(obj, ref)) {
-                    DirObjectFactory factory = (DirObjectFactory) Utils.getServicePrivileged(callerContext,
ref);
-    
-                    try {
-                        result = factory.getObjectInstance(obj, name, nameCtx, environment,
attrs);
-                    } finally {
-                        callerContext.ungetService(ref);
-                    }
-    
+                    DirObjectFactory factory = (DirObjectFactory) dirObjFactorySTC.getService(ref);
+                    result = factory.getObjectInstance(obj, name, nameCtx, environment, attrs);
+                    
                     // if the result comes back and is not null and not the reference
                     // object then we should return the result, so break out of the
                     // loop we are in.
@@ -149,15 +149,11 @@ public class DirObjectFactoryHelper exte
                                                    Attributes attrs)
         throws Exception {
 
-        Tuple<ServiceReference, ObjectFactory> tuple = ObjectFactoryHelper.findObjectFactoryByClassName(defaultContext,
className);
+        Tuple<ServiceReference, ObjectFactory> tuple = ObjectFactoryHelper.findObjectFactoryByClassName(defaultStC,
className);
         Object result = null;
         
         if (tuple.second != null) {
-            try {
-                result = ((DirObjectFactory) tuple.second).getObjectInstance(reference, name,
nameCtx, environment, attrs);
-            } finally {
-                defaultContext.ungetService(tuple.first);
-            }
+            result = ((DirObjectFactory) tuple.second).getObjectInstance(reference, name,
nameCtx, environment, attrs);
         }
 
         return (result == null) ? obj : result;
@@ -170,17 +166,15 @@ public class DirObjectFactoryHelper exte
                                                                Attributes attrs) 
         throws Exception {
         ObjectFactory factory = null;
-        ServiceReference[] refs = Utils.getReferencesPrivileged(callerContext, ObjectFactoryBuilder.class);
+        ServiceReference[] refs = objFactoryBuilderStC.getServiceRefs();
         if (refs != null) {
             Arrays.sort(refs, Utils.SERVICE_REFERENCE_COMPARATOR);
             for (ServiceReference ref : refs) {
-                ObjectFactoryBuilder builder = (ObjectFactoryBuilder) Utils.getServicePrivileged(callerContext,
ref);
+                ObjectFactoryBuilder builder = (ObjectFactoryBuilder) objFactoryBuilderStC.getService(ref);
                 try {
                     factory = builder.createObjectFactory(obj, environment);
                 } catch (NamingException e) {
                     // TODO: log it
-                } finally {
-                    callerContext.ungetService(ref);
                 }
                 if (factory != null) {
                     break;

Modified: aries/trunk/jndi/jndi-core/src/main/java/org/apache/aries/jndi/ObjectFactoryHelper.java
URL: http://svn.apache.org/viewvc/aries/trunk/jndi/jndi-core/src/main/java/org/apache/aries/jndi/ObjectFactoryHelper.java?rev=1623965&r1=1623964&r2=1623965&view=diff
==============================================================================
--- aries/trunk/jndi/jndi-core/src/main/java/org/apache/aries/jndi/ObjectFactoryHelper.java
(original)
+++ aries/trunk/jndi/jndi-core/src/main/java/org/apache/aries/jndi/ObjectFactoryHelper.java
Wed Sep 10 10:52:57 2014
@@ -20,7 +20,10 @@ package org.apache.aries.jndi;
 
 import java.security.AccessController;
 import java.security.PrivilegedAction;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.Arrays;
+import java.util.Vector;
+import java.util.Iterator;
 import java.util.Enumeration;
 import java.util.Hashtable;
 import java.util.logging.Level;
@@ -39,19 +42,36 @@ import javax.naming.spi.ObjectFactory;
 import javax.naming.spi.ObjectFactoryBuilder;
 
 import org.apache.aries.util.service.registry.ServicePair;
+import org.apache.aries.jndi.tracker.ServiceTrackerCustomizers;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.InvalidSyntaxException;
 import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
+
 
 public class ObjectFactoryHelper implements ObjectFactory {
     
     protected BundleContext defaultContext;
     protected BundleContext callerContext;
+    
+    protected ServiceTrackerCustomizers.ContextServiceTrackerCustomizer objFactoryBuilderStC
= null;
+    protected ServiceTrackerCustomizers.ContextServiceTrackerCustomizer objFactoryStC = null;
+    protected ServiceTrackerCustomizers.ContextServiceTrackerCustomizer defaultStC = null;
+    
     private static final Logger logger = Logger.getLogger(ObjectFactoryHelper.class.getName());
 
     public ObjectFactoryHelper(BundleContext defaultContext, BundleContext callerContext)
{
         this.defaultContext = defaultContext;
         this.callerContext = callerContext;
+
+        //Create service trackers for the contexts to allow caching of services
+        objFactoryBuilderStC = ServiceTrackerCustomizers.getOrRegisterServiceTracker(callerContext,
ObjectFactoryBuilder.class.getName());
+        
+        objFactoryStC = ServiceTrackerCustomizers.getOrRegisterServiceTracker(callerContext,
ObjectFactory.class.getName());
+        
+        defaultStC = ServiceTrackerCustomizers.getOrRegisterServiceTracker(defaultContext,
ObjectFactory.class.getName());
+      
     }
 
     public Object getObjectInstance(Object obj,
@@ -180,21 +200,19 @@ public class ObjectFactoryHelper impleme
                                                            Hashtable<?, ?> environment)

         throws Exception {
         Object result = null;
-        ServiceReference[] refs = Utils.getReferencesPrivileged(callerContext, ObjectFactory.class);
+        ServiceReference[] refs = objFactoryStC.getServiceRefs();
             
         if (refs != null) {
             Arrays.sort(refs, Utils.SERVICE_REFERENCE_COMPARATOR);
             
             for (ServiceReference ref : refs) {
               if (canCallObjectFactory(obj, ref)) {
-                ObjectFactory factory = (ObjectFactory) Utils.getServicePrivileged(callerContext,
ref);
+                ObjectFactory factory = (ObjectFactory) objFactoryStC.getService(ref);
 
                 try {
                     result = factory.getObjectInstance(obj, name, nameCtx, environment);
                 } catch (NamingException ne) {
                   // Ignore this since we are doing last ditch finding, another OF might
work.
-                } finally {
-                    callerContext.ungetService(ref);
                 }
 
                 // if the result comes back and is not null and not the reference
@@ -269,25 +287,15 @@ public class ObjectFactoryHelper impleme
         return (result == null) ? obj : result;
     }
 
-    static Tuple<ServiceReference,ObjectFactory> findObjectFactoryByClassName(final
BundleContext ctx, final String className) {
+    static Tuple<ServiceReference,ObjectFactory> findObjectFactoryByClassName(final
ServiceTrackerCustomizers.ContextServiceTrackerCustomizer ctxCache, final String className)
{
         return AccessController.doPrivileged(new PrivilegedAction<Tuple<ServiceReference,ObjectFactory>>()
{
             public Tuple<ServiceReference,ObjectFactory> run() {
-                ServiceReference serviceReference = null;
-                
-                try {
-                    ServiceReference[] refs = ctx.getServiceReferences(className, null);
-                    if (refs != null && refs.length > 0) {
-                        serviceReference = refs[0];
-                    }
-                } catch (InvalidSyntaxException e) {
-                    // should not happen
-                    throw new RuntimeException(Utils.MESSAGES.getMessage("null.is.invalid.filter"),
e);
-                }
+                ServiceReference serviceReference = ctxCache.getServiceRef(className);
 
                 ObjectFactory factory = null;
                 
                 if (serviceReference != null) {
-                    factory = (ObjectFactory) ctx.getService(serviceReference);         
  
+                    factory = (ObjectFactory) ctxCache.getService(serviceReference);
                 }
                 
                 return new Tuple<ServiceReference, ObjectFactory>(serviceReference,
factory);
@@ -303,15 +311,11 @@ public class ObjectFactoryHelper impleme
                                                    Hashtable<?, ?> environment) 
         throws Exception {
         
-        Tuple<ServiceReference,ObjectFactory> tuple = findObjectFactoryByClassName(defaultContext,
className);
+        Tuple<ServiceReference,ObjectFactory> tuple = findObjectFactoryByClassName(defaultStC,
className);
         Object result = null;
         
         if (tuple.second != null) {
-            try {
-                result = tuple.second.getObjectInstance(reference, name, nameCtx, environment);
-            } finally {
-                defaultContext.ungetService(tuple.first);
-            }
+            result = tuple.second.getObjectInstance(reference, name, nameCtx, environment);
         }
 
         return (result == null) ? obj : result;
@@ -325,17 +329,15 @@ public class ObjectFactoryHelper impleme
         
         ObjectFactory factory = null;
         
-        ServiceReference[] refs = Utils.getReferencesPrivileged(callerContext, ObjectFactoryBuilder.class);
+        ServiceReference[] refs = objFactoryBuilderStC.getServiceRefs();
         if (refs != null) {
             Arrays.sort(refs, Utils.SERVICE_REFERENCE_COMPARATOR);
             for (ServiceReference ref : refs) {
-                ObjectFactoryBuilder builder = (ObjectFactoryBuilder) Utils.getServicePrivileged(callerContext,
ref);
+                ObjectFactoryBuilder builder = (ObjectFactoryBuilder) objFactoryBuilderStC.getService(ref);
                 try {
                     factory = builder.createObjectFactory(obj, environment);
                 } catch (NamingException e) {
                     // TODO: log it
-                } finally {
-                    callerContext.ungetService(ref);
                 }
                 if (factory != null) {
                     break;

Modified: aries/trunk/jndi/jndi-core/src/main/java/org/apache/aries/jndi/startup/Activator.java
URL: http://svn.apache.org/viewvc/aries/trunk/jndi/jndi-core/src/main/java/org/apache/aries/jndi/startup/Activator.java?rev=1623965&r1=1623964&r2=1623965&view=diff
==============================================================================
--- aries/trunk/jndi/jndi-core/src/main/java/org/apache/aries/jndi/startup/Activator.java
(original)
+++ aries/trunk/jndi/jndi-core/src/main/java/org/apache/aries/jndi/startup/Activator.java
Wed Sep 10 10:52:57 2014
@@ -37,11 +37,13 @@ import org.apache.aries.jndi.Utils;
 import org.apache.aries.jndi.spi.EnvironmentAugmentation;
 import org.apache.aries.jndi.tracker.ServiceTrackerCustomizers;
 import org.apache.aries.jndi.urls.URLObjectFactoryFinder;
+import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleActivator;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.ServiceReference;
 import org.osgi.service.jndi.JNDIContextManager;
 import org.osgi.service.jndi.JNDIProviderAdmin;
+import org.osgi.util.tracker.BundleTracker;
 import org.osgi.util.tracker.ServiceTracker;
 import org.osgi.util.tracker.ServiceTrackerCustomizer;
 import org.slf4j.Logger;
@@ -62,13 +64,14 @@ public class Activator implements Bundle
     private static ServiceTracker initialContextFactories;
     private static ServiceTracker objectFactories;
     private static ServiceTracker environmentAugmentors;
+    private BundleTracker bt = null;
 
     public void start(BundleContext context) {
 
         initialContextFactories = initServiceTracker(context, InitialContextFactory.class,
ServiceTrackerCustomizers.ICF_CACHE);
         objectFactories = initServiceTracker(context, ObjectFactory.class, ServiceTrackerCustomizers.URL_FACTORY_CACHE);
-        icfBuilders = initServiceTracker(context, InitialContextFactoryBuilder.class, ServiceTrackerCustomizers.LAZY);
-        urlObjectFactoryFinders = initServiceTracker(context, URLObjectFactoryFinder.class,
ServiceTrackerCustomizers.LAZY);
+        icfBuilders = initServiceTracker(context, InitialContextFactoryBuilder.class, ServiceTrackerCustomizers.ICFB_CACHE);
+        urlObjectFactoryFinders = initServiceTracker(context, URLObjectFactoryFinder.class,
ServiceTrackerCustomizers.URLOBJFACTORYFINDER_CACHE);
         environmentAugmentors = initServiceTracker(context, EnvironmentAugmentation.class,
null);
 
         try {
@@ -108,6 +111,9 @@ public class Activator implements Bundle
         context.registerService(JNDIContextManager.class.getName(),
                                 new ContextManagerServiceFactory(),
                                 null);
+        //Start the bundletracker that clears out the cache. (only interested in stopping
events)
+        bt = new BundleTracker(context,Bundle.STOPPING,new ServiceTrackerCustomizers.CacheBundleTrackerCustomizer());
+        bt.open();
     }
 
     private String getClassName(Class<?> expectedType)
@@ -151,6 +157,11 @@ public class Activator implements Bundle
         objectFactories.close();
         initialContextFactories.close();
         environmentAugmentors.close();
+                
+        if (bt != null) {
+          bt.close();
+        }
+        
     }
 
     /*

Modified: aries/trunk/jndi/jndi-core/src/main/java/org/apache/aries/jndi/tracker/ServiceTrackerCustomizers.java
URL: http://svn.apache.org/viewvc/aries/trunk/jndi/jndi-core/src/main/java/org/apache/aries/jndi/tracker/ServiceTrackerCustomizers.java?rev=1623965&r1=1623964&r2=1623965&view=diff
==============================================================================
--- aries/trunk/jndi/jndi-core/src/main/java/org/apache/aries/jndi/tracker/ServiceTrackerCustomizers.java
(original)
+++ aries/trunk/jndi/jndi-core/src/main/java/org/apache/aries/jndi/tracker/ServiceTrackerCustomizers.java
Wed Sep 10 10:52:57 2014
@@ -18,25 +18,40 @@
  */
 package org.apache.aries.jndi.tracker;
 
+import java.lang.IllegalStateException;
+import java.security.PrivilegedExceptionAction;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 
 import javax.naming.spi.InitialContextFactory;
 
+import org.apache.aries.jndi.Utils;
+import org.apache.aries.jndi.startup.Activator;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleEvent;
 import org.osgi.framework.Constants;
 import org.osgi.framework.ServiceReference;
 import org.osgi.service.jndi.JNDIConstants;
+import org.osgi.util.tracker.BundleTrackerCustomizer;
+import org.osgi.util.tracker.ServiceTracker;
 import org.osgi.util.tracker.ServiceTrackerCustomizer;
 
 
 public class ServiceTrackerCustomizers 
 {
+  private static final Logger LOGGER = Logger.getLogger(ServiceTrackerCustomizers.class.getName());
+  
   public static interface CachingServiceTracker extends ServiceTrackerCustomizer {
     public ServiceReference find(String identifier);
+    public Object getServiceFromRef(BundleContext bCtx, ServiceReference ref);
   }
   
   private static abstract class BaseCachingServiceTracker implements CachingServiceTracker
{
@@ -45,6 +60,36 @@ public class ServiceTrackerCustomizers 
     /** A list of service references that are being tracked */
     protected List<ServiceReference> trackedReferences = new ArrayList<ServiceReference>();
 
+    protected ConcurrentHashMap<BundleContext, ConcurrentHashMap<ServiceReference,Object>>
ctxServiceRefServiceCache =
+        new ConcurrentHashMap<BundleContext, ConcurrentHashMap<ServiceReference,Object>>();
+    
+    private void clearCache(Bundle b) {
+      for (BundleContext bCtx : ctxServiceRefServiceCache.keySet()) {
+        Bundle cacheB = null;
+        try {
+          cacheB = bCtx.getBundle();
+        } catch (IllegalStateException ise) {
+          if (LOGGER.isLoggable(Level.FINE)) LOGGER.fine("BaseCachingServiceTracker.clearCache
IllegalStateException caught getting bundle on " + bCtx);
+        }
+        if (cacheB == null || cacheB.equals(b)) {
+          Object removedObj = ctxServiceRefServiceCache.remove(bCtx);
+          if (LOGGER.isLoggable(Level.FINE)) LOGGER.fine("BaseCachingServiceTracker.clearCache
Removed " + removedObj);
+        }
+      }
+    }
+    
+    public Object getServiceFromRef(BundleContext bCtx, ServiceReference ref) {
+      if (ctxServiceRefServiceCache.get(bCtx) == null) {
+        ctxServiceRefServiceCache.put(bCtx, new ConcurrentHashMap<ServiceReference,Object>());
+      }
+      ConcurrentHashMap<ServiceReference,Object> ctxServiceCache = ctxServiceRefServiceCache.get(bCtx);
+      Object service = ctxServiceCache.get(ref);
+      if (service == null) {
+        service = Utils.getServicePrivileged(bCtx,ref);
+        ctxServiceCache.put(ref, service);
+      }
+      return service;
+    }
     public ServiceReference find(String identifier) 
     {
       return cache.get(identifier);
@@ -87,6 +132,10 @@ public class ServiceTrackerCustomizers 
       for (String interfaceName : keysToProcess) {
         cache.remove(interfaceName, reference);
       }
+      //Work through the contexts to clear out this serviceref
+      for (BundleContext bCtx : ctxServiceRefServiceCache.keySet())  {
+        ctxServiceRefServiceCache.get(bCtx).remove(reference);
+      }
     }
 
     public void modifiedService(ServiceReference reference, Object service) { }
@@ -116,6 +165,21 @@ public class ServiceTrackerCustomizers 
     }
   };
   
+  //An empty BaseCachingServiceTracker, just to use the caching.
+  public static final CachingServiceTracker ICFB_CACHE = new BaseCachingServiceTracker()
{
+    @Override
+    protected List<String> getProperty(ServiceReference reference) {
+      return new ArrayList<String>();
+    }};
+  
+  //An empty BaseCachingServiceTracker, just to use the caching.
+  public static final CachingServiceTracker URLOBJFACTORYFINDER_CACHE = new BaseCachingServiceTracker()
{
+    @Override
+    protected List<String> getProperty(ServiceReference reference) {
+      return new ArrayList<String>();
+    }};
+    
+  
   // TODO we should probably cope with the url.scheme property changing.
   public static final CachingServiceTracker URL_FACTORY_CACHE = new BaseCachingServiceTracker()
{
     protected List<String> getProperty(ServiceReference reference) {
@@ -134,4 +198,266 @@ public class ServiceTrackerCustomizers 
       return result;
     }
   };
+  
+//Links between the BundleContext, the classes they have registered an interest in, and the
service references from those.
+  private static ConcurrentHashMap<BundleContext,ConcurrentHashMap<String,CopyOnWriteArrayList<ServiceReference>>>
srCache 
+            = new ConcurrentHashMap<BundleContext, ConcurrentHashMap<String,CopyOnWriteArrayList<ServiceReference>>>();
+  
+  //Links between references and services
+  private static ConcurrentHashMap<ServiceReference, Object> refToService = new ConcurrentHashMap<ServiceReference,
Object>();
+  
+  //Links between BundleContexts and the classes they have registered an interest in, and
the ServiceTrackerCustomizers 
+  //running for those classes.
+  private static ConcurrentHashMap<BundleContext, ConcurrentHashMap<String,ContextServiceTrackerCustomizer>>
serviceTrackerCustomizerCache 
+              = new ConcurrentHashMap<BundleContext, ConcurrentHashMap<String,ContextServiceTrackerCustomizer>>();
+  
+  //Maintain a list of service trackers created, so they can be closed off.
+  private static ConcurrentHashMap<ServiceTrackerCustomizer, ServiceTracker> serviceTrackers
= new ConcurrentHashMap<ServiceTrackerCustomizer, ServiceTracker>();
+  
+  public static ContextServiceTrackerCustomizer getOrRegisterServiceTracker(BundleContext
ctx,String clazz) {
+    
+    ConcurrentHashMap<String,ContextServiceTrackerCustomizer> stCacheForCtx = serviceTrackerCustomizerCache.get(ctx);
+    if (stCacheForCtx == null) {
+      synchronized (serviceTrackerCustomizerCache) {
+        //Check the ctx is still not known, and not set by another thread.
+        stCacheForCtx = serviceTrackerCustomizerCache.get(ctx);
+        if (stCacheForCtx == null) {
+          if (LOGGER.isLoggable(Level.FINE)) LOGGER.fine("Creating cache details for context
" + ctx);
+          stCacheForCtx = new ConcurrentHashMap<String, ServiceTrackerCustomizers.ContextServiceTrackerCustomizer>();
+          serviceTrackerCustomizerCache.put(ctx, stCacheForCtx);
+        }
+      }
+    }
+
+    ContextServiceTrackerCustomizer stc = stCacheForCtx.get(clazz);
+    if (stc == null) {
+      synchronized (stCacheForCtx) {
+        stc = stCacheForCtx.get(clazz);
+        if (stc == null) {
+          if (LOGGER.isLoggable(Level.FINE)) LOGGER.log(Level.FINE, "Creating " + clazz +
" tracker for " + ctx);
+          final BundleContext _ctx = ctx;
+          stc = new ServiceTrackerCustomizers.ContextServiceTrackerCustomizer(_ctx, clazz);
+          ServiceTracker st = new ServiceTracker(
+              _ctx,
+              clazz,
+              stc);
+          serviceTrackers.put(stc, st);
+          stCacheForCtx.put(clazz,stc);
+          if (LOGGER.isLoggable(Level.FINE)) LOGGER.log(Level.FINE, "Opening " + clazz +
" tracker " + st);
+          final ServiceTracker _st = st;
+          try {
+            Utils.doPrivileged(new PrivilegedExceptionAction<Object>() {
+              public Object run() throws Exception {
+                  _st.open(true);
+                  return null;
+              }            
+            });
+            
+          } catch (Exception e) {
+            if (LOGGER.isLoggable(Level.FINE)) LOGGER.log(Level.FINE, "Exception opening
" + clazz + " tracker " + e.getMessage());       
+          }
+        }
+      }
+    }
+    return stc;
+    
+  }
+  
+  //Class to track services for a given context
+  public static class ContextServiceTrackerCustomizer implements ServiceTrackerCustomizer
{
+    
+    private String clazz;
+    final BundleContext _callerContext;
+    
+    public BundleContext getCallerContext() {
+      return _callerContext;
+    }
+    
+    private ConcurrentHashMap<String,ServiceReference> classToSR = new ConcurrentHashMap<String,ServiceReference>();
+    
+    public ContextServiceTrackerCustomizer(BundleContext _callerContext, String clazz) {
+      this._callerContext = _callerContext;
+      this.clazz = clazz;
+    }
+    
+    public Object addingService(ServiceReference reference) {
+      if (LOGGER.isLoggable(Level.FINE)) LOGGER.log(Level.FINE, "addingService: " + reference
+ " for context " + _callerContext + " class " + clazz);
+      Object service = null;
+      //Get the appropriate cache for references
+      ConcurrentHashMap<String,CopyOnWriteArrayList<ServiceReference>> ctxCache
= srCache.get(_callerContext);
+      if(ctxCache == null) {
+      if (LOGGER.isLoggable(Level.FINE)) LOGGER.log(Level.FINE, "Creating cache for " + _callerContext);
+      ctxCache = new ConcurrentHashMap<String,CopyOnWriteArrayList<ServiceReference>>();
+        srCache.put(_callerContext,ctxCache);
+      }
+      
+      if (!ctxCache.contains(clazz)) {
+        if (LOGGER.isLoggable(Level.FINE)) LOGGER.log(Level.FINE, "Creating class based cache
for " + clazz);
+          ctxCache.put(clazz,new CopyOnWriteArrayList<ServiceReference>());
+      }
+      CopyOnWriteArrayList<ServiceReference> cache = ctxCache.get(clazz);
+      //Now see if the cache already has the ServiceReference (presumably it shouldn't)
+      if (!cache.contains(reference)) {
+        cache.add(reference);
+        service = _callerContext.getService(reference);
+        refToService.put(reference, service);
+        if (LOGGER.isLoggable(Level.FINE)) LOGGER.log(Level.FINE, "Storing reference in cache:
" + reference + " and service " + service);
+      } else {
+      service = refToService.get(reference);
+      if (LOGGER.isLoggable(Level.FINE)) LOGGER.log(Level.FINE, "Reference already in cache:
" + reference + " returning service " + service);
+      }
+      //Get a list of the classNames the service is registered under, and add to the class
-> ServiceReference list
+      String[] classNames = (String[]) reference.getProperty(Constants.OBJECTCLASS);
+      if (classNames != null) {
+        for (String cl : classNames) {
+          if (LOGGER.isLoggable(Level.FINE)) LOGGER.fine("Storing link from class " + cl
+ " to reference " + reference);
+          classToSR.put(cl, reference);
+        }
+      }
+      //Presumably we'll always want to track this reference.
+      return service;
+    }
+    public void modifiedService(ServiceReference reference,
+        java.lang.Object service) {
+      //TODO do anything here??
+      if (LOGGER.isLoggable(Level.FINE)) LOGGER.log(Level.FINE, "modifiedService: " + reference);
+    }
+    
+    public void removedService(ServiceReference reference,
+        java.lang.Object service) {
+      //Unget the service to maintain the count correctly (got in addingService)
+      if (LOGGER.isLoggable(Level.FINE)) LOGGER.log(Level.FINE, "removedService: " + reference
+ " object: " + service);
+      try {
+        _callerContext.ungetService(reference);
+      } catch (IllegalStateException e) {
+        //Shouldn't matter that we get an IllegalStateException here.
+        if (LOGGER.isLoggable(Level.FINE)) LOGGER.log(Level.FINE, "IllegalStateException
ungetting " + reference + " from " + _callerContext);
+      }
+      Object removedService = refToService.remove(reference);
+      if (LOGGER.isLoggable(Level.FINE)) LOGGER.log(Level.FINE, "refToService removed: "
+ removedService);
+      //Get the appropriate cache for references
+      if(srCache.containsKey(_callerContext)) {
+        CopyOnWriteArrayList<ServiceReference> cache = srCache.get(_callerContext).get(clazz);
+        if (cache != null && cache.contains(reference)) {
+          if (LOGGER.isLoggable(Level.FINE)) LOGGER.log(Level.FINE, "removing reference from
cache");
+          cache.remove(reference);
+        } else {
+          if (LOGGER.isLoggable(Level.FINE)) LOGGER.log(Level.FINE, "cache did not contain
reference to remove");
+        }
+      } else {
+        if (LOGGER.isLoggable(Level.FINE)) LOGGER.log(Level.FINE, "removedService: no cache
for callerContext: " + _callerContext);
+      }
+    }
+    
+    public ServiceReference getServiceRef(String clazz) {
+      ServiceReference retObj = classToSR.get(clazz);
+      if (LOGGER.isLoggable(Level.FINE)) LOGGER.fine("getServiceRef: Returning " + retObj);
+      return retObj;
+    }
+    
+    
+    public ServiceReference[] getServiceRefs() {
+      ServiceReference[] refs = null;
+      if (srCache.containsKey(_callerContext)) {
+        CopyOnWriteArrayList<ServiceReference> cache = srCache.get(_callerContext).get(clazz);
+        if (LOGGER.isLoggable(Level.FINE)) LOGGER.log(Level.FINE, "Getting service refs from
" + cache  + " for " + _callerContext);
+        if (cache != null) {
+        refs = cache.toArray(new ServiceReference[cache.size()]); 
+        if (LOGGER.isLoggable(Level.FINE)) LOGGER.log(Level.FINE, "Found refs: " + refs.length);
+        } else {
+          if (LOGGER.isLoggable(Level.FINE))LOGGER.log(Level.FINE, "Cache for class " + clazz
+ " in context " + _callerContext + " does not exist");
+        }
+      } else {
+        if (LOGGER.isLoggable(Level.FINE)) LOGGER.log(Level.FINE, "Cache for context " +
_callerContext + " does not exist");
+      }
+      return refs;
+    }
+    
+    
+    public Object getService(ServiceReference ref) {
+      Object obj = refToService.get(ref);
+      if (LOGGER.isLoggable(Level.FINE)) LOGGER.log(Level.FINE, "getService returning: "
+ obj);
+      return obj; 
+    }
+  }
+  
+  public static class CacheBundleTrackerCustomizer implements BundleTrackerCustomizer {
+
+    @Override
+    public Object addingBundle(Bundle arg0, BundleEvent arg1) {
+      //Request that all bundles are tracked, as even if it's not in the cache, it might
be later...
+      return arg0;
+    }
+
+    @Override
+    public void modifiedBundle(Bundle arg0, BundleEvent arg1, Object arg2) {
+      
+    }
+
+    @Override
+    public void removedBundle(Bundle arg0, BundleEvent arg1, Object arg2) {
+      //Work through srCache to find the bundle by matching to the context
+      if (LOGGER.isLoggable(Level.FINE)) LOGGER.fine("removedBundle: Bundle " + arg0);
+      for (BundleContext bCtx : srCache.keySet()) {
+        Bundle cacheB = null;
+        try {
+          cacheB = bCtx.getBundle();
+        } catch (IllegalStateException ise) {
+          if (LOGGER.isLoggable(Level.FINE)) LOGGER.fine("CacheBundleTrackerCustomizer.removedBundle
IllegalStateException caught getting bundle on " + bCtx);
+        }
+        //If we found no bundle on the context, or one that matches, then remove it.
+        if (cacheB == null || cacheB.equals(arg0)) {
+          if (LOGGER.isLoggable(Level.FINE)) LOGGER.fine("Found matching bundleContext "
+ bCtx);
+          //Removing the bundle in the cache, so clear it out.
+          ConcurrentHashMap<String,CopyOnWriteArrayList<ServiceReference>> classesCached
= srCache.remove(bCtx);
+          if (classesCached != null) {
+            for (String clazz : classesCached.keySet()) {
+              CopyOnWriteArrayList<ServiceReference> serviceRefs = classesCached.get(clazz);
+              //Now work through the serviceRefs, and clear out the refsToService cache
+              if (serviceRefs != null) {
+                for (ServiceReference serviceRef : serviceRefs.toArray(new ServiceReference[serviceRefs.size()]))
{
+                  Object service = refToService.remove(serviceRef);
+                  //Unget the service from the framework, as it won't be required now
+                  if (service != null) {
+                    try {
+                      bCtx.ungetService(serviceRef);
+                    } catch (IllegalStateException e) {
+                      //Shouldn't matter that we get an IllegalStateException here.
+                      if (LOGGER.isLoggable(Level.FINE)) LOGGER.log(Level.FINE, "CacheBundleTrackerCustomizer.removedBundle
IllegalStateException ungetting " + serviceRef + " from " + bCtx);
+                    }
+                  }
+                }
+              }
+            }
+          }
+          //Remove the service tracker customizer and stop the service tracker
+          ConcurrentHashMap<String,ContextServiceTrackerCustomizer> trackedClasses
= serviceTrackerCustomizerCache.remove(bCtx);
+          if (trackedClasses != null) {
+            for (String classes : trackedClasses.keySet()) {
+              ServiceTracker st = serviceTrackers.remove(trackedClasses.get(classes));
+              if (st != null) {
+                final ServiceTracker _st = st;
+                try {
+                  if (LOGGER.isLoggable(Level.FINE)) LOGGER.fine("Closing ServiceTracker
" + st);
+                  Utils.doPrivileged(new PrivilegedExceptionAction<Object>() {
+                    public Object run() throws Exception {
+                        _st.close();
+                        return null;
+                    }            
+                  });
+                } catch (Exception e) {
+                  //TODO logging?       
+                }
+              }
+            }
+          }
+        }
+      }
+      //Remove bundle from BaseCachingServiceTrackers
+      ((BaseCachingServiceTracker) ICF_CACHE).clearCache(arg0);
+      ((BaseCachingServiceTracker) ICFB_CACHE).clearCache(arg0);
+      ((BaseCachingServiceTracker) URL_FACTORY_CACHE).clearCache(arg0);
+      ((BaseCachingServiceTracker) URLOBJFACTORYFINDER_CACHE).clearCache(arg0);
+    }
+  }
 }



Mime
View raw message