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