Return-Path: X-Original-To: apmail-aries-commits-archive@www.apache.org Delivered-To: apmail-aries-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 0572611B03 for ; Wed, 10 Sep 2014 10:53:22 +0000 (UTC) Received: (qmail 23992 invoked by uid 500); 10 Sep 2014 10:53:21 -0000 Delivered-To: apmail-aries-commits-archive@aries.apache.org Received: (qmail 23926 invoked by uid 500); 10 Sep 2014 10:53:21 -0000 Mailing-List: contact commits-help@aries.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@aries.apache.org Delivered-To: mailing list commits@aries.apache.org Received: (qmail 23915 invoked by uid 99); 10 Sep 2014 10:53:21 -0000 Received: from athena.apache.org (HELO athena.apache.org) (140.211.11.136) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 10 Sep 2014 10:53:21 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=5.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 10 Sep 2014 10:53:18 +0000 Received: from eris.apache.org (localhost [127.0.0.1]) by eris.apache.org (Postfix) with ESMTP id A49BA23889BB; Wed, 10 Sep 2014 10:52:58 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit 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 -0000 To: commits@aries.apache.org From: hughesj@apache.org X-Mailer: svnmailer-1.0.9 Message-Id: <20140910105258.A49BA23889BB@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org 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(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 tuple = ObjectFactoryHelper.findObjectFactoryByClassName(defaultContext, className); + Tuple 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 findObjectFactoryByClassName(final BundleContext ctx, final String className) { + static Tuple findObjectFactoryByClassName(final ServiceTrackerCustomizers.ContextServiceTrackerCustomizer ctxCache, final String className) { return AccessController.doPrivileged(new PrivilegedAction>() { public Tuple 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, factory); @@ -303,15 +311,11 @@ public class ObjectFactoryHelper impleme Hashtable environment) throws Exception { - Tuple tuple = findObjectFactoryByClassName(defaultContext, className); + Tuple 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 trackedReferences = new ArrayList(); + protected ConcurrentHashMap> ctxServiceRefServiceCache = + new ConcurrentHashMap>(); + + 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()); + } + ConcurrentHashMap 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 getProperty(ServiceReference reference) { + return new ArrayList(); + }}; + + //An empty BaseCachingServiceTracker, just to use the caching. + public static final CachingServiceTracker URLOBJFACTORYFINDER_CACHE = new BaseCachingServiceTracker() { + @Override + protected List getProperty(ServiceReference reference) { + return new ArrayList(); + }}; + + // TODO we should probably cope with the url.scheme property changing. public static final CachingServiceTracker URL_FACTORY_CACHE = new BaseCachingServiceTracker() { protected List 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>> srCache + = new ConcurrentHashMap>>(); + + //Links between references and services + private static ConcurrentHashMap refToService = new ConcurrentHashMap(); + + //Links between BundleContexts and the classes they have registered an interest in, and the ServiceTrackerCustomizers + //running for those classes. + private static ConcurrentHashMap> serviceTrackerCustomizerCache + = new ConcurrentHashMap>(); + + //Maintain a list of service trackers created, so they can be closed off. + private static ConcurrentHashMap serviceTrackers = new ConcurrentHashMap(); + + public static ContextServiceTrackerCustomizer getOrRegisterServiceTracker(BundleContext ctx,String clazz) { + + ConcurrentHashMap 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(); + 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() { + 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 classToSR = new ConcurrentHashMap(); + + 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> ctxCache = srCache.get(_callerContext); + if(ctxCache == null) { + if (LOGGER.isLoggable(Level.FINE)) LOGGER.log(Level.FINE, "Creating cache for " + _callerContext); + ctxCache = new ConcurrentHashMap>(); + 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()); + } + CopyOnWriteArrayList 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 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 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> classesCached = srCache.remove(bCtx); + if (classesCached != null) { + for (String clazz : classesCached.keySet()) { + CopyOnWriteArrayList 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 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() { + 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); + } + } }