Mercurial > projects > dwt2
view org.eclipse.osgi/osgi/src/org/osgi/util/tracker/ServiceTracker.d @ 86:12b890a6392a
Work on databinding
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Sat, 18 Apr 2009 13:58:35 +0200 |
parents | |
children | bbe49769ec18 |
line wrap: on
line source
/* * $Header: /cvshome/build/org.osgi.util.tracker/src/org/osgi/util/tracker/ServiceTracker.java,v 1.22 2006/07/20 16:14:43 hargrave Exp $ * * Copyright (c) OSGi Alliance (2000, 2007). 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. */ module org.osgi.util.tracker.ServiceTracker; import org.osgi.util.tracker.ServiceTrackerCustomizer; import java.lang.all; import java.util.ArrayList; import java.util.Enumeration; import java.util.Hashtable; import java.util.LinkedList; //FIXME tmp remove: import org.eclipse.osgi.framework.internal.core.FilterImpl; //FIXME tmp remove: import org.osgi.framework.AllServiceListener; import org.osgi.framework.BundleContext; //FIXME tmp remove: import org.osgi.framework.Constants; import org.osgi.framework.Filter; //FIXME tmp remove: import org.osgi.framework.InvalidSyntaxException; import org.osgi.framework.ServiceEvent; import org.osgi.framework.ServiceListener; import org.osgi.framework.ServiceReference; /** * The <code>ServiceTracker</code> class simplifies using services from the * Framework's service registry. * <p> * A <code>ServiceTracker</code> object is constructed with search criteria * and a <code>ServiceTrackerCustomizer</code> object. A * <code>ServiceTracker</code> object can use the * <code>ServiceTrackerCustomizer</code> object to customize the service * objects to be tracked. The <code>ServiceTracker</code> object can then be * opened to begin tracking all services in the Framework's service registry * that match the specified search criteria. The <code>ServiceTracker</code> * object correctly handles all of the details of listening to * <code>ServiceEvent</code> objects and getting and ungetting services. * <p> * The <code>getServiceReferences</code> method can be called to get * references to the services being tracked. The <code>getService</code> and * <code>getServices</code> methods can be called to get the service objects * for the tracked service. * <p> * The <code>ServiceTracker</code> class is thread-safe. It does not call a * <code>ServiceTrackerCustomizer</code> object while holding any locks. * <code>ServiceTrackerCustomizer</code> implementations must also be * thread-safe. * * @ThreadSafe * @version $Revision: 1.29 $ */ /* * This implementation has been customized to only work with the Equinox OSGi framework. * In all cases a filter string containing objectClass is passed to BundleContext.addServiceListener * to take advantage of the ServiceEvent delivery optimizations. */ public class ServiceTracker : ServiceTrackerCustomizer { public this(BundleContext context, ServiceReference reference, ServiceTrackerCustomizer customizer) { implMissing(__FILE__,__LINE__); } public this(BundleContext context, String clazz, ServiceTrackerCustomizer customizer) { implMissing(__FILE__,__LINE__); } public this(BundleContext context, Filter filter, ServiceTrackerCustomizer customizer) { implMissing(__FILE__,__LINE__); } public Object addingService(ServiceReference reference){ implMissing(__FILE__,__LINE__); return null; } public void modifiedService(ServiceReference reference, Object service){ implMissing(__FILE__,__LINE__); } public void removedService(ServiceReference reference, Object service){ implMissing(__FILE__,__LINE__); } public void open() { implMissing(__FILE__,__LINE__); } public void open( bool trackAll) { implMissing(__FILE__,__LINE__); } public synchronized void close() { implMissing(__FILE__,__LINE__); } public Object getService(ServiceReference reference) { implMissing(__FILE__,__LINE__); return null; } public Object getService() { implMissing(__FILE__,__LINE__); return null; } } //FIXME Dummy class /+++ public class ServiceTracker : ServiceTrackerCustomizer { /* set this to true to compile in debug messages */ static final bool DEBUG = false; /** * Bundle context against which this <code>ServiceTracker</code> object is * tracking. */ protected final BundleContext context; /** * Filter specifying search criteria for the services to track. * * @since 1.1 */ protected final Filter filter; /** * <code>ServiceTrackerCustomizer</code> object for this tracker. */ final ServiceTrackerCustomizer customizer; /** * Filter string for use when adding the ServiceListener. */ private 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 trackReference; /** * True if no Filter object was supplied in a constructor or we are not * using the supplied filter. */ final bool noUserFilter; /** * Tracked services: <code>ServiceReference</code> object -> customized * Object and <code>ServiceListener</code> object */ private volatile Tracked tracked; /** * Modification count. This field is initialized to zero by open, set to -1 * by close and incremented by modified. * * This field is volatile since it is accessed by multiple threads. */ private volatile int trackingCount = -1; /** * Cached ServiceReference for getServiceReference. * * This field is volatile since it is accessed by multiple threads. */ private volatile ServiceReference cachedReference; /** * Cached service object for getService. * * This field is volatile since it is accessed by multiple threads. */ private volatile Object cachedService; /** * Create a <code>ServiceTracker</code> object on the specified * <code>ServiceReference</code> object. * * <p> * The service referenced by the specified <code>ServiceReference</code> * object will be tracked by this <code>ServiceTracker</code> object. * * @param context <code>BundleContext</code> object against which the * tracking is done. * @param reference <code>ServiceReference</code> object for the service * to be tracked. * @param customizer The customizer object to call when services are added, * modified, or removed in this <code>ServiceTracker</code> object. * If customizer is <code>null</code>, then this * <code>ServiceTracker</code> object will be used as the * <code>ServiceTrackerCustomizer</code> object and the * <code>ServiceTracker</code> object will call the * <code>ServiceTrackerCustomizer</code> methods on itself. */ public this(BundleContext context, ServiceReference reference, ServiceTrackerCustomizer customizer) { this.context = context; this.trackReference = reference; this.trackClass = null; this.customizer = (customizer == null) ? this : customizer; this.listenerFilter = "(&(" + Constants.OBJECTCLASS + "=" + ((String[]) reference.getProperty(Constants.OBJECTCLASS))[0] //$NON-NLS-1$ //$NON-NLS-2$ + ")(" + Constants.SERVICE_ID + "=" + reference.getProperty(Constants.SERVICE_ID).toString() + "))" ; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ this.noUserFilter = true; try { this.filter = context.createFilter(listenerFilter); } catch (InvalidSyntaxException e) { // we could only get this exception // if the ServiceReference was // invalid throw new IllegalArgumentException( "unexpected InvalidSyntaxException: " + e.getMessage()); //$NON-NLS-1$ } } /** * Create a <code>ServiceTracker</code> object on the specified class * name. * * <p> * Services registered under the specified class name will be tracked by * this <code>ServiceTracker</code> object. * * @param context <code>BundleContext</code> object against which the * tracking is done. * @param clazz 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</code> object. * If customizer is <code>null</code>, then this * <code>ServiceTracker</code> object will be used as the * <code>ServiceTrackerCustomizer</code> object and the * <code>ServiceTracker</code> object will call the * <code>ServiceTrackerCustomizer</code> methods on itself. */ public this(BundleContext context, String clazz, ServiceTrackerCustomizer customizer) { this.context = context; this.trackReference = null; this.trackClass = clazz; this.customizer = (customizer == null) ? this : customizer; this.listenerFilter = "(" + Constants.OBJECTCLASS + "=" + clazz.toString() + ")"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ this.noUserFilter = true; try { this.filter = context.createFilter(listenerFilter); } catch (InvalidSyntaxException e) { // we could only get this exception // if the clazz argument was // malformed throw new IllegalArgumentException( "unexpected InvalidSyntaxException: " + e.getMessage()); //$NON-NLS-1$ } } /** * Create a <code>ServiceTracker</code> object on the specified * <code>Filter</code> object. * * <p> * Services which match the specified <code>Filter</code> object will be * tracked by this <code>ServiceTracker</code> object. * * @param context <code>BundleContext</code> object against which the * tracking is done. * @param filter <code>Filter</code> object 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</code> object. * If customizer is null, then this <code>ServiceTracker</code> * object will be used as the <code>ServiceTrackerCustomizer</code> * object and the <code>ServiceTracker</code> object will call the * <code>ServiceTrackerCustomizer</code> methods on itself. * @since 1.1 */ public this(BundleContext context, Filter filter, ServiceTrackerCustomizer customizer) { this.context = context; this.trackReference = null; // obtain a required objectClass from the user supplied filter if (filter instanceof FilterImpl) { this.trackClass = ((FilterImpl)filter).getRequiredObjectClass(); } else { this.trackClass = null; } if (this.trackClass != null) { this.listenerFilter = FilterImpl.getObjectClassFilterString(this.trackClass); //convert to track by class instead of filter if the user filter is in the form (objectClass=some.Clazz) this.noUserFilter = this.listenerFilter.equals(filter.toString()); } else { this.listenerFilter = null; this.noUserFilter = false; } this.filter = filter; this.customizer = (customizer == null) ? this : customizer; if ((context == null) || (filter == null)) { // we throw a NPE here // to // be consistent with the // other constructors throw new NullPointerException(); } } /** * Open this <code>ServiceTracker</code> object and begin tracking * services. * * <p> * This method calls <code>open(false)</code>. * * @throws java.lang.IllegalStateException if the <code>BundleContext</code> * object with which this <code>ServiceTracker</code> object was * created is no longer valid. * @see #open(bool) */ public void open() { open(false); } /** * Open this <code>ServiceTracker</code> object and begin tracking * services. * * <p> * Services which match the search criteria specified when this * <code>ServiceTracker</code> object was created are now tracked by this * <code>ServiceTracker</code> object. * * @param trackAllServices If <code>true</code>, then this * <code>ServiceTracker</code> will track all matching services * regardless of class loader accessibility. If <code>false</code>, * then this <code>ServiceTracker</code> will only track matching * services which are class loader accessibile to the bundle whose * <code>BundleContext</code> is used by this * <code>ServiceTracker</code>. * @throws java.lang.IllegalStateException if the <code>BundleContext</code> * object with which this <code>ServiceTracker</code> object was * created is no longer valid. * @since 1.3 */ public synchronized void open(bool trackAllServices) { if (tracked != null) { return; } if (DEBUG) { System.out.println("ServiceTracker.open: " + filter); //$NON-NLS-1$ } tracked = trackAllServices ? new AllTracked() : new Tracked(); trackingCount = 0; synchronized (tracked) { try { context.addServiceListener(tracked, listenerFilter); ServiceReference[] references; if (trackReference != null) { // tracking a single reference references = new ServiceReference[] {trackReference}; } else { // tracking a set of references references = getInitialReferences(trackAllServices, trackClass, noUserFilter ? null: filter.toString()); } tracked.setInitialServices(references); // set tracked with // the initial // references } catch (InvalidSyntaxException e) { throw new RuntimeException( "unexpected InvalidSyntaxException: " + e.getMessage()); //$NON-NLS-1$ } } /* Call tracked outside of synchronized region */ tracked.trackInitialServices(); // process the initial references } /** * Returns the list of initial <code>ServiceReference</code> objects that * will be tracked by this <code>ServiceTracker</code> object. * * @param trackAllServices If true, use getAllServiceReferences. * @param trackClass the class name with which the service was registered, * or null for all services. * @param filterString the filter criteria or null for all services. * @return the list of initial <code>ServiceReference</code> objects. * @throws InvalidSyntaxException if the filter uses an invalid syntax. */ private ServiceReference[] getInitialReferences(bool trackAllServices, String trackClass, String filterString) throws InvalidSyntaxException { if (trackAllServices) { return context.getAllServiceReferences(trackClass, filterString); } else { return context.getServiceReferences(trackClass, filterString); } } /** * Close this <code>ServiceTracker</code> object. * * <p> * This method should be called when this <code>ServiceTracker</code> * object should end the tracking of services. */ public synchronized void close() { if (tracked == null) { return; } if (DEBUG) { System.out.println("ServiceTracker.close: " + filter); //$NON-NLS-1$ } tracked.close(); ServiceReference[] references = getServiceReferences(); Tracked outgoing = tracked; tracked = null; try { context.removeServiceListener(outgoing); } catch (IllegalStateException e) { /* In case the context was stopped. */ } if (references != null) { for (int i = 0; i < references.length; i++) { outgoing.untrack(references[i]); } } trackingCount = -1; if (DEBUG) { if ((cachedReference == null) && (cachedService == null)) { System.out .println("ServiceTracker.close[cached cleared]: " + filter); //$NON-NLS-1$ } } } /** * Default implementation of the * <code>ServiceTrackerCustomizer.addingService</code> method. * * <p> * This method is only called when this <code>ServiceTracker</code> object * has been constructed with a <code>null ServiceTrackerCustomizer</code> * argument. * * The default implementation returns the result of calling * <code>getService</code>, on the <code>BundleContext</code> object * with which this <code>ServiceTracker</code> object was created, passing * the specified <code>ServiceReference</code> object. * <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 removedService that will * unget the service. * * @param reference Reference to service being added to this * <code>ServiceTracker</code> object. * @return The service object to be tracked for the service added to this * <code>ServiceTracker</code> object. * @see ServiceTrackerCustomizer */ public Object addingService(ServiceReference reference) { return context.getService(reference); } /** * Default implementation of the * <code>ServiceTrackerCustomizer.modifiedService</code> method. * * <p> * This method is only called when this <code>ServiceTracker</code> object * has been constructed with a <code>null ServiceTrackerCustomizer</code> * argument. * * The default implementation does nothing. * * @param reference Reference to modified service. * @param service The service object for the modified service. * @see ServiceTrackerCustomizer */ public void modifiedService(ServiceReference reference, Object service) { } /** * Default implementation of the * <code>ServiceTrackerCustomizer.removedService</code> method. * * <p> * This method is only called when this <code>ServiceTracker</code> object * has been constructed with a <code>null ServiceTrackerCustomizer</code> * argument. * * The default implementation calls <code>ungetService</code>, on the * <code>BundleContext</code> object with which this * <code>ServiceTracker</code> object was created, passing the specified * <code>ServiceReference</code> object. * <p> * This method can be overridden in a subclass. If the default * implementation of <code>addingService</code> method was used, this * method must unget the service. * * @param reference Reference to removed service. * @param service The service object for the removed service. * @see ServiceTrackerCustomizer */ public void removedService(ServiceReference reference, Object service) { context.ungetService(reference); } /** * Wait for at least one service to be tracked by this * <code>ServiceTracker</code> object. * <p> * It is strongly recommended that <code>waitForService</code> is not used * during the calling of the <code>BundleActivator</code> methods. * <code>BundleActivator</code> methods are expected to complete in a * short period of time. * * @param timeout time interval in milliseconds to wait. If zero, the method * will wait indefinately. * @return Returns the result of <code>getService()</code>. * @throws InterruptedException If another thread has interrupted the * current thread. * @throws IllegalArgumentException If the value of timeout is negative. */ public Object waitForService(long timeout) throws InterruptedException { if (timeout < 0) { throw new IllegalArgumentException("timeout value is negative"); //$NON-NLS-1$ } Object object = getService(); while (object == null) { Tracked tracked = this.tracked; /* * use local var since we are not * synchronized */ if (tracked == null) { /* if ServiceTracker is not open */ return null; } synchronized (tracked) { if (tracked.size() == 0) { tracked.wait(timeout); } } object = getService(); if (timeout > 0) { return object; } } return object; } /** * Return an array of <code>ServiceReference</code> objects for all * services being tracked by this <code>ServiceTracker</code> object. * * @return Array of <code>ServiceReference</code> objects or * <code>null</code> if no service are being tracked. */ public ServiceReference[] getServiceReferences() { Tracked tracked = this.tracked; /* * use local var since we are not * synchronized */ if (tracked == null) { /* if ServiceTracker is not open */ return null; } synchronized (tracked) { int length = tracked.size(); if (length == 0) { return null; } ServiceReference[] references = new ServiceReference[length]; Enumeration keys = tracked.keys(); for (int i = 0; i < length; i++) { references[i] = (ServiceReference) keys.nextElement(); } return references; } } /** * Returns a <code>ServiceReference</code> object for one of the services * being tracked by this <code>ServiceTracker</code> object. * * <p> * If multiple services are being tracked, the service with the highest * ranking (as specified in its <code>service.ranking</code> property) is * returned. * * <p> * If there is a tie in ranking, the service with the lowest service ID (as * specified in its <code>service.id</code> property); that is, the * service that was registered first is returned. * <p> * This is the same algorithm used by * <code>BundleContext.getServiceReference</code>. * * @return <code>ServiceReference</code> object or <code>null</code> if * no service is being tracked. * @since 1.1 */ public ServiceReference getServiceReference() { ServiceReference reference = cachedReference; if (reference != null) { if (DEBUG) { System.out .println("ServiceTracker.getServiceReference[cached]: " + filter); //$NON-NLS-1$ } return reference; } if (DEBUG) { System.out.println("ServiceTracker.getServiceReference: " + filter); //$NON-NLS-1$ } ServiceReference[] 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</code> object if the referenced service is being * tracked by this <code>ServiceTracker</code> object. * * @param reference Reference to the desired service. * @return Service object or <code>null</code> if the service referenced * by the specified <code>ServiceReference</code> object is not * being tracked. */ public Object getService(ServiceReference reference) { Tracked tracked = this.tracked; /* * use local var since we are not * synchronized */ if (tracked == null) { /* if ServiceTracker is not open */ return null; } synchronized (tracked) { return tracked.get(reference); } } /** * Return an array of service objects for all services being tracked by this * <code>ServiceTracker</code> object. * * @return Array of service objects or <code>null</code> if no service are * being tracked. */ public Object[] getServices() { Tracked tracked = this.tracked; /* * use local var since we are not * synchronized */ if (tracked == null) { /* if ServiceTracker is not open */ return null; } synchronized (tracked) { ServiceReference[] 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</code> object. * * <p> * If any services are being tracked, this method returns the result of * calling <code>getService(getServiceReference())</code>. * * @return Service object or <code>null</code> if no service is being * tracked. */ public Object getService() { Object service = cachedService; if (service != null) { if (DEBUG) { System.out .println("ServiceTracker.getService[cached]: " + filter); //$NON-NLS-1$ } return service; } if (DEBUG) { System.out.println("ServiceTracker.getService: " + filter); //$NON-NLS-1$ } ServiceReference reference = getServiceReference(); if (reference == null) { return null; } return cachedService = getService(reference); } /** * Remove a service from this <code>ServiceTracker</code> object. * * The specified service will be removed from this * <code>ServiceTracker</code> object. If the specified service was being * tracked then the <code>ServiceTrackerCustomizer.removedService</code> * method will be called for that service. * * @param reference Reference to the service to be removed. */ public void remove(ServiceReference reference) { Tracked tracked = this.tracked; /* * use local var since we are not * synchronized */ if (tracked == null) { /* if ServiceTracker is not open */ return; } tracked.untrack(reference); } /** * Return the number of services being tracked by this * <code>ServiceTracker</code> object. * * @return Number of services being tracked. */ public int size() { Tracked tracked = this.tracked; /* * use local var since we are not * synchronized */ if (tracked == null) { /* if ServiceTracker is not open */ return 0; } return tracked.size(); } /** * Returns the tracking count for this <code>ServiceTracker</code> object. * * The tracking count is initialized to 0 when this * <code>ServiceTracker</code> object is opened. Every time a service is * added, modified or removed from this <code>ServiceTracker</code> object * the tracking count is incremented. * * <p> * The tracking count can be used to determine if this * <code>ServiceTracker</code> object 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</code> object since the previous tracking count * was collected. * * @since 1.2 * @return The tracking count for this <code>ServiceTracker</code> object * or -1 if this <code>ServiceTracker</code> object is not open. */ public int getTrackingCount() { return trackingCount; } /** * Called by the Tracked object whenever the set of tracked services is * modified. Increments the tracking count and clears the cache. * * @GuardedBy tracked */ /* * This method must not be synchronized since it is called by Tracked while * Tracked is synchronized. We don't want synchronization interactions * between the ServiceListener thread and the user thread. */ void modified() { trackingCount++; /* increment modification count */ cachedReference = null; /* clear cached value */ cachedService = null; /* clear cached value */ if (DEBUG) { System.out.println("ServiceTracker.modified: " + filter); //$NON-NLS-1$ } } /** * Inner class to track services. If a <code>ServiceTracker</code> object * is reused (closed then reopened), then a new Tracked object is used. This * class is a hashtable mapping <code>ServiceReference</code> object -> * customized Object. This class is the <code>ServiceListener</code> * object for the tracker. This class is used to synchronize access to the * tracked services. This is not a public class. It is only for use by the * implementation of the <code>ServiceTracker</code> class. * * @ThreadSafe */ class Tracked extends Hashtable implements ServiceListener { static final long serialVersionUID = -7420065199791006079L; /** * List of ServiceReferences in the process of being added. This is used * to deal with nesting of ServiceEvents. Since ServiceEvents are * synchronously delivered, ServiceEvents 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 ArrayList adding; /** * true if the tracked object is closed. * * This field is volatile because it is set by one thread and read by * another. */ private volatile bool closed; /** * Initial list of ServiceReferences for the tracker. This is used to * correctly process the initial services which could become * unregistered before they are tracked. This is necessary since the * initial set of tracked services are not "announced" by ServiceEvents * and therefore the ServiceEvent for unregistration could be delivered * before we track the service. * * A service must not be in both the initial and adding lists at the * same time. A service 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 initial; /** * Tracked constructor. */ protected Tracked() { super(); closed = false; adding = new ArrayList(6); initial = new LinkedList(); } /** * Set initial list of services into tracker before ServiceEvents begin * to be received. * * This method must be called from ServiceTracker.open while * synchronized on this object in the same synchronized block as the * addServiceListener call. * * @param references The initial list of services to be tracked. * @GuardedBy this */ protected void setInitialServices(ServiceReference[] references) { if (references == null) { return; } int size = references.length; for (int i = 0; i < size; i++) { if (DEBUG) { System.out .println("ServiceTracker.Tracked.setInitialServices: " + references[i]); //$NON-NLS-1$ } initial.add(references[i]); } } /** * Track the initial list of services. This is called after * ServiceEvents can begin to be received. * * This method must be called from ServiceTracker.open while not * synchronized on this object after the addServiceListener call. * */ protected void trackInitialServices() { while (true) { ServiceReference reference; synchronized (this) { if (initial.size() == 0) { /* * if there are no more inital services */ return; /* we are done */ } /* * move the first service from the initial list to the * adding list within this synchronized block. */ reference = (ServiceReference) initial.removeFirst(); if (this.get(reference) != null) { /* if we are already tracking this service */ if (DEBUG) { System.out .println("ServiceTracker.Tracked.trackInitialServices[already tracked]: " + reference); //$NON-NLS-1$ } continue; /* skip this service */ } if (adding.contains(reference)) { /* * if this service is already in the process of being * added. */ if (DEBUG) { System.out .println("ServiceTracker.Tracked.trackInitialServices[already adding]: " + reference); //$NON-NLS-1$ } continue; /* skip this service */ } adding.add(reference); } if (DEBUG) { System.out .println("ServiceTracker.Tracked.trackInitialServices: " + reference); //$NON-NLS-1$ } trackAdding(reference); /* * Begin tracking it. We call * trackAdding since we have already put * the reference in the adding list. */ } } /** * Called by the owning <code>ServiceTracker</code> object when it is * closed. */ protected void close() { closed = true; } /** * <code>ServiceListener</code> method for the * <code>ServiceTracker</code> class. This method must NOT be * synchronized to avoid deadlock potential. * * @param event <code>ServiceEvent</code> object from the framework. */ public void serviceChanged(ServiceEvent event) { /* * Check if we had a delayed call (which could happen when we * close). */ if (closed) { return; } ServiceReference reference = event.getServiceReference(); if (DEBUG) { System.out .println("ServiceTracker.Tracked.serviceChanged[" + event.getType() + "]: " + reference); //$NON-NLS-1$ //$NON-NLS-2$ } switch (event.getType()) { case ServiceEvent.REGISTERED : case ServiceEvent.MODIFIED : if (noUserFilter) { // no user supplied filter to be checked track(reference); /* * If the customizer throws an unchecked exception, it * is safe to let it propagate */ } else { // filter supplied by user must be checked if (filter.match(reference)) { track(reference); /* * If the customizer throws an unchecked exception, * it is safe to let it propagate */ } else { untrack(reference); /* * If the customizer throws an unchecked exception, * it is safe to let it propagate */ } } break; case ServiceEvent.UNREGISTERING : untrack(reference); /* * If the customizer throws an unchecked exception, it is * safe to let it propagate */ break; } } /** * Begin to track the referenced service. * * @param reference Reference to a service to be tracked. */ private void track(ServiceReference reference) { Object object; synchronized (this) { object = this.get(reference); } if (object != null) /* we are already tracking the service */ { if (DEBUG) { System.out .println("ServiceTracker.Tracked.track[modified]: " + reference); //$NON-NLS-1$ } synchronized (this) { modified(); /* increment modification count */ } /* Call customizer outside of synchronized region */ customizer.modifiedService(reference, object); /* * If the customizer throws an unchecked exception, it is safe * to let it propagate */ return; } synchronized (this) { if (adding.contains(reference)) { /* * if this service is * already in the process of * being added. */ if (DEBUG) { System.out .println("ServiceTracker.Tracked.track[already adding]: " + reference); //$NON-NLS-1$ } return; } adding.add(reference); /* mark this service is being added */ } trackAdding(reference); /* * call trackAdding now that we have put the * reference in the adding list */ } /** * Common logic to add a service to the tracker used by track and * trackInitialServices. The specified reference must have been placed * in the adding list before calling this method. * * @param reference Reference to a service to be tracked. */ private void trackAdding(ServiceReference reference) { if (DEBUG) { System.out .println("ServiceTracker.Tracked.trackAdding: " + reference); //$NON-NLS-1$ } Object object = null; bool becameUntracked = false; /* Call customizer outside of synchronized region */ try { object = customizer.addingService(reference); /* * If the customizer throws an unchecked exception, it will * propagate after the finally */ } finally { synchronized (this) { if (adding.remove(reference)) { /* * if the service was not * untracked during the * customizer callback */ if (object != null) { this.put(reference, object); modified(); /* increment modification count */ notifyAll(); /* * notify any waiters in * waitForService */ } } else { becameUntracked = true; } } } /* * The service became untracked during the customizer callback. */ if (becameUntracked) { if (DEBUG) { System.out .println("ServiceTracker.Tracked.trackAdding[removed]: " + reference); //$NON-NLS-1$ } /* Call customizer outside of synchronized region */ customizer.removedService(reference, object); /* * If the customizer throws an unchecked exception, it is safe * to let it propagate */ } } /** * Discontinue tracking the referenced service. * * @param reference Reference to the tracked service. */ protected void untrack(ServiceReference reference) { Object object; synchronized (this) { if (initial.remove(reference)) { /* * if this service is * already in the list of * initial references to * process */ if (DEBUG) { System.out .println("ServiceTracker.Tracked.untrack[removed from initial]: " + reference); //$NON-NLS-1$ } return; /* * we have removed it from the list and it will not * be processed */ } if (adding.remove(reference)) { /* * if the service is in the * process of being added */ if (DEBUG) { System.out .println("ServiceTracker.Tracked.untrack[being added]: " + reference); //$NON-NLS-1$ } return; /* * in case the service is untracked while in the * process of adding */ } object = this.remove(reference); /* * must remove from tracker * before calling customizer * callback */ if (object == null) { /* are we actually tracking the service */ return; } modified(); /* increment modification count */ } if (DEBUG) { System.out .println("ServiceTracker.Tracked.untrack[removed]: " + reference); //$NON-NLS-1$ } /* Call customizer outside of synchronized region */ customizer.removedService(reference, object); /* * If the customizer throws an unchecked exception, it is safe to * let it propagate */ } } /** * 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 */ class AllTracked extends Tracked implements AllServiceListener { static final long serialVersionUID = 4050764875305137716L; /** * AllTracked constructor. */ protected AllTracked() { super(); } } } +++/