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