You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by to...@apache.org on 2017/06/08 07:10:19 UTC
svn commit: r1798025 - in /jackrabbit/oak/trunk/oak-core-spi/src:
main/java/org/apache/jackrabbit/oak/osgi/
main/java/org/apache/jackrabbit/oak/spi/whiteboard/
test/java/org/apache/jackrabbit/oak/osgi/
test/java/org/apache/jackrabbit/oak/spi/whiteboard/
Author: tomekr
Date: Thu Jun 8 07:10:18 2017
New Revision: 1798025
URL: http://svn.apache.org/viewvc?rev=1798025&view=rev
Log:
OAK-6313: Support properties filtering in the Whiteboard track method
Added:
jackrabbit/oak/trunk/oak-core-spi/src/test/java/org/apache/jackrabbit/oak/spi/whiteboard/DefaultWhiteboardTest.java
Modified:
jackrabbit/oak/trunk/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/osgi/OsgiUtil.java
jackrabbit/oak/trunk/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/osgi/OsgiWhiteboard.java
jackrabbit/oak/trunk/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/spi/whiteboard/DefaultWhiteboard.java
jackrabbit/oak/trunk/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/spi/whiteboard/Whiteboard.java
jackrabbit/oak/trunk/oak-core-spi/src/test/java/org/apache/jackrabbit/oak/osgi/OsgiUtilTest.java
Modified: jackrabbit/oak/trunk/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/osgi/OsgiUtil.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/osgi/OsgiUtil.java?rev=1798025&r1=1798024&r2=1798025&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/osgi/OsgiUtil.java (original)
+++ jackrabbit/oak/trunk/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/osgi/OsgiUtil.java Thu Jun 8 07:10:18 2017
@@ -18,8 +18,14 @@
package org.apache.jackrabbit.oak.osgi;
import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Filter;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.InvalidSyntaxException;
import org.osgi.service.component.ComponentContext;
+import java.util.Map;
+
import static com.google.common.base.Preconditions.checkNotNull;
/**
@@ -153,4 +159,46 @@ public class OsgiUtil {
return string;
}
+ /**
+ * Create a {@link Filter} using the passed Class as an objectClass and the map
+ * as the filter attributes.
+ * @param clazz the target objectClass
+ * @param attributes target attributes (null value for the absence)
+ * @return OSGi filter representing the input
+ */
+ public static Filter getFilter(Class<?> clazz, Map<String, String> attributes) {
+ StringBuilder filterBuilder = new StringBuilder("(&");
+ appendLdapFilterAttribute(filterBuilder, Constants.OBJECTCLASS, clazz.getName());
+ for (Map.Entry<String, String> e : attributes.entrySet()) {
+ appendLdapFilterAttribute(filterBuilder, e.getKey(), e.getValue());
+ }
+ filterBuilder.append(')');
+ try {
+ return FrameworkUtil.createFilter(filterBuilder.toString());
+ } catch(InvalidSyntaxException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ static StringBuilder appendLdapFilterAttribute(StringBuilder filterBuilder, String key, String value) {
+ if (value == null) {
+ filterBuilder.append("(!(").append(key).append("=*))");
+ } else {
+ filterBuilder.append("(").append(key).append("=");
+ appendEscapedLdapValue(filterBuilder, value);
+ filterBuilder.append(")");
+ }
+ return filterBuilder;
+ }
+
+ static StringBuilder appendEscapedLdapValue(StringBuilder filterBuilder, String value) {
+ for (int i = 0; i < value.length(); i++) {
+ char c = value.charAt(i);
+ if ((c == '\\') || (c == '(') || (c == ')') || (c == '*')) {
+ filterBuilder.append('\\');
+ }
+ filterBuilder.append(c);
+ }
+ return filterBuilder;
+ }
}
Modified: jackrabbit/oak/trunk/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/osgi/OsgiWhiteboard.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/osgi/OsgiWhiteboard.java?rev=1798025&r1=1798024&r2=1798025&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/osgi/OsgiWhiteboard.java (original)
+++ jackrabbit/oak/trunk/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/osgi/OsgiWhiteboard.java Thu Jun 8 07:10:18 2017
@@ -22,7 +22,9 @@ import static com.google.common.collect.
import static com.google.common.collect.Maps.newHashMap;
import static com.google.common.collect.Maps.newTreeMap;
import static java.util.Collections.emptyList;
+import static java.util.Collections.emptyMap;
import static java.util.Collections.singletonList;
+import static org.apache.jackrabbit.oak.osgi.OsgiUtil.getFilter;
import java.util.Collections;
import java.util.Dictionary;
@@ -33,11 +35,13 @@ import java.util.SortedMap;
import java.util.concurrent.atomic.AtomicReference;
import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
import org.apache.jackrabbit.oak.spi.whiteboard.Registration;
import org.apache.jackrabbit.oak.spi.whiteboard.Tracker;
import org.apache.jackrabbit.oak.spi.whiteboard.Whiteboard;
import org.osgi.framework.BundleContext;
+import org.osgi.framework.Filter;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.ServiceRegistration;
import org.osgi.util.tracker.ServiceTracker;
@@ -101,6 +105,15 @@ public class OsgiWhiteboard implements W
*/
@Override
public <T> Tracker<T> track(final Class<T> type) {
+ return track(type, emptyMap());
+ }
+
+ @Override
+ public <T> Tracker<T> track(Class<T> type, Map<String, String> filterProperties) {
+ return track(type, getFilter(type, filterProperties));
+ }
+
+ private <T> Tracker<T> track(Class<T> type, Filter filter) {
checkNotNull(type);
final AtomicReference<List<T>> list =
new AtomicReference<List<T>>(Collections.<T>emptyList());
@@ -143,8 +156,8 @@ public class OsgiWhiteboard implements W
context.ungetService(reference);
}
};
- final ServiceTracker tracker =
- new ServiceTracker(context, type.getName(), customizer);
+
+ final ServiceTracker tracker = new ServiceTracker(context, filter, customizer);
tracker.open();
return new Tracker<T>() {
@Override
Modified: jackrabbit/oak/trunk/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/spi/whiteboard/DefaultWhiteboard.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/spi/whiteboard/DefaultWhiteboard.java?rev=1798025&r1=1798024&r2=1798025&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/spi/whiteboard/DefaultWhiteboard.java (original)
+++ jackrabbit/oak/trunk/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/spi/whiteboard/DefaultWhiteboard.java Thu Jun 8 07:10:18 2017
@@ -16,9 +16,10 @@
*/
package org.apache.jackrabbit.oak.spi.whiteboard;
+import javax.annotation.Nonnull;
+
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.collect.Lists.newArrayList;
import static com.google.common.collect.Maps.newHashMap;
import static com.google.common.collect.Sets.newIdentityHashSet;
import static java.util.Collections.emptyList;
@@ -26,13 +27,14 @@ import static java.util.Collections.empt
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.stream.Collectors;
public class DefaultWhiteboard implements Whiteboard {
- private final Map<Class<?>, Set<Object>> registry = newHashMap();
+ private final Map<Class<?>, Set<Service>> registry = newHashMap();
- private synchronized <T> void registered(Class<T> type, T service) {
- Set<Object> services = registry.get(type);
+ private synchronized <T> void registered(Class<T> type, Service service) {
+ Set<Service> services = registry.get(type);
if (services == null) {
services = newIdentityHashSet();
registry.put(type, services);
@@ -40,8 +42,8 @@ public class DefaultWhiteboard implement
services.add(service);
}
- private synchronized <T> void unregistered(Class<T> type, T service) {
- Set<Object> services = registry.get(type);
+ private synchronized <T> void unregistered(Class<T> type, Service service) {
+ Set<Service> services = registry.get(type);
if (services != null) {
services.remove(service);
}
@@ -49,9 +51,26 @@ public class DefaultWhiteboard implement
@SuppressWarnings("unchecked")
private synchronized <T> List<T> lookup(Class<T> type) {
- Set<Object> services = registry.get(type);
+ Set<Service> services = registry.get(type);
if (services != null) {
- return (List<T>) newArrayList(services);
+ return (List<T>) services
+ .stream()
+ .map(Service::getService)
+ .collect(Collectors.toList());
+ } else {
+ return emptyList();
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private synchronized <T> List<T> lookup(Class<T> type, Map<String, String> filterProperties) {
+ Set<Service> services = registry.get(type);
+ if (services != null) {
+ return (List<T>) services
+ .stream()
+ .filter(s -> s.matches(filterProperties))
+ .map(Service::getService)
+ .collect(Collectors.toList());
} else {
return emptyList();
}
@@ -65,11 +84,14 @@ public class DefaultWhiteboard implement
checkNotNull(type);
checkNotNull(service);
checkArgument(type.isInstance(service));
- registered(type, service);
+
+ Service s = new Service(service, properties);
+
+ registered(type, s);
return new Registration() {
@Override
public void unregister() {
- unregistered(type, service);
+ unregistered(type, s);
}
};
}
@@ -88,4 +110,53 @@ public class DefaultWhiteboard implement
};
}
+ @Override
+ public <T> Tracker<T> track(Class<T> type, Map<String, String> filterProperties) {
+
+ checkNotNull(type);
+ return new Tracker<T>() {
+ @Override
+ public List<T> getServices() {
+ return lookup(type, filterProperties);
+ }
+ @Override
+ public void stop() {
+ }
+ };
+ }
+
+ private static class Service {
+
+ private final Object service;
+
+ private final Map<?, ?> properties;
+
+ private Service(@Nonnull Object service, Map<?, ?> properties) {
+ checkNotNull(service);
+ this.service = service;
+ this.properties = properties;
+ }
+
+ private Object getService() {
+ return service;
+ }
+
+ private boolean matches(Map<String, String> properties) {
+ return properties.entrySet().stream()
+ .allMatch(this::propertyMatches);
+ }
+
+ private boolean propertyMatches(Map.Entry<String, String> filterEntry) {
+ String key = filterEntry.getKey();
+ String expectedValue = filterEntry.getValue();
+ if (properties == null || !properties.containsKey(key)) {
+ return expectedValue == null;
+ }
+ Object value = properties.get(key);
+ if (value == null) {
+ return expectedValue == null;
+ }
+ return value.toString().equals(expectedValue);
+ }
+ }
}
Modified: jackrabbit/oak/trunk/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/spi/whiteboard/Whiteboard.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/spi/whiteboard/Whiteboard.java?rev=1798025&r1=1798024&r2=1798025&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/spi/whiteboard/Whiteboard.java (original)
+++ jackrabbit/oak/trunk/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/spi/whiteboard/Whiteboard.java Thu Jun 8 07:10:18 2017
@@ -39,4 +39,15 @@ public interface Whiteboard {
*/
<T> Tracker<T> track(Class<T> type);
+ /**
+ * Starts tracking services of the given type, with given attributes.
+ *
+ * @param type type of the services to track
+ * @param filterProperties only services with these properties will be tracked.
+ * Null keys are not permitted. Null values means that
+ * the property should be absent.
+ * @return service tracker
+ */
+ <T> Tracker<T> track(Class<T> type, Map<String, String> filterProperties);
+
}
Modified: jackrabbit/oak/trunk/oak-core-spi/src/test/java/org/apache/jackrabbit/oak/osgi/OsgiUtilTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core-spi/src/test/java/org/apache/jackrabbit/oak/osgi/OsgiUtilTest.java?rev=1798025&r1=1798024&r2=1798025&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core-spi/src/test/java/org/apache/jackrabbit/oak/osgi/OsgiUtilTest.java (original)
+++ jackrabbit/oak/trunk/oak-core-spi/src/test/java/org/apache/jackrabbit/oak/osgi/OsgiUtilTest.java Thu Jun 8 07:10:18 2017
@@ -17,12 +17,20 @@
package org.apache.jackrabbit.oak.osgi;
+import org.apache.jackrabbit.oak.spi.whiteboard.WhiteboardUtils;
import org.junit.Test;
import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.InvalidSyntaxException;
import org.osgi.service.component.ComponentContext;
import java.util.Dictionary;
+import java.util.Map;
+import static com.google.common.collect.Maps.newLinkedHashMap;
+import static org.apache.jackrabbit.oak.osgi.OsgiUtil.appendEscapedLdapValue;
+import static org.apache.jackrabbit.oak.osgi.OsgiUtil.appendLdapFilterAttribute;
+import static org.apache.jackrabbit.oak.osgi.OsgiUtil.getFilter;
import static org.apache.jackrabbit.oak.osgi.OsgiUtil.lookup;
import static org.apache.jackrabbit.oak.osgi.OsgiUtil.lookupConfigurationThenFramework;
import static org.apache.jackrabbit.oak.osgi.OsgiUtil.lookupFrameworkThenConfiguration;
@@ -204,4 +212,27 @@ public class OsgiUtilTest {
assertEquals("fvalue", lookupFrameworkThenConfiguration(componentContext, "cname", "fname"));
}
+ @Test
+ public void filterBuilding() throws InvalidSyntaxException {
+ StringBuilder b = new StringBuilder();
+
+ assertEquals("foo\\\\bar\\(foo\\)bar\\*foo", appendEscapedLdapValue(b, "foo\\bar(foo)bar*foo").toString());
+ b.setLength(0);
+
+ assertEquals("(foo=bar)", appendLdapFilterAttribute(b, "foo", "bar").toString());
+ b.setLength(0);
+
+ assertEquals("(foo=\\(bar\\))", appendLdapFilterAttribute(b, "foo", "(bar)").toString());
+ b.setLength(0);
+
+ assertEquals("(!(foo=*))", appendLdapFilterAttribute(b, "foo", null).toString());
+ b.setLength(0);
+
+ Map<String, String> m = newLinkedHashMap();
+ m.put("foo", "bar");
+ m.put("empty", null);
+ m.put("escaped", "*xyz)");
+ assertEquals(FrameworkUtil.createFilter("(&(objectClass=java.lang.String)(foo=bar)(!(empty=*))(escaped=\\*xyz\\)))"), getFilter(String.class, m));
+ b.setLength(0);
+ }
}
Added: jackrabbit/oak/trunk/oak-core-spi/src/test/java/org/apache/jackrabbit/oak/spi/whiteboard/DefaultWhiteboardTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core-spi/src/test/java/org/apache/jackrabbit/oak/spi/whiteboard/DefaultWhiteboardTest.java?rev=1798025&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core-spi/src/test/java/org/apache/jackrabbit/oak/spi/whiteboard/DefaultWhiteboardTest.java (added)
+++ jackrabbit/oak/trunk/oak-core-spi/src/test/java/org/apache/jackrabbit/oak/spi/whiteboard/DefaultWhiteboardTest.java Thu Jun 8 07:10:18 2017
@@ -0,0 +1,148 @@
+/*
+ * 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
+ *
+ * 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.jackrabbit.oak.spi.whiteboard;
+
+import com.google.common.collect.ImmutableMap;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Map;
+import java.util.Set;
+
+import static com.google.common.collect.ImmutableSet.of;
+import static java.util.Collections.singletonMap;
+import static java.util.stream.Collectors.toSet;
+import static org.junit.Assert.assertEquals;
+
+public class DefaultWhiteboardTest {
+
+ private Whiteboard whiteboard;
+
+ @Before
+ public void createWhiteboard() {
+ whiteboard = new DefaultWhiteboard();
+ }
+
+ @Test
+ public void filteredTracker() {
+ whiteboard.register(Service1.class, new Service1("s1"), ImmutableMap.of());
+ whiteboard.register(Service2.class, new Service2("s2"), ImmutableMap.of("role", "myrole"));
+ whiteboard.register(Service3.class, new Service3("s3_1"), ImmutableMap.of());
+ whiteboard.register(Service3.class, new Service3("s3_2"), ImmutableMap.of("role", "myrole"));
+ whiteboard.register(Service3.class, new Service3("s3_3"), ImmutableMap.of("role", "myotherrole", "id", 1024));
+
+ assertEquals(of("s1"), track(Service1.class));
+ assertEquals(of("s1"), track(Service1.class, singletonMap("role", null)));
+ assertEquals(of(), track(Service1.class, ImmutableMap.of("role", "myrole")));
+
+ assertEquals(of("s2"), track(Service2.class));
+ assertEquals(of(), track(Service2.class, singletonMap("role", null)));
+ assertEquals(of("s2"), track(Service2.class, ImmutableMap.of("role", "myrole")));
+
+ assertEquals(of("s3_1", "s3_2", "s3_3"), track(Service3.class));
+ assertEquals(of("s3_1"), track(Service3.class, singletonMap("role", null)));
+ assertEquals(of("s3_2"), track(Service3.class, ImmutableMap.of("role", "myrole")));
+ assertEquals(of("s3_3"), track(Service3.class, ImmutableMap.of("role", "myotherrole")));
+ assertEquals(of("s3_3"), track(Service3.class, ImmutableMap.of("role", "myotherrole", "id", "1024")));
+ assertEquals(of("s3_3"), track(Service3.class, ImmutableMap.of("id", "1024")));
+ assertEquals(of(), track(Service3.class, ImmutableMap.of("id", "2048")));
+ }
+
+ @Test
+ public void sameServiceRegisteredAgain() {
+ Service1 s1 = new Service1("s1");
+
+ whiteboard.register(Service1.class, s1, ImmutableMap.of());
+ whiteboard.register(Service1.class, s1, ImmutableMap.of());
+ whiteboard.register(Service1.class, s1, ImmutableMap.of());
+
+ assertEquals(of("s1"), track(Service1.class));
+ }
+
+ @Test
+ public void unregister() {
+ Registration r1 = whiteboard.register(Service1.class, new Service1("s1"), ImmutableMap.of());
+ Registration r2 = whiteboard.register(Service2.class, new Service2("s2"), ImmutableMap.of("role", "myrole"));
+ Registration r3_1 = whiteboard.register(Service3.class, new Service3("s3_1"), ImmutableMap.of());
+ Registration r3_2 = whiteboard.register(Service3.class, new Service3("s3_2"), ImmutableMap.of("role", "myrole"));
+ Registration r3_3 = whiteboard.register(Service3.class, new Service3("s3_3"), ImmutableMap.of("role", "myotherrole", "id", 1024));
+
+ assertEquals(of("s1"), track(Service1.class));
+ r1.unregister();
+ assertEquals(of(), track(Service1.class));
+
+ assertEquals(of("s2"), track(Service2.class));
+ r2.unregister();
+ assertEquals(of(), track(Service2.class));
+
+ assertEquals(of("s3_1", "s3_2", "s3_3"), track(Service3.class));
+ r3_1.unregister();
+ assertEquals(of("s3_2", "s3_3"), track(Service3.class));
+ r3_2.unregister();
+ assertEquals(of("s3_3"), track(Service3.class));
+ r3_3.unregister();
+ assertEquals(of(), track(Service3.class));
+ }
+
+ private <T extends Service> Set<String> track(Class<T> clazz) {
+ return track(clazz, null);
+ }
+
+ private <T extends Service> Set<String> track(Class<T> clazz, Map<String, String> properties) {
+ final Tracker<T> tracker;
+ if (properties == null) {
+ tracker = whiteboard.track(clazz);
+ } else {
+ tracker = whiteboard.track(clazz, properties);
+ }
+ try {
+ return tracker.getServices().stream().map(Service::getId).collect(toSet());
+ } finally {
+ tracker.stop();
+ }
+ }
+
+ public abstract static class Service {
+ private final String id;
+
+ private Service(String id) {
+ this.id = id;
+ }
+
+ public String getId() {
+ return id;
+ }
+ }
+
+ private final static class Service1 extends Service {
+ private Service1(String id) {
+ super(id);
+ }
+ }
+
+ private final static class Service2 extends Service {
+ private Service2(String id) {
+ super(id);
+ }
+ }
+
+ private final static class Service3 extends Service {
+ private Service3(String id) {
+ super(id);
+ }
+ }
+}