| /* |
| * 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 org.osgi.annotation.versioning.ConsumerType; |
| import org.osgi.annotation.versioning.ProviderType; |
| import org.osgi.framework.BundleContext; |
| import org.osgi.framework.ServiceReference; |
| |
| /** |
| * 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.<br><br> |
| * <i>With Declarative Services 1.3 supporting field injection with multiple cardinality (leveraging Collections), |
| * this class should only be used if DS 1.3 cannot be used for some reason. |
| * DS 1.3 is using the same ordering as {@link ServiceReference#compareTo(Object)}.</i> |
| * <br><br> |
| * <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>(Order.DESCENDING); |
| * </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 changed.</p> |
| * @param <T> Service type |
| * @since 2.3 |
| * @see "OSGi Compendium 6.0, Declarative Services 1.3, Reference Field Option, ยง112.3.8.1" |
| */ |
| @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 List<T> sortedServices = Collections.emptyList(); |
| private final Order order; |
| |
| /** |
| * Instantiate without change listener in ascending order (lowest service ranking first). |
| * @deprecated Use {@link #RankedServices(Order)} to explicitly give the order. |
| */ |
| @Deprecated |
| public RankedServices() { |
| this(Order.ASCENDING, null); |
| } |
| |
| /** |
| * Instantiate with change listener in ascending order (lowest service ranking first). |
| * @param changeListener Change listener |
| * @deprecated Use {@link #RankedServices(Order order, ChangeListener changeListener)} instead |
| */ |
| @Deprecated |
| public RankedServices(ChangeListener changeListener) { |
| this(Order.ASCENDING, changeListener); |
| } |
| |
| /** |
| * Instantiate without change listener but with a given order. |
| * @param order the order in which the services should be returned in {@link #iterator()} and {@link #get()}. |
| * Either {@link Order#ASCENDING} or {@link Order#DESCENDING}. |
| * Use {@link Order#DESCENDING} if you want to have the service with the highest ranking returned first |
| * (this is the service which would also be chosen by {@link BundleContext#getServiceReference(String)}). |
| * @since 2.4 |
| */ |
| public RankedServices(Order order) { |
| this(order, null); |
| } |
| |
| /** |
| * Instantiate with change listener. |
| * @param order the order in which the services should be returned in {@link #iterator()} and {@link #get()}. |
| * Either {@link Order#ASCENDING} or {@link Order#DESCENDING}. |
| * Use {@link Order#DESCENDING} if you want to have the service with the highest ranking returned first |
| * (this is the service which would also be chosen by {@link BundleContext#getServiceReference(String)}). |
| * @param changeListener Change listener |
| * @since 2.4 |
| */ |
| public RankedServices(Order order, ChangeListener changeListener) { |
| this.order = order; |
| 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, order), 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, order)); |
| 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 |
| * (either ascending or descending depending on the order given in the constructor). |
| * @return Collection of service instances |
| * @deprecated Use {@link #getList()} instead |
| */ |
| public Collection<T> get() { |
| return sortedServices; |
| } |
| |
| /** |
| * Lists all services registered in OSGi, sorted by service ranking |
| * (either ascending or descending depending on the order given in the constructor). |
| * @return List of service instances |
| */ |
| public List<T> getList() { |
| return sortedServices; |
| } |
| |
| /** |
| * Iterates all services registered in OSGi, sorted by service ranking |
| * (either ascending or descending depending on the order given in the constructor). |
| * @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(); |
| |
| } |
| |
| } |