You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by pd...@apache.org on 2017/04/26 23:55:58 UTC
svn commit: r1792829 - in
/felix/trunk/dependencymanager/org.apache.felix.dependencymanager:
src/org/apache/felix/dm/impl/index/multiproperty/ test/test/
Author: pderop
Date: Wed Apr 26 23:55:58 2017
New Revision: 1792829
URL: http://svn.apache.org/viewvc?rev=1792829&view=rev
Log:
FELIX-5619: MultiProperyFilterIndex memory consumption
Added:
felix/trunk/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/index/multiproperty/MultiPropertyKey.java
felix/trunk/dependencymanager/org.apache.felix.dependencymanager/test/test/MultiPropertyFilterIndexPerformanceTest.java
felix/trunk/dependencymanager/org.apache.felix.dependencymanager/test/test/MultiPropertyFilterIndexReferencedTest.java
felix/trunk/dependencymanager/org.apache.felix.dependencymanager/test/test/MultiPropertyKeyTest.java
felix/trunk/dependencymanager/org.apache.felix.dependencymanager/test/test/TestReference.java
Modified:
felix/trunk/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/index/multiproperty/Filter.java
felix/trunk/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/index/multiproperty/MultiPropertyFilterIndex.java
Modified: felix/trunk/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/index/multiproperty/Filter.java
URL: http://svn.apache.org/viewvc/felix/trunk/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/index/multiproperty/Filter.java?rev=1792829&r1=1792828&r2=1792829&view=diff
==============================================================================
--- felix/trunk/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/index/multiproperty/Filter.java (original)
+++ felix/trunk/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/index/multiproperty/Filter.java Wed Apr 26 23:55:58 2017
@@ -111,15 +111,9 @@ public class Filter {
return true;
}
- public static void main(String args[]) {
- Filter parser = Filter.parse("(&(objectClass=OBJECTCLASS)(&(a=x)(a=n)(a=y)(b=y)(c=z)))");
- System.out.println("key: " + parser.createKey());
- }
-
- protected String createKey() {
- StringBuilder builder = new StringBuilder();
+ protected MultiPropertyKey createKey(int filterSize) {
+ MultiPropertyKey multiPropertyKey = new MultiPropertyKey(0);
Iterator<String> keys = m_propertyKeys.iterator();
-
while (keys.hasNext()) {
String key = keys.next();
Property prop = m_properties.get(key);
@@ -127,21 +121,17 @@ public class Filter {
Iterator<String> values = prop.getValues().iterator();
while (values.hasNext()) {
String value = values.next();
- builder.append(key);
- builder.append("=");
- builder.append(value);
- if (keys.hasNext() || values.hasNext()) {
- builder.append(";");
- }
+ multiPropertyKey.append(toKey(key, value));
}
}
}
- // strip the final ';' in case the last key was a wildcard property
- if (builder.charAt(builder.length() - 1) == ';') {
- return builder.toString().substring(0, builder.length() - 1);
- } else {
- return builder.toString();
- }
+ return multiPropertyKey;
}
+ protected MultiPropertyKey toKey(String key, Object value) {
+ MultiPropertyKey multiPropertyKey = new MultiPropertyKey(1);
+ multiPropertyKey.add(key, value.toString());
+ return multiPropertyKey;
+ }
+
}
Modified: felix/trunk/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/index/multiproperty/MultiPropertyFilterIndex.java
URL: http://svn.apache.org/viewvc/felix/trunk/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/index/multiproperty/MultiPropertyFilterIndex.java?rev=1792829&r1=1792828&r2=1792829&view=diff
==============================================================================
--- felix/trunk/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/index/multiproperty/MultiPropertyFilterIndex.java (original)
+++ felix/trunk/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/index/multiproperty/MultiPropertyFilterIndex.java Wed Apr 26 23:55:58 2017
@@ -1,20 +1,12 @@
/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for
+ * additional information regarding copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/
package org.apache.felix.dm.impl.index.multiproperty;
@@ -46,22 +38,29 @@ import org.osgi.framework.ServiceReferen
@SuppressWarnings("rawtypes")
public class MultiPropertyFilterIndex implements FilterIndex, ServiceTrackerCustomizer {
- private final Object m_lock = new Object();
- private ServiceTracker m_tracker;
- private BundleContext m_context;
+ private final Object m_lock = new Object();
+
+ private ServiceTracker m_tracker;
+
+ private BundleContext m_context;
+
private Map<String, Property> m_configProperties = new LinkedHashMap<>();
+
private List<String> m_negatePropertyKeys = new ArrayList<>();
- 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<>();
+
+ private final Map<MultiPropertyKey, List<ServiceReference>> m_keyToServiceReferencesMap = new HashMap<>();
+
+ private final Map<MultiPropertyKey, List<ServiceListener>> m_keyToListenersMap = new HashMap<>();
+
+ private final Map<ServiceListener, String> m_listenerToFilterMap = new HashMap<>();
public MultiPropertyFilterIndex(String configString) {
parseConfig(configString);
}
-
+
public boolean isApplicable(String clazz, String filterString) {
Filter filter = createFilter(clazz, filterString);
-
+
if (!filter.isValid()) {
return false;
}
@@ -70,6 +69,7 @@ public class MultiPropertyFilterIndex im
if (m_configProperties.size() != filterPropertyKeys.size()) {
return false;
}
+
Iterator<String> filterPropertyKeysIterator = filterPropertyKeys.iterator();
while (filterPropertyKeysIterator.hasNext()) {
String filterPropertyKey = filterPropertyKeysIterator.next();
@@ -81,31 +81,31 @@ public class MultiPropertyFilterIndex im
} else if (!filter.getProperty(filterPropertyKey).isNegate() && filter.getProperty(filterPropertyKey).getValue().equals("*")) {
// no wildcards without negation allowed
return false;
- }
+ }
}
// our properties match so we're applicable
return true;
}
-
- public boolean isApplicable(ServiceReference ref) {
- String[] propertyKeys = ref.getPropertyKeys();
- TreeSet<String> referenceProperties = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
- for (int i = 0; i < propertyKeys.length; i++) {
- referenceProperties.add(propertyKeys[i]);
- }
- Iterator<String> iterator = m_configProperties.keySet().iterator();
- while (iterator.hasNext()) {
- String item = iterator.next();
- Property configProperty = m_configProperties.get(item);
- if (!configProperty.isNegate() && !(referenceProperties.contains(item))) {
- return false;
- } else if (configProperty.isNegate() && referenceProperties.contains(item)) {
- return false;
- }
- }
- return true;
- }
-
+
+ public boolean isApplicable(ServiceReference ref) {
+ String[] propertyKeys = ref.getPropertyKeys();
+ TreeSet<String> referenceProperties = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
+ for (int i = 0; i < propertyKeys.length; i++) {
+ referenceProperties.add(propertyKeys[i]);
+ }
+ Iterator<String> iterator = m_configProperties.keySet().iterator();
+ while (iterator.hasNext()) {
+ String item = iterator.next();
+ Property configProperty = m_configProperties.get(item);
+ if (!configProperty.isNegate() && !(referenceProperties.contains(item))) {
+ return false;
+ } else if (configProperty.isNegate() && referenceProperties.contains(item)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
private void parseConfig(String configString) {
String[] propertyConfigs = configString.split(",");
for (int i = 0; i < propertyConfigs.length; i++) {
@@ -134,18 +134,20 @@ public class MultiPropertyFilterIndex im
}
}
}
-
+
protected Collection<Property> getProperties() {
return m_configProperties.values();
}
-
- protected String createKeyFromFilter(String clazz, String filterString) {
- return createFilter(clazz, filterString).createKey();
- }
-
- private Filter createFilter(String clazz, String filterString) {
+
+ protected MultiPropertyKey createKeyFromFilter(String clazz, String filterString) {
+ int filterSize = m_configProperties.size();
+ return createFilter(clazz, filterString).createKey(filterSize);
+ }
+
+ //KEYS OF A FILTER
+ private Filter createFilter(String clazz, String filterString) {
String filterStringWithObjectClass = filterString;
- if (clazz != null) {
+ if (clazz != null && !clazz.isEmpty()) {
if (filterString != null) {
if (!filterStringWithObjectClass.startsWith("(&(objectClass=")) {
filterStringWithObjectClass = "(&(objectClass=" + clazz + ")" + filterString + ")";
@@ -156,84 +158,99 @@ public class MultiPropertyFilterIndex im
}
Filter filter = Filter.parse(filterStringWithObjectClass);
return filter;
- }
-
- protected List<String> createKeys(ServiceReference reference) {
- List<String> results = new ArrayList<>();
- List<List<String>> sets = new ArrayList<>();
+ }
+
+ public List<MultiPropertyKey> createKeys(ServiceReference reference) {
+ List<MultiPropertyKey> results = new ArrayList<>();
+
String[] keys = reference.getPropertyKeys();
Arrays.sort(keys, String.CASE_INSENSITIVE_ORDER);
+
+ MultiPropertyKey multiPropertyKey = new MultiPropertyKey(0);
+ List<MultiPropertyKey> permutations = new ArrayList<>();
+
+ List<List<MultiPropertyKey>> sets = new ArrayList<>();
+
for (int i = 0; i < keys.length; i++) {
- List<String> set = new ArrayList<>();
- String key = keys[i].toLowerCase();
- if (m_configProperties.containsKey(key)) {
+ String key = null;
+ boolean hasUpperCase = !keys[i].equals(keys[i].toLowerCase());
+ if(hasUpperCase) {
+ key = keys[i].toLowerCase();
+ } else {
+ key = keys[i];
+ }
+
+ if (m_configProperties.containsKey(key)) {
Object valueObject = reference.getProperty(key);
if (valueObject instanceof String[]) {
String[] values = (String[]) valueObject;
if (m_configProperties.get(key).isPermute()) {
- set.addAll(getPermutations(key, values));
+ sets.add(getPermutations(key, values));
} else {
- set.addAll(getSingleValues(key, values));
+ List<MultiPropertyKey> singleValues = new ArrayList<>();
+ for (int v = 0; v < values.length; v++) {
+ MultiPropertyKey single = new MultiPropertyKey(0);
+ single.append(toKey(key, values[v]));
+ singleValues.add(single);
+ }
+ sets.add(singleValues);
}
} else {
- set.add(toKey(key, valueObject));
+ multiPropertyKey.append(toKey(key, (String) valueObject.toString()));
}
- sets.add(set);
- }
+ }
}
- List<List<String>> reversedSets = new ArrayList<>();
- int size = sets.size();
- for (int i = size - 1; i > -1; i--) {
- reversedSets.add(sets.get(i));
- }
- List<List<String>> products = carthesianProduct(0, reversedSets);
- // convert sets into strings
- for (int i = 0; i < products.size(); i++) {
- List<String> set = products.get(i);
- StringBuilder b = new StringBuilder();
- for (int j = 0; j < set.size(); j++) {
- String item = set.get(j);
- b.append(item);
- if (j < set.size() - 1) {
- b.append(";");
+ if (permutations != null && !permutations.isEmpty()) {
+ for (MultiPropertyKey permutation : permutations) {
+ permutation.append(multiPropertyKey);
+ results.add(permutation);
+ }
+ } else {
+ if (!sets.isEmpty()) {
+
+ List<List<MultiPropertyKey>> carthesianProductMultiProperty = carthesianProductMultiProperty(0, sets);
+ for (List<MultiPropertyKey> keyList : carthesianProductMultiProperty) {
+ MultiPropertyKey merged = new MultiPropertyKey(0);
+ merged.append(multiPropertyKey);
+ for (MultiPropertyKey single : keyList) {
+
+ merged.append(single);
+
+ }
+ results.add(merged);
}
+
+ } else {
+
+ results.add(multiPropertyKey);
}
- results.add(b.toString());
}
-
return results;
}
-
- /**
- * Note that we calculate the carthesian product for multi value properties. Use filters on these sparingly since memory
- * consumption can get really high when multiple properties have a lot of values.
- *
- * @param index
- * @param sets
- * @return
- */
- private List<List<String>> carthesianProduct(int index, List<List<String>> sets) {
- List<List<String>> result = new ArrayList<>();
- if (index == sets.size()) {
- result.add(new ArrayList<String>());
- } else {
- List<String> set = sets.get(index);
+
+ private List<List<MultiPropertyKey>> carthesianProductMultiProperty(int index, List<List<MultiPropertyKey>> sets) {
+ List<List<MultiPropertyKey>> result = new ArrayList<>();
+ if (index == sets.size()) {
+ result.add(new ArrayList<MultiPropertyKey>());
+ } else {
+ List<MultiPropertyKey> set = sets.get(index);
for (int i = 0; i < set.size(); i++) {
- String object = set.get(i);
- List<List<String>> pSets = carthesianProduct(index + 1, sets);
- for (int j = 0; j < pSets.size(); j++) {
- List<String> pSet = pSets.get(j);
- pSet.add(object);
- result.add(pSet);
- }
- }
- }
- return result;
- }
-
- List<String> getPermutations(String key, String[] values) {
- List<String> results = new ArrayList<>();
+ MultiPropertyKey object = set.get(i);
+ List<List<MultiPropertyKey>> pSets = carthesianProductMultiProperty(index + 1, sets);
+ for (int j = 0; j < pSets.size(); j++) {
+ List<MultiPropertyKey> pSet = pSets.get(j);
+ pSet.add(object);
+ result.add(pSet);
+ }
+ }
+ }
+ return result;
+ }
+
+
+ List<MultiPropertyKey> getPermutations(String key, String[] values) {
+ List<MultiPropertyKey> results = new ArrayList<>();
Arrays.sort(values, String.CASE_INSENSITIVE_ORDER);
for (int v = 0; v < values.length; v++) {
String processValue = values[v];
@@ -252,259 +269,249 @@ public class MultiPropertyFilterIndex im
}
}
return results;
- }
-
- private Collection<? extends String> getSingleValues(String key, String[] values) {
- List<String> results = new ArrayList<>();
- Arrays.sort(values, String.CASE_INSENSITIVE_ORDER);
- for (int v = 0; v < values.length; v++) {
- results.add(toKey(key, values[v]));
+ }
+
+
+ protected MultiPropertyKey toKey(String key, List<String> values) {
+ MultiPropertyKey kvc = new MultiPropertyKey(values.size());
+
+ for (int i = 0; i < values.size(); i++) {
+ kvc.add(key, values.get(i));
}
- return results;
+ return kvc;
}
-
- protected String toKey(String key, List<String> values) {
- StringBuilder builder = new StringBuilder();
- for (int i = 0; i < values.size(); i++) {
- builder.append(toKey(key, values.get(i)));
- if (i < values.size() - 1) {
- builder.append(";");
- }
- }
- return builder.toString();
- }
-
- protected String toKey(String key, Object value) {
- StringBuilder builder = new StringBuilder();
- builder.append(key);
- builder.append("=");
- builder.append(value.toString());
- return builder.toString();
- }
-
- @SuppressWarnings("unchecked")
+
+ protected MultiPropertyKey toKey(String key, Object value) {
+ MultiPropertyKey kvc = new MultiPropertyKey(1);
+ kvc.add(key, value.toString());
+ return kvc;
+ }
+
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) && shouldBeIndexed(reference)) {
- handleServiceAdd(reference);
- }
- }
-
- public void modifiedService(ServiceReference reference, Object service) {
- if (isApplicable(reference)) {
- handleServicePropertiesChange(reference);
- }
- }
-
- public void removedService(ServiceReference reference, Object service) {
- if (isApplicable(reference) && shouldBeIndexed(reference)) {
- handleServiceRemove(reference);
- }
- }
-
- protected void handleServiceAdd(ServiceReference reference) {
- List<String> keys = createKeys(reference);
- synchronized (m_keyToServiceReferencesMap) {
- for (int i = 0; i < keys.size(); i++) {
- List<ServiceReference> references = m_keyToServiceReferencesMap.get(keys.get(i));
- if (references == null) {
- references = new ArrayList<>();
- m_keyToServiceReferencesMap.put(keys.get(i), references);
- }
- references.add(reference);
- }
- }
- }
-
- protected void handleServicePropertiesChange(ServiceReference reference) {
-
- synchronized (m_keyToServiceReferencesMap) {
- // TODO this is a quite expensive linear scan over the existing collection
- // because we first need to remove any existing references and they can be
- // all over the place :)
- Iterator<List<ServiceReference>> iterator = m_keyToServiceReferencesMap.values().iterator();
- while (iterator.hasNext()) {
- List<ServiceReference> list = iterator.next();
- if (list != null) {
- Iterator<ServiceReference> i2 = list.iterator();
- while (i2.hasNext()) {
- ServiceReference ref = i2.next();
- if (ref.equals(reference)) {
- i2.remove();
- }
- }
- }
- }
- // only re-add the reference when it is still applicable for this filter index
- if (shouldBeIndexed(reference)) {
- List<String> keys = createKeys(reference);
- for (int i = 0; i < keys.size(); i++) {
- List<ServiceReference> references = m_keyToServiceReferencesMap.get(keys.get(i));
- if (references == null) {
- references = new ArrayList<>();
- m_keyToServiceReferencesMap.put(keys.get(i), references);
- }
- references.add(reference);
- }
- }
- }
- }
-
- protected void handleServiceRemove(ServiceReference reference) {
- List<String> keys = createKeys(reference);
- synchronized (m_keyToServiceReferencesMap) {
- for (int i = 0; i < keys.size(); i++) {
- List<ServiceReference> references = m_keyToServiceReferencesMap.get(keys.get(i));
- if (references != null) {
- references.remove(reference);
- if (references.isEmpty()) {
- m_keyToServiceReferencesMap.remove(keys.get(i));
- }
- }
- }
- }
- }
-
- protected boolean shouldBeIndexed(ServiceReference reference) {
- // is already applicable, so we should only check whether there's a negate field in the filter which has a value in the reference
- Iterator<String> negatePropertyKeyIterator = m_negatePropertyKeys.iterator();
- while (negatePropertyKeyIterator.hasNext()) {
- String negatePropertyKey = negatePropertyKeyIterator.next();
- if (reference.getProperty(negatePropertyKey) != null) {
- return false;
- }
- }
- return true;
- }
-
- 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, true);
- }
+ 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) && shouldBeIndexed(reference)) {
+ handleServiceAdd(reference);
+ }
+ }
+
+ public void modifiedService(ServiceReference reference, Object service) {
+ if (isApplicable(reference)) {
+ handleServicePropertiesChange(reference);
+ }
+ }
+
+ public void removedService(ServiceReference reference, Object service) {
+ if (isApplicable(reference) && shouldBeIndexed(reference)) {
+ handleServiceRemove(reference);
+ }
+ }
+
+ protected void handleServiceAdd(ServiceReference reference) {
+ List<MultiPropertyKey> keys = createKeys(reference);
+
+ synchronized (m_keyToServiceReferencesMap) {
+ for (int i = 0; i < keys.size(); i++) {
+ List<ServiceReference> references = m_keyToServiceReferencesMap.get(keys.get(i));
+ if (references == null) {
+ references = new ArrayList<>(1);
+ m_keyToServiceReferencesMap.put(keys.get(i), references);
+ }
+ references.add(reference);
+ }
+
+ }
+ }
+
+ protected void handleServicePropertiesChange(ServiceReference reference) {
+
+ synchronized (m_keyToServiceReferencesMap) {
+ // TODO this is a quite expensive linear scan over the existing collection
+ // because we first need to remove any existing references and they can be
+ // all over the place :)
+ Iterator<List<ServiceReference>> iterator = m_keyToServiceReferencesMap.values().iterator();
+ while (iterator.hasNext()) {
+ List<ServiceReference> list = iterator.next();
+ if (list != null) {
+ Iterator<ServiceReference> i2 = list.iterator();
+ while (i2.hasNext()) {
+ ServiceReference ref = i2.next();
+ if (ref.equals(reference)) {
+ i2.remove();
+ }
+ }
+ }
+ }
+ // only re-add the reference when it is still applicable for this filter index
+ if (shouldBeIndexed(reference)) {
+ List<MultiPropertyKey> keys = createKeys(reference);
+ for (int i = 0; i < keys.size(); i++) {
+ List<ServiceReference> references = m_keyToServiceReferencesMap.get(keys.get(i));
+ if (references == null) {
+ references = new ArrayList<>(keys.size());
+ m_keyToServiceReferencesMap.put(keys.get(i), references);
+ }
+ references.add(reference);
+ }
+ }
+ }
+ }
+
+ protected void handleServiceRemove(ServiceReference reference) {
+ List<MultiPropertyKey> keys = createKeys(reference);
+ synchronized (m_keyToServiceReferencesMap) {
+ for (int i = 0; i < keys.size(); i++) {
+ List<ServiceReference> references = m_keyToServiceReferencesMap.get(keys.get(i));
+ if (references != null) {
+ references.remove(reference);
+ if (references.isEmpty()) {
+ m_keyToServiceReferencesMap.remove(keys.get(i));
+ } else {
+ ((ArrayList) reference).trimToSize();
+ }
+ }
+ }
+ }
+ }
+
+ protected boolean shouldBeIndexed(ServiceReference reference) {
+ // is already applicable, so we should only check whether there's a negate field in the filter which has a value in the reference
+ Iterator<String> negatePropertyKeyIterator = m_negatePropertyKeys.iterator();
+ while (negatePropertyKeyIterator.hasNext()) {
+ String negatePropertyKey = negatePropertyKeyIterator.next();
+ if (reference.getProperty(negatePropertyKey) != null) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ 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, 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 List<ServiceReference> getAllServiceReferences(String clazz, String filter) {
- List<ServiceReference> result = new ArrayList<>();
- String key = createKeyFromFilter(clazz, filter);
- synchronized (m_keyToServiceReferencesMap) {
- List<ServiceReference> references = m_keyToServiceReferencesMap.get(key);
- if (references != null) {
- result.addAll(references);
- }
- }
- return result;
- }
-
- public void serviceChanged(ServiceEvent event) {
- if (isApplicable(event.getServiceReference())) {
- List<String> keys = createKeys(event.getServiceReference());
- List<ServiceListener> list = new ArrayList<ServiceListener>();
- synchronized (m_keyToListenersMap) {
- for (int i = 0; i < keys.size(); i++) {
- String key = keys.get(i);
- List<ServiceListener> listeners = m_keyToListenersMap.get(key);
- if (listeners != null) {
- list.addAll(listeners);
- }
- }
- }
- if (list != null) {
- Iterator<ServiceListener> iterator = list.iterator();
- while (iterator.hasNext()) {
- ServiceListener listener = iterator.next();
- listener.serviceChanged(event);
- }
- }
- }
- }
-
- public void addServiceListener(ServiceListener listener, String filter) {
- String key = createKeyFromFilter(null, filter);
- synchronized (m_keyToListenersMap) {
- List<ServiceListener> listeners = m_keyToListenersMap.get(key);
- if (listeners == null) {
- listeners = new CopyOnWriteArrayList<ServiceListener>();
- m_keyToListenersMap.put(key, listeners);
- }
- listeners.add(listener);
- m_listenerToFilterMap.put(listener, filter);
- }
- }
-
- public void removeServiceListener(ServiceListener listener) {
- synchronized (m_keyToListenersMap) {
- String filter = m_listenerToFilterMap.remove(listener);
- if (filter != null) {
- // the listener does exist
- String key = createKeyFromFilter(null, filter);
-
- boolean result = filter != null;
- if (result) {
- List<ServiceListener> listeners = m_keyToListenersMap.get(key);
- if (listeners != null) {
- listeners.remove(listener);
- if (listeners.isEmpty()) {
- m_keyToListenersMap.remove(key);
- }
- }
- // TODO actually, if listeners == null that would be strange....
- }
- }
- }
- }
-
- protected Collection<ServiceListener> getServiceListeners() {
- return m_listenerToFilterMap.keySet();
- }
-
- public String toString() {
- StringBuffer sb = new StringBuffer();
- sb.append(" dMultiPropertyExactFilter[");
- sb.append("K2L: " + m_keyToListenersMap.size());
- sb.append(", K2SR: " + m_keyToServiceReferencesMap.size());
- sb.append(", L2F: " + m_listenerToFilterMap.size());
- sb.append("]");
- return sb.toString();
- }
+ 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 List<ServiceReference> getAllServiceReferences(String clazz, String filter) {
+ List<ServiceReference> result = new ArrayList<>();
+ MultiPropertyKey key = createKeyFromFilter(clazz, filter);
+ synchronized (m_keyToServiceReferencesMap) {
+ List<ServiceReference> references = m_keyToServiceReferencesMap.get(key);
+ if (references != null) {
+ result.addAll(references);
+ }
+ }
+ return result;
+ }
+
+ public void serviceChanged(ServiceEvent event) {
+ if (isApplicable(event.getServiceReference())) {
+ List<MultiPropertyKey> keys = createKeys(event.getServiceReference());
+ List<ServiceListener> list = new ArrayList<ServiceListener>();
+ synchronized (m_keyToListenersMap) {
+ for (int i = 0; i < keys.size(); i++) {
+ //TODO fix
+ MultiPropertyKey key = keys.get(i);
+ List<ServiceListener> listeners = m_keyToListenersMap.get(key);
+ if (listeners != null) {
+ list.addAll(listeners);
+ }
+ }
+ }
+ if (list != null) {
+ Iterator<ServiceListener> iterator = list.iterator();
+ while (iterator.hasNext()) {
+ ServiceListener listener = iterator.next();
+ listener.serviceChanged(event);
+ }
+ }
+ }
+ }
+
+ public void addServiceListener(ServiceListener listener, String filter) {
+ MultiPropertyKey key = createKeyFromFilter(null, filter);
+
+ synchronized (m_keyToListenersMap) {
+ List<ServiceListener> listeners = m_keyToListenersMap.get(key);
+ if (listeners == null) {
+ listeners = new CopyOnWriteArrayList<ServiceListener>();
+ m_keyToListenersMap.put(key, listeners);
+ }
+ listeners.add(listener);
+ m_listenerToFilterMap.put(listener, filter);
+ }
+ }
+
+ public void removeServiceListener(ServiceListener listener) {
+ synchronized (m_keyToListenersMap) {
+ String filter = m_listenerToFilterMap.remove(listener);
+ if (filter != null) {
+ // the listener does exist
+ MultiPropertyKey key = createKeyFromFilter(null, filter);
+
+ boolean result = filter != null;
+ if (result) {
+ List<ServiceListener> listeners = m_keyToListenersMap.get(key);
+ if (listeners != null) {
+ listeners.remove(listener);
+ if (listeners.isEmpty()) {
+ m_keyToListenersMap.remove(key);
+ }
+ }
+ // TODO actually, if listeners == null that would be strange....
+ }
+ }
+ }
+ }
+
+ protected Collection<ServiceListener> getServiceListeners() {
+ return m_listenerToFilterMap.keySet();
+ }
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ sb.append(" dMultiPropertyExactFilter[");
+ sb.append("K2L: " + m_keyToListenersMap.size());
+ sb.append(", K2SR: " + m_keyToServiceReferencesMap.size());
+ sb.append(", L2F: " + m_listenerToFilterMap.size());
+ sb.append("]");
+ return sb.toString();
+ }
@Override
- public void swappedService(ServiceReference reference, Object service,
- ServiceReference newReference, Object newService) {
+ public void swappedService(ServiceReference reference, Object service, ServiceReference newReference, Object newService) {
addedService(newReference, newService);
removedService(reference, service);
}
Added: felix/trunk/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/index/multiproperty/MultiPropertyKey.java
URL: http://svn.apache.org/viewvc/felix/trunk/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/index/multiproperty/MultiPropertyKey.java?rev=1792829&view=auto
==============================================================================
--- felix/trunk/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/index/multiproperty/MultiPropertyKey.java (added)
+++ felix/trunk/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/index/multiproperty/MultiPropertyKey.java Wed Apr 26 23:55:58 2017
@@ -0,0 +1,123 @@
+package org.apache.felix.dm.impl.index.multiproperty;
+
+import java.util.Arrays;
+
+public class MultiPropertyKey {
+
+ private MultiPropertyKeyPart[] properties;
+
+ public MultiPropertyKey(int filterSize) {
+ properties = new MultiPropertyKeyPart[0];
+ }
+
+ // Initial adding key values.
+ public void add(String key, String value) {
+ int newLength = properties.length + 1;
+ MultiPropertyKeyPart[] tmpKeys = new MultiPropertyKeyPart[newLength];
+
+ System.arraycopy(properties, 0, tmpKeys, 0, properties.length);
+ tmpKeys[newLength-1] = new MultiPropertyKeyPart(key, value);
+
+ this.properties = tmpKeys;
+ Arrays.sort(properties);
+ }
+
+ public void append(MultiPropertyKey other) {
+ int newLength = properties.length + other.properties.length;
+ MultiPropertyKeyPart[] tmpKeys = new MultiPropertyKeyPart[newLength];
+
+ System.arraycopy(properties, 0, tmpKeys, 0, properties.length);
+ System.arraycopy(other.properties, 0, tmpKeys, properties.length, other.properties.length);
+
+ this.properties = tmpKeys;
+ Arrays.sort(properties);
+ }
+
+ @Override
+ public int hashCode() {
+ return Arrays.hashCode(properties);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+
+ if (!(obj instanceof MultiPropertyKey)) {
+ return false;
+ }
+
+ MultiPropertyKey other = (MultiPropertyKey) obj;
+
+ if (properties.length != other.properties.length) {
+ return false;
+ }
+
+ for (int i = 0; i < properties.length; i++) {
+ if (!(properties[i].equals(other.properties[i]))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ for (int i = 0; i < properties.length; i++) {
+ builder.append(properties[i].key);
+ builder.append('=');
+ builder.append(properties[i].value);
+ if (i < properties.length - 1) {
+ builder.append(';');
+ }
+ }
+ return builder.toString();
+ }
+
+ // (key=value) part of a multi property index (&(key1=val1)(key2=val2) .. )
+ private class MultiPropertyKeyPart implements Comparable<MultiPropertyKeyPart> {
+
+ private final String key;
+ private final String value;
+
+ public MultiPropertyKeyPart(String key, String value) {
+ this.key = key;
+ this.value = value;
+ }
+
+ @Override
+ public int hashCode() {
+ int h = 0;
+ h += 31 * key.hashCode();
+ h += value.hashCode();
+ return h;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (!(obj instanceof MultiPropertyKeyPart))
+ return false;
+
+ MultiPropertyKeyPart object = (MultiPropertyKeyPart) obj;
+
+ if (!(this.key.equals(object.key)))
+ return false;
+ if (!(this.value.equals(object.value)))
+ return false;
+
+ return true;
+ }
+
+ @Override
+ public int compareTo(MultiPropertyKeyPart o) {
+ if (this.key.compareTo(o.key) == 0) {
+ return this.value.compareTo(o.value);
+ }
+ return this.key.compareTo(o.key);
+ }
+ }
+}
\ No newline at end of file
Added: felix/trunk/dependencymanager/org.apache.felix.dependencymanager/test/test/MultiPropertyFilterIndexPerformanceTest.java
URL: http://svn.apache.org/viewvc/felix/trunk/dependencymanager/org.apache.felix.dependencymanager/test/test/MultiPropertyFilterIndexPerformanceTest.java?rev=1792829&view=auto
==============================================================================
--- felix/trunk/dependencymanager/org.apache.felix.dependencymanager/test/test/MultiPropertyFilterIndexPerformanceTest.java (added)
+++ felix/trunk/dependencymanager/org.apache.felix.dependencymanager/test/test/MultiPropertyFilterIndexPerformanceTest.java Wed Apr 26 23:55:58 2017
@@ -0,0 +1,83 @@
+package test;
+
+import static org.junit.Assert.assertFalse;
+
+import java.util.List;
+
+import org.apache.felix.dm.impl.index.multiproperty.MultiPropertyFilterIndex;
+import org.junit.Test;
+import org.osgi.framework.ServiceReference;
+
+public class MultiPropertyFilterIndexPerformanceTest {
+
+ final int testSize = 500000;
+
+ final int iterations = 10;
+
+ @Test
+ public void MultiPropertyFilterIndexTest() {
+ MultiPropertyFilterIndex stringIndex;
+
+ for (int i = 0; i < iterations; i++) {
+ stringIndex = new MultiPropertyFilterIndex("component-identifier,model,concept,role");
+ testPerformance(stringIndex, testSize, "bystring");
+ }
+ }
+
+
+ private void testPerformance(MultiPropertyFilterIndex filterIndex, int runSize, String indexName) {
+ System.gc();
+ long start = System.currentTimeMillis();
+ long memoryBefore = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
+
+ for (int i = 0; i < runSize; i++) {
+ TestReference newReference = new TestReference();
+ String[] multiValue = { "CREATES", "UPDATES" };
+ String[] mv2 = { "extra1", "extra2", "extra3" };
+
+ newReference.addProperty("component-identifier",
+ "org.acme.xyz.platform.interfaces.modeldrivenservices.DialogService");
+ newReference.addProperty("model", "//Housing benefit request/Housing benefit request.model" + i);
+ newReference.addProperty("concept",
+ "//Housing benefit request/2000 Requests/40000/Housing benefit request.model#concept" + i);
+ newReference.addProperty("role", multiValue);
+ newReference.addProperty("extra", mv2);
+
+ filterIndex.addedService(newReference, new Object());
+ }
+
+ double writeTime = ((System.currentTimeMillis() - start) / 1000.0);
+ long startReading = System.currentTimeMillis();
+
+ for (int i = 0; i < runSize; i++) {
+ List<ServiceReference> allServiceReferences = filterIndex.getAllServiceReferences(null,
+ "(&(component-identifier=org.acme.xyz.platform.interfaces.modeldrivenservices.DialogService)"
+ + "(model=//Housing benefit request/Housing benefit request.model" + i + ")"
+ + "(concept=//Housing benefit request/2000 Requests/40000/Housing benefit request.model#concept"
+ + i + ")" + "(role=CREATES))");
+
+ if (allServiceReferences.size() != 1) {
+ throw new AssertionError("Failed to find reference in cache");
+ }
+
+ }
+
+ // Sanitiy check
+ List<ServiceReference> allServiceReferences = filterIndex.getAllServiceReferences(null,
+ "(&(model=mymodel1)(concept=abracadabrra)(role=CREATES))");
+ if (allServiceReferences.size() != 0) {
+ assertFalse(allServiceReferences.size() != 0);
+ }
+
+ double readTime = ((System.currentTimeMillis() - startReading) / 1000.0);
+
+ System.gc();
+ long memoryAfter = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
+ double consumed = (memoryAfter - memoryBefore) / 1048576.0;
+
+ System.err.println("w: " + writeTime + ", r: " + readTime + ",m: " + consumed + " # of iterations: " + runSize + " -- "
+ + indexName);
+ }
+
+
+}
Added: felix/trunk/dependencymanager/org.apache.felix.dependencymanager/test/test/MultiPropertyFilterIndexReferencedTest.java
URL: http://svn.apache.org/viewvc/felix/trunk/dependencymanager/org.apache.felix.dependencymanager/test/test/MultiPropertyFilterIndexReferencedTest.java?rev=1792829&view=auto
==============================================================================
--- felix/trunk/dependencymanager/org.apache.felix.dependencymanager/test/test/MultiPropertyFilterIndexReferencedTest.java (added)
+++ felix/trunk/dependencymanager/org.apache.felix.dependencymanager/test/test/MultiPropertyFilterIndexReferencedTest.java Wed Apr 26 23:55:58 2017
@@ -0,0 +1,275 @@
+package test;
+
+import static org.junit.Assert.assertTrue;
+
+import java.util.List;
+import java.util.Properties;
+
+import org.apache.felix.dm.impl.index.multiproperty.MultiPropertyFilterIndex;
+import org.junit.Test;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.ServiceReference;
+
+public class MultiPropertyFilterIndexReferencedTest {
+
+
+ @Test
+ public void noContext() {
+ MultiPropertyFilterIndex singleValueFilterIndex = new MultiPropertyFilterIndex("objectClass,!context");
+
+ TestReference ref1 = new TestReference();
+ ref1.addProperty("service.id", 4711);
+ ref1.addProperty("objectclass", "java.lang.String");
+ ref1.addProperty("context", "context");
+
+ TestReference ref2 = new TestReference();
+ ref2.addProperty("service.id", 1000);
+ ref2.addProperty("objectclass", "java.lang.String");
+
+ singleValueFilterIndex.addedService(ref1, new String("Service1"));
+
+ singleValueFilterIndex.addedService(ref2, new String("Service2"));
+
+ List<ServiceReference> allServiceReferencesByClass = singleValueFilterIndex.getAllServiceReferences("java.lang.String", null);
+ List<ServiceReference> allServiceReferencesByFilter = singleValueFilterIndex.getAllServiceReferences(null, "(objectClass=java.lang.String)");
+
+ assertTrue(allServiceReferencesByClass.size() == allServiceReferencesByFilter.size());
+ assertTrue(allServiceReferencesByFilter.size() == 1);
+ }
+
+
+ @Test
+ public void noContextCid() {
+ MultiPropertyFilterIndex multiPropertyIndex_new = new MultiPropertyFilterIndex("objectClass,cid,!context");
+
+ TestReference ref1 = new TestReference();
+ ref1.addProperty("service.id", 4711);
+ ref1.addProperty("objectclass", "java.lang.String");
+ ref1.addProperty("context", "context");
+
+ TestReference ref2 = new TestReference();
+ ref2.addProperty("service.id", 1000);
+ ref2.addProperty("cid", "cid2");
+ ref2.addProperty("objectclass", "java.lang.String");
+
+ TestReference ref3 = new TestReference();
+ ref3.addProperty("cid", "cid2");
+ ref3.addProperty("rank", "1");
+ ref3.addProperty("service.id", 1000);
+ ref3.addProperty("objectclass", "java.lang.String");
+
+ multiPropertyIndex_new.addedService(ref1, new String("Service1"));
+
+ multiPropertyIndex_new.addedService(ref2, new String("Service2"));
+
+ multiPropertyIndex_new.addedService(ref3, new String("Service3"));
+
+ List<ServiceReference> result_new = multiPropertyIndex_new.getAllServiceReferences("java.lang.String", "(cid=cid2)");
+
+ assertTrue(result_new.size() == 2);
+ }
+
+
+
+
+ @Test
+ public void singleKeyfilterIndex() {
+ MultiPropertyFilterIndex multiPropertyIndex = new MultiPropertyFilterIndex("objectClass");
+ TestReference ref1 = new TestReference();
+ ref1.addProperty("service.id", 4711);
+ ref1.addProperty("objectclass", "java.lang.String");
+
+ TestReference ref2 = new TestReference();
+ ref2.addProperty("service.id", 4711);
+ ref2.addProperty("objectclass", "java.lang.Object");
+
+ multiPropertyIndex.addedService(ref1, new String("Service1"));
+ multiPropertyIndex.addedService(ref2, new Object());
+
+ //find by classname;
+ assertTrue(multiPropertyIndex.isApplicable("java.lang.String", ""));
+ List<ServiceReference> byClazzName = multiPropertyIndex.getAllServiceReferences("java.lang.String", "");
+ assertTrue(byClazzName.size() == 1);
+ assertTrue(byClazzName.get(0).equals(ref1));
+
+ //find by filter
+ assertTrue(multiPropertyIndex.isApplicable(null, "(objectClass=java.lang.String)"));
+ byClazzName = multiPropertyIndex.getAllServiceReferences(null, "(objectClass=java.lang.String)");
+ assertTrue(byClazzName.size() == 1);
+ assertTrue(byClazzName.get(0).equals(ref1));
+
+ //Add extra service
+ TestReference ref3 = new TestReference();
+ ref3.addProperty("service.id", 4712);
+ ref3.addProperty("objectclass", "java.lang.String");
+ multiPropertyIndex.addedService(ref3, new String("Service3"));
+
+ byClazzName = multiPropertyIndex.getAllServiceReferences("java.lang.String", null);
+ assertTrue(byClazzName.size() == 2);
+ assertTrue(byClazzName.get(0).equals(ref1));
+ assertTrue(byClazzName.get(1).equals(ref3));
+ }
+
+ @Test
+ public void propertyIndexWithDoubleNoPermutationKeys() {
+ String filterConfig = "objectClass,#related-concept-absoluteidentifier,#context-concept-absoluteidentifier,StoreClass";
+
+ MultiPropertyFilterIndex multiPropertyIndex_new = new MultiPropertyFilterIndex(filterConfig);
+
+ String[] relatedConcepts = {"rel-a", "rel-b"};
+ String[] contextConcepts = {"cca-a", "cca-b"};
+
+ TestReference ref1 = new TestReference();
+ ref1.addProperty("service.id", 4711);
+ ref1.addProperty("objectclass", "java.lang.String");
+ ref1.addProperty("related-concept-absoluteidentifier", relatedConcepts);
+ ref1.addProperty("context-concept-absoluteidentifier", contextConcepts);
+ ref1.addProperty("StoreClass", "NoteStore");
+
+ multiPropertyIndex_new.addedService(ref1, new String("Service1"));
+
+ List<ServiceReference> result_new = multiPropertyIndex_new.getAllServiceReferences("java.lang.String", "(&(context-concept-absoluteidentifier=cca-a)(related-concept-absoluteidentifier=rel-b)(storeclass=NoteStore))");
+
+ assertTrue(result_new.size() == 1);
+ }
+
+ @Test
+ public void propertyIndexWithDoubleMultiProperty() {
+ String filterConfig = "objectClass,related-concept-absoluteidentifier,context-concept-absoluteidentifier,StoreClass";
+ MultiPropertyFilterIndex multiPropertyIndex_new = new MultiPropertyFilterIndex(filterConfig);
+
+
+ String[] relatedConcepts = {"rel-a", "rel-b"};
+ String[] contextConcepts = {"cca-a", "cca-b"};
+
+
+ TestReference ref1 = new TestReference();
+ ref1.addProperty("service.id", 4711);
+ ref1.addProperty("objectclass", "java.lang.String");
+ ref1.addProperty("related-concept-absoluteidentifier", relatedConcepts);
+ ref1.addProperty("context-concept-absoluteidentifier", contextConcepts);
+ ref1.addProperty("StoreClass", "NoteStore");
+
+ multiPropertyIndex_new.addedService(ref1, new String("Service1"));
+
+ List<ServiceReference> result_new = multiPropertyIndex_new.getAllServiceReferences("java.lang.String", "(&(context-concept-absoluteidentifier=cca-a)(related-concept-absoluteidentifier=rel-b)(storeclass=NoteStore))");
+
+ assertTrue(result_new.size() == 1);
+ }
+
+
+ @Test
+ public void MultiPropertyFilterIndexTypes() {
+
+ long serviceId = 4711;
+ int ranking = 46;
+ boolean isSomeBoolean = true;
+ String[] interfaces = {"A", "B", "C"};
+
+ MultiPropertyFilterIndex multiPropertyIndexSingleFilter = new MultiPropertyFilterIndex("objectClass");
+ MultiPropertyFilterIndex multiPropertyIndexMultiple = new MultiPropertyFilterIndex("objectClass,service.id,ranking,interfaces");
+
+ TestReference newReference = new TestReference();
+ newReference.addProperty("service.id", serviceId);
+ newReference.addProperty("objectclass", "java.lang.Object");
+ newReference.addProperty("ranking", ranking);
+ newReference.addProperty("someboolvalue", isSomeBoolean);
+ newReference.addProperty("interfaces", interfaces);
+
+ multiPropertyIndexMultiple.addedService(newReference, new Object());
+ multiPropertyIndexSingleFilter.addedService(newReference, new Object());
+
+ List<ServiceReference> noFilter = multiPropertyIndexSingleFilter.getAllServiceReferences("java.lang.Object", null);
+ assertTrue(noFilter.size() == 1);
+
+ List<ServiceReference> noClazz = multiPropertyIndexMultiple.getAllServiceReferences(null, "(&(objectClass=java.lang.Object)(&(service.id=4711)(ranking=46)(interfaces=B)))");
+ assertTrue(noClazz.size() == 1);
+
+ List<ServiceReference> combi = multiPropertyIndexMultiple.getAllServiceReferences("java.lang.Object", "(&(ranking=46)(interfaces=C)(service.id=4711))");
+ assertTrue(combi.size() == 1);
+ }
+
+ @Test
+ public void MultiPropertyFilterIndexKeyGen() {
+ String key = "(&(objectClass=org.acme.xyz.framework.internationalization.Translatable)(component-identifier=org.acme.xyz.framework.webui.engine.impl.CompoundProcessContextGroupingPanelFactory))";
+
+ long serviceId = 4711;
+ int ranking = 46;
+
+ MultiPropertyFilterIndex biIndex = new MultiPropertyFilterIndex("objectClass,component-identifier");
+ TestReference newReference = new TestReference();
+ newReference.addProperty("service.id", serviceId);
+ newReference.addProperty("ranking", ranking);
+ newReference.addProperty("component-identifier", "org.acme.xyz.framework.webui.engine.impl.CompoundProcessContextGroupingPanelFactory");
+ newReference.addProperty("objectclass", "org.acme.xyz.framework.internationalization.Translatable");
+
+ biIndex.addedService(newReference, new Object());
+
+
+ List<ServiceReference> noClazz = biIndex.getAllServiceReferences(null, key);
+
+ assertTrue(noClazz.size() == 1);
+ }
+
+
+ class TestReference implements ServiceReference {
+ Properties props = new Properties();
+
+ TestReference() {
+ }
+
+ public void addProperty(String key, String value) {
+ /* Property keys are case-insensitive. -> see @ org.osgi.framework.ServiceReference */
+ props.put(key.toLowerCase(), value);
+ }
+
+ public void addProperty(String key, long value) {
+ props.put(key, value);
+ }
+
+ public void addProperty(String key, int value) {
+ props.put(key, value);
+ }
+
+ public void addProperty(String key, boolean value) {
+ props.put(key, value);
+ }
+
+ public void addProperty(String key, String[] multiValue) {
+ props.put(key, multiValue);
+ }
+
+
+ @Override
+ public Object getProperty(String key) {
+ return props.get(key);
+ }
+
+ @Override
+ public String[] getPropertyKeys() {
+ return props.keySet().toArray(new String[]{});
+ }
+
+ @Override
+ public Bundle getBundle() {
+ return null;
+ }
+
+ @Override
+ public Bundle[] getUsingBundles() {
+ return null;
+ }
+
+ @Override
+ public boolean isAssignableTo(Bundle bundle, String className) {
+ return false;
+ }
+
+ @Override
+ public int compareTo(Object reference) {
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+ }
+}
Added: felix/trunk/dependencymanager/org.apache.felix.dependencymanager/test/test/MultiPropertyKeyTest.java
URL: http://svn.apache.org/viewvc/felix/trunk/dependencymanager/org.apache.felix.dependencymanager/test/test/MultiPropertyKeyTest.java?rev=1792829&view=auto
==============================================================================
--- felix/trunk/dependencymanager/org.apache.felix.dependencymanager/test/test/MultiPropertyKeyTest.java (added)
+++ felix/trunk/dependencymanager/org.apache.felix.dependencymanager/test/test/MultiPropertyKeyTest.java Wed Apr 26 23:55:58 2017
@@ -0,0 +1,98 @@
+package test;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.List;
+
+import org.apache.felix.dm.impl.index.multiproperty.MultiPropertyFilterIndex;
+import org.apache.felix.dm.impl.index.multiproperty.MultiPropertyKey;
+import org.junit.Test;
+
+public class MultiPropertyKeyTest {
+
+ @Test
+ public void sameKeysAndValues() {
+ MultiPropertyKey actual = new MultiPropertyKey(2);
+ actual.add("key1", "abc");
+ actual.add("key2", "efg");
+
+ MultiPropertyKey other = new MultiPropertyKey(2);
+ other.add("key1", "abc");
+ other.add("key2", "efg");
+
+ assertTrue(actual.equals(other));
+ assertTrue(actual.hashCode() == other.hashCode());
+ }
+
+ @Test
+ public void sameKeysAndValuesDifferentOrder() {
+ MultiPropertyKey actual = new MultiPropertyKey(2);
+ actual.add("key1", "abc");
+ actual.add("key2", "efg");
+
+ MultiPropertyKey other = new MultiPropertyKey(2);
+ other.add("key2", "efg");
+ other.add("key1", "abc");
+
+ assertTrue(actual.equals(other));
+ assertTrue(actual.hashCode() == other.hashCode());
+ }
+
+ @Test
+ public void sameKeysDifferentValues() {
+ MultiPropertyKey actual = new MultiPropertyKey(2);
+ actual.add("key1", "abc");
+ actual.add("key2", "efg");
+
+ MultiPropertyKey other = new MultiPropertyKey(2);
+ other.add("key1", "efg");
+ other.add("key2", "abc");
+
+ assertFalse(actual.equals(other));
+ assertFalse(actual.hashCode() == other.hashCode());
+ }
+
+ @Test
+ public void subSetOfKeyValues() {
+ MultiPropertyKey actual = new MultiPropertyKey(3);
+ actual.add("key1", "abc");
+ actual.add("key2", "efg");
+ actual.add("key3", "hij");
+
+ MultiPropertyKey other = new MultiPropertyKey(2);
+ other.add("key1", "abc");
+ other.add("key2", "efg");
+
+ assertFalse(actual.equals(other));
+ assertFalse(actual.hashCode() == other.hashCode());
+ }
+
+ @Test
+ public void noPermutations() {
+ MultiPropertyFilterIndex singleValueFilterIndex = new MultiPropertyFilterIndex("objectClass,#nopermutation");
+
+ TestReference ref1 = new TestReference();
+ ref1.addProperty("service.id", 4711);
+ ref1.addProperty("objectclass", "java.lang.String");
+ ref1.addProperty("nopermutation", new String[] { "a", "b", "c", "d" });
+
+ List<MultiPropertyKey> keys = singleValueFilterIndex.createKeys(ref1);
+
+ assertTrue(keys.size() == 4);
+ }
+
+ @Test
+ public void permutations() {
+ MultiPropertyFilterIndex singleValueFilterIndex = new MultiPropertyFilterIndex("objectClass,permutation");
+
+ TestReference ref1 = new TestReference();
+ ref1.addProperty("service.id", 4711);
+ ref1.addProperty("objectclass", "java.lang.String");
+ ref1.addProperty("permutation", new String[] { "a", "b", "c", "d" });
+
+ List<MultiPropertyKey> keys = singleValueFilterIndex.createKeys(ref1);
+
+ assertTrue(keys.size() == 10);
+ }
+}
Added: felix/trunk/dependencymanager/org.apache.felix.dependencymanager/test/test/TestReference.java
URL: http://svn.apache.org/viewvc/felix/trunk/dependencymanager/org.apache.felix.dependencymanager/test/test/TestReference.java?rev=1792829&view=auto
==============================================================================
--- felix/trunk/dependencymanager/org.apache.felix.dependencymanager/test/test/TestReference.java (added)
+++ felix/trunk/dependencymanager/org.apache.felix.dependencymanager/test/test/TestReference.java Wed Apr 26 23:55:58 2017
@@ -0,0 +1,69 @@
+package test;
+
+import java.util.Properties;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.ServiceReference;
+
+class TestReference implements ServiceReference {
+ Properties props = new Properties();
+
+ public TestReference() {
+ }
+
+ public void addProperty(String key, String value) {
+ /*
+ * Property keys are case-insensitive. -> see @
+ * org.osgi.framework.ServiceReference
+ */
+ props.put(key.toLowerCase(), value);
+ }
+
+ public void addProperty(String key, long value) {
+ props.put(key, value);
+ }
+
+ public void addProperty(String key, int value) {
+ props.put(key, value);
+ }
+
+ public void addProperty(String key, boolean value) {
+ props.put(key, value);
+ }
+
+ public void addProperty(String key, String[] multiValue) {
+ props.put(key, multiValue);
+ }
+
+ @Override
+ public Object getProperty(String key) {
+ return props.get(key);
+ }
+
+ @Override
+ public String[] getPropertyKeys() {
+ return props.keySet().toArray(new String[] {});
+ }
+
+ @Override
+ public Bundle getBundle() {
+ return null;
+ }
+
+ @Override
+ public Bundle[] getUsingBundles() {
+ return null;
+ }
+
+ @Override
+ public boolean isAssignableTo(Bundle bundle, String className) {
+ return false;
+ }
+
+ @Override
+ public int compareTo(Object reference) {
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+}
\ No newline at end of file