You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ro...@apache.org on 2017/11/07 09:25:19 UTC
[sling-org-apache-sling-commons-osgi] 05/11: SLING-4521 Commons
OSGi: Add helper class for thread-safe access list of ranked service
dependencies
This is an automated email from the ASF dual-hosted git repository.
rombert pushed a commit to annotated tag org.apache.sling.commons.osgi-2.3.0
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-commons-osgi.git
commit 609eaf5fbfe7c96b02c73da4dbbcae8282623919
Author: Stefan Seifert <ss...@apache.org>
AuthorDate: Thu Mar 19 11:31:57 2015 +0000
SLING-4521 Commons OSGi: Add helper class for thread-safe access list of ranked service dependencies
git-svn-id: https://svn.apache.org/repos/asf/sling/trunk/bundles/commons/osgi@1667709 13f79535-47bb-0310-9956-ffa450edef68
---
pom.xml | 13 ++
.../apache/sling/commons/osgi/RankedServices.java | 151 +++++++++++++++++++++
.../apache/sling/commons/osgi/package-info.java | 2 +-
.../sling/commons/osgi/RankedServicesTest.java | 81 +++++++++++
4 files changed, 246 insertions(+), 1 deletion(-)
diff --git a/pom.xml b/pom.xml
index 8f846e0..8d1035f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -104,6 +104,7 @@
<groupId>org.jmock</groupId>
<artifactId>jmock-junit4</artifactId>
</dependency>
+
<dependency>
<!-- Not used by our code, but need a non-bundle jar for unit tests -->
<groupId>javax.jcr</groupId>
@@ -118,6 +119,18 @@
<version>2.0.6</version>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <version>1.9.5</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ <version>15.0</version>
+ <scope>test</scope>
+ </dependency>
</dependencies>
</project>
diff --git a/src/main/java/org/apache/sling/commons/osgi/RankedServices.java b/src/main/java/org/apache/sling/commons/osgi/RankedServices.java
new file mode 100644
index 0000000..ee45f2e
--- /dev/null
+++ b/src/main/java/org/apache/sling/commons/osgi/RankedServices.java
@@ -0,0 +1,151 @@
+/*
+ * 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.sling.commons.osgi;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import aQute.bnd.annotation.ConsumerType;
+import aQute.bnd.annotation.ProviderType;
+
+/**
+ * Helper class that collects all services registered via OSGi bind/unbind methods.
+ * The services are ordered by service ranking and can be iterated directly using this object instance.
+ * Implementation is thread-safe.
+ * <p>Usage example:</p>
+ * <p>1. Define a dynamic reference with cardinality OPTIONAL_MULTIPLE in your service:
+ * <pre>
+ * @Reference(name = "myService", referenceInterface = MyService.class,
+ * cardinality = ReferenceCardinality.OPTIONAL_MULTIPLE, policy = ReferencePolicy.DYNAMIC)
+ * private final RankedServices<MyService> myServices = new RankedServices<MyService>();
+ * </pre>
+ * <p>2. Define bind/unbind methods that delegate to the RankedServices instance:</p>
+ * <pre>
+ * void bindMyService(MyService service, Map<String, Object> props) {
+ * myServices.bind(service, props);
+ * }
+ * void unbindMyService(MyService service, Map<String, Object> props) {
+ * myServices.unbind(service, props);
+ * }
+ * </pre>
+ * <p>To access the list of referenced services you can access them in a thread-safe manner:</p>
+ * <pre>
+ * for (MyService service : myServices) {
+ * // your code...
+ * }
+ * </pre>
+ * <p>Optionally you can pass in a {@link ChangeListener} instance to get notified when the list
+ * of referenced services has chagned.</p>
+ * @param <T> Service type
+ * @since 2.3
+ */
+@ProviderType
+public final class RankedServices<T> implements Iterable<T> {
+
+ private final ChangeListener changeListener;
+ private final SortedMap<Comparable<Object>, T> serviceMap = new TreeMap<Comparable<Object>, T>();
+ private volatile Collection<T> sortedServices = Collections.emptyList();
+
+ /**
+ * Instantiate without change listener.
+ */
+ public RankedServices() {
+ this(null);
+ }
+
+ /**
+ * Instantiate without change listener.
+ * @param changeListener Change listener
+ */
+ public RankedServices(ChangeListener changeListener) {
+ this.changeListener = changeListener;
+ }
+
+ /**
+ * Handle bind service event.
+ * @param service Service instance
+ * @param props Service reference properties
+ */
+ public void bind(T service, Map<String, Object> props) {
+ synchronized (serviceMap) {
+ serviceMap.put(ServiceUtil.getComparableForServiceRanking(props), service);
+ updateSortedServices();
+ }
+ }
+
+ /**
+ * Handle unbind service event.
+ * @param service Service instance
+ * @param props Service reference properties
+ */
+ public void unbind(T service, Map<String, Object> props) {
+ synchronized (serviceMap) {
+ serviceMap.remove(ServiceUtil.getComparableForServiceRanking(props));
+ updateSortedServices();
+ }
+ }
+
+ /**
+ * Update list of sorted services by copying it from the array and making it unmodifiable.
+ */
+ private void updateSortedServices() {
+ List<T> copiedList = new ArrayList<T>(serviceMap.values());
+ sortedServices = Collections.unmodifiableList(copiedList);
+ if (changeListener != null) {
+ changeListener.changed();
+ }
+ }
+
+ /**
+ * Lists all services registered in OSGi, sorted by service ranking.
+ * @return Collection of service instances
+ */
+ public Collection<T> get() {
+ return sortedServices;
+ }
+
+ /**
+ * Iterates all services registered in OSGi, sorted by service ranking.
+ * @return Iterator with service instances.
+ */
+ public Iterator<T> iterator() {
+ return sortedServices.iterator();
+ }
+
+ /**
+ * Notification for changes on services list.
+ */
+ @ConsumerType
+ public interface ChangeListener {
+
+ /**
+ * Is called when the list of ranked services was changed due to bundle bindings/unbindings.
+ * This method is called within a synchronized block, so it's code should be kept as efficient as possible.
+ */
+ void changed();
+
+ }
+
+}
diff --git a/src/main/java/org/apache/sling/commons/osgi/package-info.java b/src/main/java/org/apache/sling/commons/osgi/package-info.java
index ccd9e91..243300d 100644
--- a/src/main/java/org/apache/sling/commons/osgi/package-info.java
+++ b/src/main/java/org/apache/sling/commons/osgi/package-info.java
@@ -16,5 +16,5 @@
* specific language governing permissions and limitations
* under the License.
*/
-@aQute.bnd.annotation.Version("2.2")
+@aQute.bnd.annotation.Version("2.3")
package org.apache.sling.commons.osgi;
diff --git a/src/test/java/org/apache/sling/commons/osgi/RankedServicesTest.java b/src/test/java/org/apache/sling/commons/osgi/RankedServicesTest.java
new file mode 100644
index 0000000..867e165
--- /dev/null
+++ b/src/test/java/org/apache/sling/commons/osgi/RankedServicesTest.java
@@ -0,0 +1,81 @@
+/*
+ * 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.sling.commons.osgi;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import java.util.Map;
+
+import org.apache.sling.commons.osgi.RankedServices.ChangeListener;
+import org.junit.Test;
+import org.osgi.framework.Constants;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterators;
+
+public class RankedServicesTest {
+
+ private static final String SERVICE_1 = "service1";
+ private static final Map<String, Object> SERVICE_1_PROPS = ImmutableMap.<String, Object>builder()
+ .put(Constants.SERVICE_RANKING, 50).put(Constants.SERVICE_ID, 1L).build();
+ private static final String SERVICE_2 = "service2";
+ private static final Map<String, Object> SERVICE_2_PROPS = ImmutableMap.<String, Object>builder()
+ .put(Constants.SERVICE_RANKING, 10).put(Constants.SERVICE_ID, 2L).build();
+ private static final String SERVICE_3 = "service3";
+ private static final Map<String, Object> SERVICE_3_PROPS = ImmutableMap.<String, Object>builder()
+ .put(Constants.SERVICE_RANKING, 100).put(Constants.SERVICE_ID, 3L).build();
+
+ @Test
+ public void testSortedServices() {
+ RankedServices<Comparable> underTest = new RankedServices<Comparable>();
+ assertEquals(0, underTest.get().size());
+
+ underTest.bind(SERVICE_1, SERVICE_1_PROPS);
+ assertEquals(1, underTest.get().size());
+ Comparable[] services = Iterators.toArray(underTest.get().iterator(), Comparable.class);
+ assertSame(SERVICE_1, services[0]);
+
+ underTest.bind(SERVICE_2, SERVICE_2_PROPS);
+ underTest.bind(SERVICE_3, SERVICE_3_PROPS);
+ assertEquals(3, underTest.get().size());
+ services = Iterators.toArray(underTest.get().iterator(), Comparable.class);
+ assertSame(SERVICE_2, services[0]);
+ assertSame(SERVICE_1, services[1]);
+ assertSame(SERVICE_3, services[2]);
+
+ underTest.unbind(SERVICE_2, SERVICE_2_PROPS);
+ assertEquals(2, underTest.get().size());
+ services = Iterators.toArray(underTest.get().iterator(), Comparable.class);
+ assertSame(SERVICE_1, services[0]);
+ assertSame(SERVICE_3, services[1]);
+ }
+
+ @Test
+ public void testChangeListener() {
+ ChangeListener changeListener = mock(ChangeListener.class);
+
+ RankedServices<Comparable> underTest = new RankedServices<Comparable>(changeListener);
+ underTest.bind(SERVICE_1, SERVICE_1_PROPS);
+ verify(changeListener).changed();
+ }
+
+}
--
To stop receiving notification emails like this one, please contact
"commits@sling.apache.org" <co...@sling.apache.org>.