Added: felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/manager/ServiceTracker.java URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/manager/ServiceTracker.java?rev=1689973&view=auto ============================================================================== --- felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/manager/ServiceTracker.java (added) +++ felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/manager/ServiceTracker.java Wed Jul 8 22:10:14 2015 @@ -0,0 +1,1538 @@ +/* + * Copyright (c) OSGi Alliance (2000, 2012). All Rights Reserved. + * + * Licensed 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.felix.scr.impl.manager; + +import java.lang.reflect.Array; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.SortedMap; +import java.util.TreeMap; +import java.util.concurrent.atomic.AtomicInteger; + +import org.osgi.framework.AllServiceListener; +import org.osgi.framework.BundleContext; +import org.osgi.framework.Constants; +import org.osgi.framework.Filter; +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.framework.ServiceEvent; +import org.osgi.framework.ServiceListener; +import org.osgi.framework.ServiceReference; + +/** + * The {@code ServiceTracker} class simplifies using services from the + * Framework's service registry. + * <p> + * A {@code ServiceTracker} object is constructed with search criteria and a + * {@code ServiceTrackerCustomizer} object. A {@code ServiceTracker} can use a + * {@code ServiceTrackerCustomizer} to customize the service objects to be + * tracked. The {@code ServiceTracker} can then be opened to begin tracking all + * services in the Framework's service registry that match the specified search + * criteria. The {@code ServiceTracker} correctly handles all of the details of + * listening to {@code ServiceEvent}s and getting and ungetting services. + * <p> + * The {@code getServiceReferences} method can be called to get references to + * the services being tracked. The {@code getService} and {@code getServices} + * methods can be called to get the service objects for the tracked service. + * <p> + * The {@code ServiceTracker} class is thread-safe. It does not call a + * {@code ServiceTrackerCustomizer} while holding any locks. + * {@code ServiceTrackerCustomizer} implementations must also be thread-safe. + * + * @param <S> The type of the service being tracked. + * @param <T> The type of the tracked object. + * @ThreadSafe + * @version $Id: 21926ad8717a91633face6bbf570febfcd23b1c7 $ + */ + +/** + * changes from osgi service tracker: + * + * - included AbstractTracked as an inner class. + * - addedService method on customizer called after the object is tracked. + * - we always track all matching services. + * + * @param <S> + * @param <T> + */ +public class ServiceTracker<S, T> { + /* set this to true to compile in debug messages */ + static final boolean DEBUG = false; + /** + * The Bundle Context used by this {@code ServiceTracker}. + */ + protected final BundleContext context; + /** + * The Filter used by this {@code ServiceTracker} which specifies the search + * criteria for the services to track. + * + * @since 1.1 + */ + protected final Filter filter; + /** + * The {@code ServiceTrackerCustomizer} for this tracker. + */ + final ServiceTrackerCustomizer<S, T> customizer; + /** + * Filter string for use when adding the ServiceListener. If this field is + * set, then certain optimizations can be taken since we don't have a user + * supplied filter. + */ + final String listenerFilter; + /** + * Class name to be tracked. If this field is set, then we are tracking by + * class name. + */ + private final String trackClass; + /** + * Reference to be tracked. If this field is set, then we are tracking a + * single ServiceReference. + */ + private final ServiceReference<S> trackReference; + /** + * Tracked services: {@code ServiceReference} -> customized Object and + * {@code ServiceListener} object + */ + private volatile Tracked tracked; + + + /** + * whether the DependencyManager is getting the service immediately. + */ + private boolean active; + + /** + * Accessor method for the current Tracked object. This method is only + * intended to be used by the unsynchronized methods which do not modify the + * tracked field. + * + * @return The current Tracked object. + */ + public Tracked tracked() { + return tracked; + } + + /** + * Cached ServiceReference for getServiceReference. + * + * This field is volatile since it is accessed by multiple threads. + */ + private volatile ServiceReference<S> cachedReference; + /** + * Cached service object for getService. + * + * This field is volatile since it is accessed by multiple threads. + */ + private volatile T cachedService; + + /** + * Create a {@code ServiceTracker} on the specified {@code ServiceReference} + * . + * + * <p> + * The service referenced by the specified {@code ServiceReference} will be + * tracked by this {@code ServiceTracker}. + * + * @param context The {@code BundleContext} against which the tracking is + * done. + * @param reference The {@code ServiceReference} for the service to be + * tracked. + * @param customizer The customizer object to call when services are added, + * modified, or removed in this {@code ServiceTracker}. If customizer + * is {@code null}, then this {@code ServiceTracker} will be used as + * the {@code ServiceTrackerCustomizer} and this + * {@code ServiceTracker} will call the + * {@code ServiceTrackerCustomizer} methods on itself. + */ + public ServiceTracker(final BundleContext context, final ServiceReference<S> reference, final ServiceTrackerCustomizer<S, T> customizer) { + this.context = context; + this.trackReference = reference; + this.trackClass = null; + this.customizer = customizer; + this.listenerFilter = "(" + Constants.SERVICE_ID + "=" + reference.getProperty(Constants.SERVICE_ID).toString() + ")"; + try { + this.filter = context.createFilter(listenerFilter); + } catch (InvalidSyntaxException e) { + /* + * we could only get this exception if the ServiceReference was + * invalid + */ + IllegalArgumentException iae = new IllegalArgumentException("unexpected InvalidSyntaxException: " + e.getMessage()); + iae.initCause(e); + throw iae; + } + } + + /** + * Create a {@code ServiceTracker} on the specified class name. + * + * <p> + * Services registered under the specified class name will be tracked by + * this {@code ServiceTracker}. + * + * @param context The {@code BundleContext} against which the tracking is + * done. + * @param clazz The class name of the services to be tracked. + * @param customizer The customizer object to call when services are added, + * modified, or removed in this {@code ServiceTracker}. If customizer + * is {@code null}, then this {@code ServiceTracker} will be used as + * the {@code ServiceTrackerCustomizer} and this + * {@code ServiceTracker} will call the + * {@code ServiceTrackerCustomizer} methods on itself. + */ + public ServiceTracker(final BundleContext context, final String clazz, final ServiceTrackerCustomizer<S, T> customizer) { + this.context = context; + this.trackReference = null; + this.trackClass = clazz; + this.customizer = customizer; + // we call clazz.toString to verify clazz is non-null! + this.listenerFilter = "(" + Constants.OBJECTCLASS + "=" + clazz + ")"; + try { + this.filter = context.createFilter(listenerFilter); + } catch (InvalidSyntaxException e) { + /* + * we could only get this exception if the clazz argument was + * malformed + */ + IllegalArgumentException iae = new IllegalArgumentException("unexpected InvalidSyntaxException: " + e.getMessage()); + iae.initCause(e); + throw iae; + } + } + + /** + * Create a {@code ServiceTracker} on the specified {@code Filter} object. + * + * <p> + * Services which match the specified {@code Filter} object will be tracked + * by this {@code ServiceTracker}. + * + * @param context The {@code BundleContext} against which the tracking is + * done. + * @param filter The {@code Filter} to select the services to be tracked. + * @param customizer The customizer object to call when services are added, + * modified, or removed in this {@code ServiceTracker}. If customizer + * is null, then this {@code ServiceTracker} will be used as the + * {@code ServiceTrackerCustomizer} and this {@code ServiceTracker} + * will call the {@code ServiceTrackerCustomizer} methods on itself. + * @param initialActive Initial active state of the tracker. + * @since 1.1 + */ + public ServiceTracker(final BundleContext context, final Filter filter, final ServiceTrackerCustomizer<S, T> customizer, boolean initialActive) { + this.context = context; + this.trackReference = null; + this.trackClass = null; + this.listenerFilter = filter.toString(); + this.filter = filter; + this.customizer = customizer; + this.active = initialActive; + if ((context == null)) { + /* + * we throw a NPE here to be consistent with the other constructors + */ + throw new NullPointerException( "BundleContext"); + } + } + + /** + * Create a {@code ServiceTracker} on the specified class. + * + * <p> + * Services registered under the name of the specified class will be tracked + * by this {@code ServiceTracker}. + * + * @param context The {@code BundleContext} against which the tracking is + * done. + * @param clazz The class of the services to be tracked. + * @param customizer The customizer object to call when services are added, + * modified, or removed in this {@code ServiceTracker}. If customizer + * is {@code null}, then this {@code ServiceTracker} will be used as + * the {@code ServiceTrackerCustomizer} and this + * {@code ServiceTracker} will call the + * {@code ServiceTrackerCustomizer} methods on itself. + * @since 1.5 + */ + public ServiceTracker(final BundleContext context, final Class<S> clazz, final ServiceTrackerCustomizer<S, T> customizer) { + this(context, clazz.getName(), customizer); + } + + /** + * Open this {@code ServiceTracker} and begin tracking services. + * + * <p> + * This implementation calls {@code open(false)}. + * + * @throws java.lang.IllegalStateException If the {@code BundleContext} with + * which this {@code ServiceTracker} was created is no longer valid. + * @see #open(boolean, java.util.concurrent.atomic.AtomicInteger) + * @param trackingCount + */ + public void open( AtomicInteger trackingCount ) { + open(false, trackingCount ); + } + + /** + * Open this {@code ServiceTracker} and begin tracking services. + * + * <p> + * Services which match the search criteria specified when this + * {@code ServiceTracker} was created are now tracked by this + * {@code ServiceTracker}. + * + * + * @param trackAllServices If {@code true}, then this {@code ServiceTracker} + * will track all matching services regardless of class loader + * accessibility. If {@code false}, then this {@code ServiceTracker} + * will only track matching services which are class loader + * accessible to the bundle whose {@code BundleContext} is used by + * this {@code ServiceTracker}. + * @param trackingCount + * @throws java.lang.IllegalStateException If the {@code BundleContext} with + * which this {@code ServiceTracker} was created is no longer valid. + * @since 1.3 + */ + public void open( boolean trackAllServices, AtomicInteger trackingCount ) { + final Tracked t; + synchronized (this) { + if (tracked != null) { + return; + } + if (DEBUG) { + System.out.println("ServiceTracker.open: " + filter); + } + t = trackAllServices ? new AllTracked( trackingCount ) : new Tracked( trackingCount ); + synchronized (t) { + try { + context.addServiceListener(t, listenerFilter); + ServiceReference<S>[] references = null; + if (trackClass != null) { + references = getInitialReferences(trackAllServices, trackClass, null); + } else { + if (trackReference != null) { + if (trackReference.getBundle() != null) { + ServiceReference<S>[] single = new ServiceReference[] {trackReference}; + references = single; + } + } else { /* user supplied filter */ + references = getInitialReferences(trackAllServices, null, listenerFilter); + } + } + /* set tracked with the initial references */ + t.setInitial(references); + } catch (InvalidSyntaxException e) { + throw new RuntimeException("unexpected InvalidSyntaxException: " + e.getMessage(), e); + } + } + tracked = t; + } + /* Call tracked outside of synchronized region */ + t.trackInitial(); /* process the initial references */ + } + + /** + * Returns the list of initial {@code ServiceReference}s that will be + * tracked by this {@code ServiceTracker}. + * + * @param trackAllServices If {@code true}, use + * {@code getAllServiceReferences}. + * @param className The class name with which the service was registered, or + * {@code null} for all services. + * @param filterString The filter criteria or {@code null} for all services. + * @return The list of initial {@code ServiceReference}s. + * @throws InvalidSyntaxException If the specified filterString has an + * invalid syntax. + */ + private ServiceReference<S>[] getInitialReferences(boolean trackAllServices, String className, String filterString) throws InvalidSyntaxException { + ServiceReference<S>[] result = (ServiceReference<S>[]) ((trackAllServices) ? context.getAllServiceReferences(className, filterString) : context.getServiceReferences(className, filterString)); + return result; + } + + /** + * Close this {@code ServiceTracker}. + * + * <p> + * This method should be called when this {@code ServiceTracker} should end + * the tracking of services. + * + * <p> + * This implementation calls {@link #getServiceReferences()} to get the list + * of tracked services to remove. + * @param trackingCount + */ + public SortedMap<ServiceReference<S>, T> close( AtomicInteger trackingCount ) { + final Tracked outgoing; +// final ServiceReference<S>[] references; + SortedMap<ServiceReference<S>, T> map = new TreeMap<ServiceReference<S>, T>(Collections.reverseOrder()); + synchronized (this) { + outgoing = tracked; + if (outgoing == null) { + return map; + } + if (DEBUG) { + System.out.println("ServiceTracker.close: " + filter); + } + outgoing.close(); + synchronized ( outgoing ) + { + trackingCount.set( outgoing.getTrackingCount() ); + outgoing.copyEntries( map ); + } +// references = getServiceReferences(); +// tracked = null; + try { + context.removeServiceListener(outgoing); + } catch (IllegalStateException e) { + /* In case the context was stopped. */ + } + } + modified(); /* clear the cache */ + synchronized (outgoing) { + outgoing.notifyAll(); /* wake up any waiters */ + } +// if (references != null) { +// for (int i = 0; i < references.length; i++) { +// outgoing.untrack(references[i], null); +// } +// } + if (DEBUG) { + if ((cachedReference == null) && (cachedService == null)) { + System.out.println("ServiceTracker.close[cached cleared]: " + filter); + } + } + return map; + } + + public void completeClose(Map<ServiceReference<S>, T> toUntrack) { + final Tracked outgoing; + synchronized (this) { + outgoing = tracked; + if (outgoing == null) { + return; + } + if (DEBUG) { + System.out.println("ServiceTracker.close: " + filter); + } + } + for (ServiceReference<S> ref: toUntrack.keySet()) { + outgoing.untrack( ref, null ); + } + tracked = null; + } + + /** + * Default implementation of the + * {@code ServiceTrackerCustomizer.addingService} method. + * + * <p> + * This method is only called when this {@code ServiceTracker} has been + * constructed with a {@code null ServiceTrackerCustomizer} argument. + * + * <p> + * This implementation returns the result of calling {@code getService} on + * the {@code BundleContext} with which this {@code ServiceTracker} was + * created passing the specified {@code ServiceReference}. + * <p> + * This method can be overridden in a subclass to customize the service + * object to be tracked for the service being added. In that case, take care + * not to rely on the default implementation of + * {@link #removedService(ServiceReference, Object, int) removedService} to unget + * the service. + * + * @param reference The reference to the service being added to this + * {@code ServiceTracker}. + * @return The service object to be tracked for the service added to this + * {@code ServiceTracker}. + * @see ServiceTrackerCustomizer#addingService(org.osgi.framework.ServiceReference + */ + public T addingService(ServiceReference<S> reference, int trackingCount) { + T result = (T) context.getService(reference); + return result; + } + + /** + * Default implementation of the + * {@code ServiceTrackerCustomizer.modifiedService} method. + * + * <p> + * This method is only called when this {@code ServiceTracker} has been + * constructed with a {@code null ServiceTrackerCustomizer} argument. + * + * <p> + * This implementation does nothing. + * + * @param reference The reference to modified service. + * @param service The service object for the modified service. + * @see ServiceTrackerCustomizer#modifiedService(org.osgi.framework.ServiceReference, Object, int) + */ + public void modifiedService(ServiceReference<S> reference, T service, int trackingCount) { + /* do nothing */ + } + + /** + * Default implementation of the + * {@code ServiceTrackerCustomizer.removedService} method. + * + * <p> + * This method is only called when this {@code ServiceTracker} has been + * constructed with a {@code null ServiceTrackerCustomizer} argument. + * + * <p> + * This implementation calls {@code ungetService}, on the + * {@code BundleContext} with which this {@code ServiceTracker} was created, + * passing the specified {@code ServiceReference}. + * <p> + * This method can be overridden in a subclass. If the default + * implementation of {@link #addingService(ServiceReference, int) addingService} + * method was used, this method must unget the service. + * + * @param reference The reference to removed service. + * @param service The service object for the removed service. + * @see ServiceTrackerCustomizer#removedService(org.osgi.framework.ServiceReference, Object, int) + */ + public void removedService(ServiceReference<S> reference, T service, int trackingCount) { + context.ungetService(reference); + } + + /** + * Wait for at least one service to be tracked by this + * {@code ServiceTracker}. This method will also return when this + * {@code ServiceTracker} is closed. + * + * <p> + * It is strongly recommended that {@code waitForService} is not used during + * the calling of the {@code BundleActivator} methods. + * {@code BundleActivator} methods are expected to complete in a short + * period of time. + * + * <p> + * This implementation calls {@link #getService()} to determine if a service + * is being tracked. + * + * @param timeout The time interval in milliseconds to wait. If zero, the + * method will wait indefinitely. + * @return Returns the result of {@link #getService()}. + * @throws InterruptedException If another thread has interrupted the + * current thread. + * @throws IllegalArgumentException If the value of timeout is negative. + */ + public T waitForService(long timeout) throws InterruptedException { + if (timeout < 0) { + throw new IllegalArgumentException("timeout value is negative"); + } + + T object = getService(); + if (object != null) { + return object; + } + + final long endTime = (timeout == 0) ? 0 : (System.currentTimeMillis() + timeout); + do { + final Tracked t = tracked(); + if (t == null) { /* if ServiceTracker is not open */ + return null; + } + synchronized (t) { + if (t.size() == 0) { + t.wait(timeout); + } + } + object = getService(); + if (endTime > 0) { // if we have a timeout + timeout = endTime - System.currentTimeMillis(); + if (timeout <= 0) { // that has expired + break; + } + } + } while (object == null); + return object; + } + + /** + * Return an array of {@code ServiceReference}s for all services being + * tracked by this {@code ServiceTracker}. + * + * @return Array of {@code ServiceReference}s or {@code null} if no services + * are being tracked. + */ + public ServiceReference<S>[] getServiceReferences() { + final Tracked t = tracked(); + if (t == null) { /* if ServiceTracker is not open */ + return null; + } + synchronized (t) { + int length = t.size(); + if (length == 0) { + return null; + } + ServiceReference<S>[] result = new ServiceReference[length]; + return t.copyKeys(result); + } + } + + /** + * Returns a {@code ServiceReference} for one of the services being tracked + * by this {@code ServiceTracker}. + * + * <p> + * If multiple services are being tracked, the service with the highest + * ranking (as specified in its {@code service.ranking} property) is + * returned. If there is a tie in ranking, the service with the lowest + * service ID (as specified in its {@code service.id} property); that is, + * the service that was registered first is returned. This is the same + * algorithm used by {@code BundleContext.getServiceReference}. + * + * <p> + * This implementation calls {@link #getServiceReferences()} to get the list + * of references for the tracked services. + * + * @return A {@code ServiceReference} or {@code null} if no services are + * being tracked. + * @since 1.1 + */ + public ServiceReference<S> getServiceReference() { + ServiceReference<S> reference = cachedReference; + if (reference != null) { + if (DEBUG) { + System.out.println("ServiceTracker.getServiceReference[cached]: " + filter); + } + return reference; + } + if (DEBUG) { + System.out.println("ServiceTracker.getServiceReference: " + filter); + } + ServiceReference<S>[] references = getServiceReferences(); + int length = (references == null) ? 0 : references.length; + if (length == 0) { /* if no service is being tracked */ + return null; + } + int index = 0; + if (length > 1) { /* if more than one service, select highest ranking */ + int rankings[] = new int[length]; + int count = 0; + int maxRanking = Integer.MIN_VALUE; + for (int i = 0; i < length; i++) { + Object property = references[i].getProperty(Constants.SERVICE_RANKING); + int ranking = (property instanceof Integer) ? ((Integer) property).intValue() : 0; + rankings[i] = ranking; + if (ranking > maxRanking) { + index = i; + maxRanking = ranking; + count = 1; + } else { + if (ranking == maxRanking) { + count++; + } + } + } + if (count > 1) { /* if still more than one service, select lowest id */ + long minId = Long.MAX_VALUE; + for (int i = 0; i < length; i++) { + if (rankings[i] == maxRanking) { + long id = ((Long) (references[i].getProperty(Constants.SERVICE_ID))).longValue(); + if (id < minId) { + index = i; + minId = id; + } + } + } + } + } + return cachedReference = references[index]; + } + + /** + * Returns the service object for the specified {@code ServiceReference} if + * the specified referenced service is being tracked by this + * {@code ServiceTracker}. + * + * @param reference The reference to the desired service. + * @return A service object or {@code null} if the service referenced by the + * specified {@code ServiceReference} is not being tracked. + */ + public T getService(ServiceReference<S> reference) { + final Tracked t = tracked(); + if (t == null) { /* if ServiceTracker is not open */ + return null; + } + synchronized (t) { + return t.getCustomizedObject(reference); + } + } + + /** + * Return an array of service objects for all services being tracked by this + * {@code ServiceTracker}. + * + * <p> + * This implementation calls {@link #getServiceReferences()} to get the list + * of references for the tracked services and then calls + * {@link #getService(ServiceReference)} for each reference to get the + * tracked service object. + * + * @return An array of service objects or {@code null} if no services are + * being tracked. + */ + public Object[] getServices() { + final Tracked t = tracked(); + if (t == null) { /* if ServiceTracker is not open */ + return null; + } + synchronized (t) { + ServiceReference<S>[] references = getServiceReferences(); + int length = (references == null) ? 0 : references.length; + if (length == 0) { + return null; + } + Object[] objects = new Object[length]; + for (int i = 0; i < length; i++) { + objects[i] = getService(references[i]); + } + return objects; + } + } + + /** + * Returns a service object for one of the services being tracked by this + * {@code ServiceTracker}. + * + * <p> + * If any services are being tracked, this implementation returns the result + * of calling {@code getService(getServiceReference())}. + * + * @return A service object or {@code null} if no services are being + * tracked. + */ + public T getService() { + T service = cachedService; + if (service != null) { + if (DEBUG) { + System.out.println("ServiceTracker.getService[cached]: " + filter); + } + return service; + } + if (DEBUG) { + System.out.println("ServiceTracker.getService: " + filter); + } + ServiceReference<S> reference = getServiceReference(); + if (reference == null) { + return null; + } + return cachedService = getService(reference); + } + + /** + * Remove a service from this {@code ServiceTracker}. + * + * The specified service will be removed from this {@code ServiceTracker}. + * If the specified service was being tracked then the + * {@code ServiceTrackerCustomizer.removedService} method will be called for + * that service. + * + * @param reference The reference to the service to be removed. + */ + public void remove(ServiceReference<S> reference) { + final Tracked t = tracked(); + if (t == null) { /* if ServiceTracker is not open */ + return; + } + t.untrack(reference, null); + } + + /** + * Return the number of services being tracked by this + * {@code ServiceTracker}. + * + * @return The number of services being tracked. + */ + public int size() { + final Tracked t = tracked(); + if (t == null) { /* if ServiceTracker is not open */ + return 0; + } + synchronized (t) { + return t.size(); + } + } + + /** + * Returns the tracking count for this {@code ServiceTracker}. + * + * The tracking count is initialized to 0 when this {@code ServiceTracker} + * is opened. Every time a service is added, modified or removed from this + * {@code ServiceTracker}, the tracking count is incremented. + * + * <p> + * The tracking count can be used to determine if this + * {@code ServiceTracker} has added, modified or removed a service by + * comparing a tracking count value previously collected with the current + * tracking count value. If the value has not changed, then no service has + * been added, modified or removed from this {@code ServiceTracker} since + * the previous tracking count was collected. + * + * @since 1.2 + * @return The tracking count for this {@code ServiceTracker} or -1 if this + * {@code ServiceTracker} is not open. + */ + public int getTrackingCount() { + final Tracked t = tracked(); + if (t == null) { /* if ServiceTracker is not open */ + return -1; + } + synchronized (t) { + return t.getTrackingCount(); + } + } + + /** + * Called by the Tracked object whenever the set of tracked services is + * modified. Clears the cache. + */ + /* + * This method must not be synchronized since it is called by Tracked while + * Tracked is synchronized. We don't want synchronization interactions + * between the listener thread and the user thread. + */ + void modified() { + cachedReference = null; /* clear cached value */ + cachedService = null; /* clear cached value */ + if (DEBUG) { + System.out.println("ServiceTracker.modified: " + filter); + } + } + + /** + * Return a {@code SortedMap} of the {@code ServiceReference}s and service + * objects for all services being tracked by this {@code ServiceTracker}. + * The map is sorted in reverse natural order of {@code ServiceReference}. + * That is, the first entry is the service with the highest ranking and the + * lowest service id. + * + * @return A {@code SortedMap} with the {@code ServiceReference}s and + * service objects for all services being tracked by this + * {@code ServiceTracker}. If no services are being tracked, then + * the returned map is empty. + * @since 1.5 + * @param activate + * @param trackingCount + */ + public SortedMap<ServiceReference<S>, T> getTracked( Boolean activate, AtomicInteger trackingCount ) { + SortedMap<ServiceReference<S>, T> map = new TreeMap<ServiceReference<S>, T>(Collections.reverseOrder()); + final Tracked t = tracked(); + if (t == null) { /* if ServiceTracker is not open */ + return map; + } + synchronized (t) { + if ( activate != null ) + { + active = activate; + } + trackingCount.set( t.getTrackingCount() ); + return t.copyEntries(map); + } + } + + void deactivate() { + final Tracked t = tracked(); + if (t == null) { /* if ServiceTracker is not open */ + return; + } + synchronized (t) { + active = false; + } + } + + /** + * Return if this {@code ServiceTracker} is empty. + * + * @return {@code true} if this {@code ServiceTracker} is not tracking any + * services. + * @since 1.5 + */ + public boolean isEmpty() { + final Tracked t = tracked(); + if (t == null) { /* if ServiceTracker is not open */ + return true; + } + synchronized (t) { + return t.isEmpty(); + } + } + + public int getServiceCount() { + final Tracked t = tracked(); + if (t == null) { /* if ServiceTracker is not open */ + return 0; + } + synchronized (t) { + return t.size(); + } + } + + public boolean isActive() { + final Tracked t = tracked(); + if (t == null) { /* if ServiceTracker is not open */ + return false; + } + synchronized (t) { + return active; + } + + } + + /** + * Return an array of service objects for all services being tracked by this + * {@code ServiceTracker}. The runtime type of the returned array is that of + * the specified array. + * + * <p> + * This implementation calls {@link #getServiceReferences()} to get the list + * of references for the tracked services and then calls + * {@link #getService(ServiceReference)} for each reference to get the + * tracked service object. + * + * @param array An array into which the tracked service objects will be + * stored, if the array is large enough. + * @return An array of service objects being tracked. If the specified array + * is large enough to hold the result, then the specified array is + * returned. If the specified array is longer then necessary to hold + * the result, the array element after the last service object is + * set to {@code null}. If the specified array is not large enough + * to hold the result, a new array is created and returned. + * @since 1.5 + */ + public T[] getServices(T[] array) { + final Tracked t = tracked(); + if (t == null) { /* if ServiceTracker is not open */ + if (array.length > 0) { + array[0] = null; + } + return array; + } + synchronized (t) { + ServiceReference<S>[] references = getServiceReferences(); + int length = (references == null) ? 0 : references.length; + if (length == 0) { + if (array.length > 0) { + array[0] = null; + } + return array; + } + if (length > array.length) { + array = (T[]) Array.newInstance(array.getClass().getComponentType(), length); + } + for (int i = 0; i < length; i++) { + array[i] = getService(references[i]); + } + if (array.length > length) { + array[length] = null; + } + return array; + } + } + + /** + * Abstract class to track items. If a Tracker is reused (closed then reopened), + * then a new AbstractTracked object is used. This class acts a map of tracked + * item -> customized object. Subclasses of this class will act as the listener + * object for the tracker. This class is used to synchronize access to the + * tracked items. This is not a public class. It is only for use by the + * implementation of the Tracker class. + * + * @param <S> The tracked item. It is the key. + * @param <T> The value mapped to the tracked item. + * @param <R> The reason the tracked item is being tracked or untracked. + * @ThreadSafe + * @version $Id: 16340086b98d308c2d12f13bcd87fc6467a5a367 $ + * @since 1.4 + */ + abstract class AbstractTracked<S, T, R> { + /* set this to true to compile in debug messages */ + static final boolean DEBUG = false; + + /** + * Map of tracked items to customized objects. + * + * @GuardedBy this + */ + private final Map<S, T> tracked; + + /** + * Modification count. This field is initialized to zero and incremented by + * modified. + * + * @GuardedBy this + */ + private final AtomicInteger trackingCount; + + /** + * List of items in the process of being added. This is used to deal with + * nesting of events. Since events may be synchronously delivered, events + * can be nested. For example, when processing the adding of a service and + * the customizer causes the service to be unregistered, notification to the + * nested call to untrack that the service was unregistered can be made to + * the track method. + * + * Since the ArrayList implementation is not synchronized, all access to + * this list must be protected by the same synchronized object for + * thread-safety. + * + * @GuardedBy this + */ + private final List<S> adding; + + /** + * true if the tracked object is closed. + * + * This field is volatile because it is set by one thread and read by + * another. + */ + volatile boolean closed; + + /** + * Initial list of items for the tracker. This is used to correctly process + * the initial items which could be modified before they are tracked. This + * is necessary since the initial set of tracked items are not "announced" + * by events and therefore the event which makes the item untracked could be + * delivered before we track the item. + * + * An item must not be in both the initial and adding lists at the same + * time. An item must be moved from the initial list to the adding list + * "atomically" before we begin tracking it. + * + * Since the LinkedList implementation is not synchronized, all access to + * this list must be protected by the same synchronized object for + * thread-safety. + * + * @GuardedBy this + */ + private final LinkedList<S> initial; + + /** + * AbstractTracked constructor. + * @param trackingCount + */ + AbstractTracked( AtomicInteger trackingCount ) { + tracked = new HashMap<S, T>(); + this.trackingCount = trackingCount; + adding = new ArrayList<S>(6); + initial = new LinkedList<S>(); + closed = false; + } + + /** + * Set initial list of items into tracker before events begin to be + * received. + * + * This method must be called from Tracker's open method while synchronized + * on this object in the same synchronized block as the add listener call. + * + * @param list The initial list of items to be tracked. {@code null} entries + * in the list are ignored. + * @GuardedBy this + */ + void setInitial(S[] list) { + if (list == null) { + return; + } + for (S item : list) { + if (item == null) { + continue; + } + if (DEBUG) { + System.out.println("AbstractTracked.setInitial: " + item); //$NON-NLS-1$ + } + initial.add(item); + } + } + + /** + * Track the initial list of items. This is called after events can begin to + * be received. + * + * This method must be called from Tracker's open method while not + * synchronized on this object after the add listener call. + * + */ + void trackInitial() { + while (true) { + S item; + synchronized (this) { + if (closed || (initial.size() == 0)) { + /* + * if there are no more initial items + */ + return; /* we are done */ + } + /* + * move the first item from the initial list to the adding list + * within this synchronized block. + */ + item = initial.removeFirst(); + if (tracked.get(item) != null) { + /* if we are already tracking this item */ + if (DEBUG) { + System.out.println("AbstractTracked.trackInitial[already tracked]: " + item); //$NON-NLS-1$ + } + continue; /* skip this item */ + } + if (adding.contains(item)) { + /* + * if this item is already in the process of being added. + */ + if (DEBUG) { + System.out.println("AbstractTracked.trackInitial[already adding]: " + item); //$NON-NLS-1$ + } + continue; /* skip this item */ + } + adding.add(item); + } + if (DEBUG) { + System.out.println("AbstractTracked.trackInitial: " + item); //$NON-NLS-1$ + } + trackAdding(item, null); /* + * Begin tracking it. We call trackAdding + * since we have already put the item in the + * adding list. + */ + } + } + + /** + * Called by the owning Tracker object when it is closed. + */ + void close() { + closed = true; + } + + /** + * Begin to track an item. + * + * @param item Item to be tracked. + * @param related Action related object. + */ + void track(final S item, final R related) { + boolean tracking; + final T object; + int trackingCount = -1; + synchronized (this) { + if (closed) { + return; + } + tracking = tracked.containsKey( item ); + object = tracked.get(item); + if (!tracking) { /* we are not tracking the item */ + if (adding.contains(item)) { + /* if this item is already in the process of being added. */ + if (DEBUG) { + System.out.println("AbstractTracked.track[already adding]: " + item); //$NON-NLS-1$ + } + return; + } + adding.add(item); /* mark this item is being added */ + } else { /* we are currently tracking this item */ + if (DEBUG) { + System.out.println("AbstractTracked.track[modified]: " + item); //$NON-NLS-1$ + } + trackingCount = modified(); /* increment modification count */ + } + } + + if (!tracking) { /* we are not tracking the item */ + trackAdding(item, related); + } else { + /* Call customizer outside of synchronized region */ + customizerModified(item, related, object, trackingCount ); + /* + * If the customizer throws an unchecked exception, it is safe to + * let it propagate + */ + } + } + + /** + * Common logic to add an item to the tracker used by track and + * trackInitial. The specified item must have been placed in the adding list + * before calling this method. + * + * @param item Item to be tracked. + * @param related Action related object. + */ + private void trackAdding(final S item, final R related) { + if (DEBUG) { + System.out.println("AbstractTracked.trackAdding: " + item); //$NON-NLS-1$ + } + T object = null; + boolean becameUntracked = false; + int trackingCount = -1; + int serviceCount = -1; + /* Call customizer outside of synchronized region */ + try { + object = customizerAdding(item, related); + /* + * If the customizer throws an unchecked exception, it will + * propagate after the finally + */ + } finally { + synchronized (this) { + if (adding.remove(item) && !closed) { + /* + * if the item was not untracked during the customizer + * callback + */ + tracked.put( item, object ); + trackingCount = modified(); /* increment modification count */ + serviceCount = tracked.size(); + notifyAll(); /* notify any waiters */ + } else { + becameUntracked = true; + } + } + } + /* + * The item became untracked during the customizer callback. + */ + if (becameUntracked) { + if (DEBUG) { + System.out.println("AbstractTracked.trackAdding[removed]: " + item); //$NON-NLS-1$ + } + /* Call customizer outside of synchronized region */ + customizerRemoved(item, related, object, trackingCount ); + /* + * If the customizer throws an unchecked exception, it is safe to + * let it propagate + */ + } else { + customizerAdded( item, related, object, trackingCount, serviceCount ); + } + } + + /** + * Discontinue tracking the item. + * + * @param item Item to be untracked. + * @param related Action related object. + */ + void untrack(final S item, final R related) { + final T object; + int trackingCount; + synchronized (this) { + if (initial.remove(item)) { /* + * if this item is already in the list + * of initial references to process + */ + if (DEBUG) { + System.out.println("AbstractTracked.untrack[removed from initial]: " + item); //$NON-NLS-1$ + } + return; /* + * we have removed it from the list and it will not be + * processed + */ + } + + if (adding.remove(item)) { /* + * if the item is in the process of + * being added + */ + if (DEBUG) { + System.out.println("AbstractTracked.untrack[being added]: " + item); //$NON-NLS-1$ + } + return; /* + * in case the item is untracked while in the process of + * adding + */ + } + object = tracked.remove(item); /* * must remove from tracker before + * calling customizer callback + */ + if (object == null) { /* are we actually tracking the item */ + return; + } + trackingCount = modified(); /* increment modification count */ + } + if (DEBUG) { + System.out.println("AbstractTracked.untrack[removed]: " + item); //$NON-NLS-1$ + } + /* Call customizer outside of synchronized region */ + customizerRemoved(item, related, object, trackingCount ); + /* + * If the customizer throws an unchecked exception, it is safe to let it + * propagate + */ + } + + /** + * Returns the number of tracked items. + * + * @return The number of tracked items. + * + * @GuardedBy this + */ + int size() { + return tracked.size(); + } + + /** + * Returns if the tracker is empty. + * + * @return Whether the tracker is empty. + * + * @GuardedBy this + * @since 1.5 + */ + boolean isEmpty() { + return tracked.isEmpty(); + } + + /** + * Return the customized object for the specified item + * + * @param item The item to lookup in the map + * @return The customized object for the specified item. + * + * @GuardedBy this + */ + T getCustomizedObject(final S item) { + return tracked.get( item ); + } + + /** + * Copy the tracked items into an array. + * + * @param list An array to contain the tracked items. + * @return The specified list if it is large enough to hold the tracked + * items or a new array large enough to hold the tracked items. + * @GuardedBy this + */ + S[] copyKeys(final S[] list) { + return tracked.keySet().toArray(list); + } + + /** + * Increment the modification count. If this method is overridden, the + * overriding method MUST call this method to increment the tracking count. + * + * @GuardedBy this + */ + int modified() { + return trackingCount.incrementAndGet(); + } + + /** + * Returns the tracking count for this {@code ServiceTracker} object. + * + * The tracking count is initialized to 0 when this object is opened. Every + * time an item is added, modified or removed from this object the tracking + * count is incremented. + * + * @GuardedBy this + * @return The tracking count for this object. + */ + int getTrackingCount() { + return trackingCount.get(); + } + + /** + * Copy the tracked items and associated values into the specified map. + * + * @param <M> Type of {@code Map} to hold the tracked items and associated + * values. + * @param map The map into which to copy the tracked items and associated + * values. This map must not be a user provided map so that user code + * is not executed while synchronized on this. + * @return The specified map. + * @GuardedBy this + * @since 1.5 + */ + <M extends Map<? super S, ? super T>> M copyEntries(final M map) { + map.putAll(tracked); + return map; + } + + /** + * Call the specific customizer adding method. This method must not be + * called while synchronized on this object. + * + * + * + * @param item Item to be tracked. + * @param related Action related object. + * @return Customized object for the tracked item or {@code null} if the + * item is not to be tracked. + */ + abstract T customizerAdding( final S item, final R related ); + + abstract void customizerAdded( final S item, final R related, final T object, int trackingCount, int serviceCount ); + + /** + * Call the specific customizer modified method. This method must not be + * called while synchronized on this object. + * + * @param item Tracked item. + * @param related Action related object. + * @param object Customized object for the tracked item. + * @param trackingCount + */ + abstract void customizerModified( final S item, final R related, final T object, int trackingCount ); + + /** + * Call the specific customizer removed method. This method must not be + * called while synchronized on this object. + * + * @param item Tracked item. + * @param related Action related object. + * @param object Customized object for the tracked item. + * @param trackingCount + */ + abstract void customizerRemoved( final S item, final R related, final T object, int trackingCount ); + } + + + /** + * Inner class which subclasses AbstractTracked. This class is the + * {@code ServiceListener} object for the tracker. + * + * @ThreadSafe + */ + private class Tracked extends AbstractTracked<ServiceReference<S>, T, ServiceEvent> implements ServiceListener { + /** + * Tracked constructor. + * @param trackingCount + */ + Tracked( AtomicInteger trackingCount ) { + super( trackingCount ); + } + + /** + * {@code ServiceListener} method for the {@code ServiceTracker} class. + * This method must NOT be synchronized to avoid deadlock potential. + * + * @param event {@code ServiceEvent} object from the framework. + */ + final public void serviceChanged(final ServiceEvent event) { + /* + * Check if we had a delayed call (which could happen when we + * close). + */ + if (closed) { + return; + } + final ServiceReference<S> reference = (ServiceReference<S>) event.getServiceReference(); + if (DEBUG) { + System.out.println("ServiceTracker.Tracked.serviceChanged[" + event.getType() + "]: " + reference); + } + + switch (event.getType()) { + case ServiceEvent.REGISTERED : + case ServiceEvent.MODIFIED : + track(reference, event); + /* + * If the customizer throws an unchecked exception, it is + * safe to let it propagate + */ + break; + case ServiceEvent.MODIFIED_ENDMATCH : + case ServiceEvent.UNREGISTERING : + untrack(reference, event); + /* + * If the customizer throws an unchecked exception, it is + * safe to let it propagate + */ + break; + default : + System.out.println("Unrecognized event type: ServiceTracker.Tracked.serviceChanged[" + event.getType() + "]: " + reference); + + } + } + + /** + * Increment the tracking count and tell the tracker there was a + * modification. + * + * @GuardedBy this + */ + final int modified() { + int trackingCount = super.modified(); /* increment the modification count */ + ServiceTracker.this.modified(); + return trackingCount; + } + + /** + * Call the specific customizer adding method. This method must not be + * called while synchronized on this object. + * + * + * + * @param item Item to be tracked. + * @param related Action related object. + * @return Customized object for the tracked item or {@code null} if the + * item is not to be tracked. + */ + final T customizerAdding( final ServiceReference<S> item, final ServiceEvent related ) { + return customizer.addingService( item ); + } + + final void customizerAdded( final ServiceReference<S> item, final ServiceEvent related, final T object, int trackingCount, int serviceCount ) { + customizer.addedService( item, object, trackingCount, serviceCount ); + } + + /** + * Call the specific customizer modified method. This method must not be + * called while synchronized on this object. + * + * @param item Tracked item. + * @param related Action related object. + * @param object Customized object for the tracked item. + * @param trackingCount + */ + final void customizerModified( final ServiceReference<S> item, final ServiceEvent related, final T object, int trackingCount ) { + customizer.modifiedService( item, object, trackingCount ); + } + + /** + * Call the specific customizer removed method. This method must not be + * called while synchronized on this object. + * + * @param item Tracked item. + * @param related Action related object. + * @param object Customized object for the tracked item. + * @param trackingCount + */ + final void customizerRemoved( final ServiceReference<S> item, final ServiceEvent related, final T object, int trackingCount ) { + customizer.removedService(item, object, trackingCount ); + } + } + + /** + * Subclass of Tracked which implements the AllServiceListener interface. + * This class is used by the ServiceTracker if open is called with true. + * + * @since 1.3 + * @ThreadSafe + */ + private class AllTracked extends Tracked implements AllServiceListener { + /** + * AllTracked constructor. + * @param trackingCount + */ + AllTracked( AtomicInteger trackingCount ) { + super( trackingCount ); + } + } +}
Added: felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/manager/ServiceTrackerCustomizer.java URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/manager/ServiceTrackerCustomizer.java?rev=1689973&view=auto ============================================================================== --- felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/manager/ServiceTrackerCustomizer.java (added) +++ felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/manager/ServiceTrackerCustomizer.java Wed Jul 8 22:10:14 2015 @@ -0,0 +1,100 @@ +/* + * Copyright (c) OSGi Alliance (2000, 2012). All Rights Reserved. + * + * Licensed 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.felix.scr.impl.manager; + +import org.osgi.framework.ServiceReference; + +/** + * The {@code ServiceTrackerCustomizer} interface allows a + * {@code ServiceTracker} to customize the service objects that are tracked. A + * {@code ServiceTrackerCustomizer} is called when a service is being added to a + * {@code ServiceTracker}. The {@code ServiceTrackerCustomizer} can then return + * an object for the tracked service. A {@code ServiceTrackerCustomizer} is also + * called when a tracked service is modified or has been removed from a + * {@code ServiceTracker}. + * + * <p> + * The methods in this interface may be called as the result of a + * {@code ServiceEvent} being received by a {@code ServiceTracker}. Since + * {@code ServiceEvent}s are synchronously delivered by the Framework, it is + * highly recommended that implementations of these methods do not register ( + * {@code BundleContext.registerService}), modify ( + * {@code ServiceRegistration.setProperties}) or unregister ( + * {@code ServiceRegistration.unregister}) a service while being synchronized on + * any object. + * + * <p> + * The {@code ServiceTracker} class is thread-safe. It does not call a + * {@code ServiceTrackerCustomizer} while holding any locks. + * {@code ServiceTrackerCustomizer} implementations must also be thread-safe. + * + * @param <S> The type of the service being tracked. + * @param <T> The type of the tracked object. + * @ThreadSafe + * @version $Id: c14b8d47026b6bd4ba1f2db7bf7e755d00fc6f6a $ + */ +public interface ServiceTrackerCustomizer<S, T> { + /** + * A service is being added to the {@code ServiceTracker}. + * + * <p> + * This method is called before a service which matched the search + * parameters of the {@code ServiceTracker} is added to the + * {@code ServiceTracker}. This method should return the service object to + * be tracked for the specified {@code ServiceReference}. The returned + * service object is stored in the {@code ServiceTracker} and is available + * from the {@code getService} and {@code getServices} methods. + * + * + * + * @param reference The reference to the service being added to the + * {@code ServiceTracker}. + * @return The service object to be tracked for the specified referenced + * service or {@code null} if the specified referenced service + * should not be tracked. + */ + public T addingService( ServiceReference<S> reference ); + + public void addedService( ServiceReference<S> reference, T service, int trackingCount, int serviceCount ); + + /** + * A service tracked by the {@code ServiceTracker} has been modified. + * + * <p> + * This method is called when a service being tracked by the + * {@code ServiceTracker} has had it properties modified. + * + * @param reference The reference to the service that has been modified. + * @param service The service object for the specified referenced service. + * @param trackingCount + */ + public void modifiedService( ServiceReference<S> reference, T service, int trackingCount ); + + /** + * A service tracked by the {@code ServiceTracker} has been removed. + * + * <p> + * This method is called after a service is no longer being tracked by the + * {@code ServiceTracker}. + * + * @param reference The reference to the service that has been removed. + * @param service The service object for the specified referenced service. + * @param trackingCount + */ + public void removedService( ServiceReference<S> reference, T service, int trackingCount ); + +}
