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);
+ }
+ }
}