KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > osgi > util > tracker > ServiceTracker


1 /*
2  * $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 $
3  *
4  * Copyright (c) OSGi Alliance (2000, 2007). All Rights Reserved.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */

18
19 package org.osgi.util.tracker;
20
21 import java.util.ArrayList JavaDoc;
22 import java.util.Enumeration JavaDoc;
23 import java.util.Hashtable JavaDoc;
24 import java.util.LinkedList JavaDoc;
25
26 import org.eclipse.osgi.framework.internal.core.FilterImpl;
27 import org.osgi.framework.AllServiceListener;
28 import org.osgi.framework.BundleContext;
29 import org.osgi.framework.Constants;
30 import org.osgi.framework.Filter;
31 import org.osgi.framework.InvalidSyntaxException;
32 import org.osgi.framework.ServiceEvent;
33 import org.osgi.framework.ServiceListener;
34 import org.osgi.framework.ServiceReference;
35
36 /**
37  * The <code>ServiceTracker</code> class simplifies using services from the
38  * Framework's service registry.
39  * <p>
40  * A <code>ServiceTracker</code> object is constructed with search criteria
41  * and a <code>ServiceTrackerCustomizer</code> object. A
42  * <code>ServiceTracker</code> object can use the
43  * <code>ServiceTrackerCustomizer</code> object to customize the service
44  * objects to be tracked. The <code>ServiceTracker</code> object can then be
45  * opened to begin tracking all services in the Framework's service registry
46  * that match the specified search criteria. The <code>ServiceTracker</code>
47  * object correctly handles all of the details of listening to
48  * <code>ServiceEvent</code> objects and getting and ungetting services.
49  * <p>
50  * The <code>getServiceReferences</code> method can be called to get
51  * references to the services being tracked. The <code>getService</code> and
52  * <code>getServices</code> methods can be called to get the service objects
53  * for the tracked service.
54  * <p>
55  * The <code>ServiceTracker</code> class is thread-safe. It does not call a
56  * <code>ServiceTrackerCustomizer</code> object while holding any locks.
57  * <code>ServiceTrackerCustomizer</code> implementations must also be
58  * thread-safe.
59  *
60  * @ThreadSafe
61  * @version $Revision: 1.29 $
62  */

63
64 /*
65  * This implementation has been customized to only work with the Equinox OSGi framework.
66  * In all cases a filter string containing objectClass is passed to BundleContext.addServiceListener
67  * to take advantage of the ServiceEvent delivery optimizations.
68  */

69
70 public class ServiceTracker implements ServiceTrackerCustomizer {
71     /* set this to true to compile in debug messages */
72     static final boolean DEBUG = false;
73     /**
74      * Bundle context against which this <code>ServiceTracker</code> object is
75      * tracking.
76      */

77     protected final BundleContext context;
78     /**
79      * Filter specifying search criteria for the services to track.
80      *
81      * @since 1.1
82      */

83     protected final Filter filter;
84     /**
85      * <code>ServiceTrackerCustomizer</code> object for this tracker.
86      */

87     final ServiceTrackerCustomizer customizer;
88     /**
89      * Filter string for use when adding the ServiceListener.
90      */

91     private final String JavaDoc listenerFilter;
92     /**
93      * Class name to be tracked. If this field is set, then we are tracking by
94      * class name.
95      */

96     private final String JavaDoc trackClass;
97     /**
98      * Reference to be tracked. If this field is set, then we are tracking a
99      * single ServiceReference.
100      */

101     private final ServiceReference trackReference;
102     /**
103      * True if no Filter object was supplied in a constructor or we are not
104      * using the supplied filter.
105      */

106     final boolean noUserFilter;
107     /**
108      * Tracked services: <code>ServiceReference</code> object -> customized
109      * Object and <code>ServiceListener</code> object
110      */

111     private volatile Tracked tracked;
112     /**
113      * Modification count. This field is initialized to zero by open, set to -1
114      * by close and incremented by modified.
115      *
116      * This field is volatile since it is accessed by multiple threads.
117      */

118     private volatile int trackingCount = -1;
119     /**
120      * Cached ServiceReference for getServiceReference.
121      *
122      * This field is volatile since it is accessed by multiple threads.
123      */

124     private volatile ServiceReference cachedReference;
125     /**
126      * Cached service object for getService.
127      *
128      * This field is volatile since it is accessed by multiple threads.
129      */

130     private volatile Object JavaDoc cachedService;
131
132     /**
133      * Create a <code>ServiceTracker</code> object on the specified
134      * <code>ServiceReference</code> object.
135      *
136      * <p>
137      * The service referenced by the specified <code>ServiceReference</code>
138      * object will be tracked by this <code>ServiceTracker</code> object.
139      *
140      * @param context <code>BundleContext</code> object against which the
141      * tracking is done.
142      * @param reference <code>ServiceReference</code> object for the service
143      * to be tracked.
144      * @param customizer The customizer object to call when services are added,
145      * modified, or removed in this <code>ServiceTracker</code> object.
146      * If customizer is <code>null</code>, then this
147      * <code>ServiceTracker</code> object will be used as the
148      * <code>ServiceTrackerCustomizer</code> object and the
149      * <code>ServiceTracker</code> object will call the
150      * <code>ServiceTrackerCustomizer</code> methods on itself.
151      */

152     public ServiceTracker(BundleContext context, ServiceReference reference,
153             ServiceTrackerCustomizer customizer) {
154         this.context = context;
155         this.trackReference = reference;
156         this.trackClass = null;
157         this.customizer = (customizer == null) ? this : customizer;
158         this.listenerFilter = "(&(" + Constants.OBJECTCLASS + "=" + ((String JavaDoc[]) reference.getProperty(Constants.OBJECTCLASS))[0] //$NON-NLS-1$ //$NON-NLS-2$
159
+ ")(" + Constants.SERVICE_ID + "=" + reference.getProperty(Constants.SERVICE_ID).toString() + "))" ; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
160
this.noUserFilter = true;
161         try {
162             this.filter = context.createFilter(listenerFilter);
163         }
164         catch (InvalidSyntaxException e) { // we could only get this exception
165
// if the ServiceReference was
166
// invalid
167
throw new IllegalArgumentException JavaDoc(
168                     "unexpected InvalidSyntaxException: " + e.getMessage()); //$NON-NLS-1$
169
}
170     }
171
172     /**
173      * Create a <code>ServiceTracker</code> object on the specified class
174      * name.
175      *
176      * <p>
177      * Services registered under the specified class name will be tracked by
178      * this <code>ServiceTracker</code> object.
179      *
180      * @param context <code>BundleContext</code> object against which the
181      * tracking is done.
182      * @param clazz Class name of the services to be tracked.
183      * @param customizer The customizer object to call when services are added,
184      * modified, or removed in this <code>ServiceTracker</code> object.
185      * If customizer is <code>null</code>, then this
186      * <code>ServiceTracker</code> object will be used as the
187      * <code>ServiceTrackerCustomizer</code> object and the
188      * <code>ServiceTracker</code> object will call the
189      * <code>ServiceTrackerCustomizer</code> methods on itself.
190      */

191     public ServiceTracker(BundleContext context, String JavaDoc clazz,
192             ServiceTrackerCustomizer customizer) {
193         this.context = context;
194         this.trackReference = null;
195         this.trackClass = clazz;
196         this.customizer = (customizer == null) ? this : customizer;
197         this.listenerFilter = "(" + Constants.OBJECTCLASS + "=" + clazz.toString() + ")"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
198
this.noUserFilter = true;
199         try {
200             this.filter = context.createFilter(listenerFilter);
201         }
202         catch (InvalidSyntaxException e) { // we could only get this exception
203
// if the clazz argument was
204
// malformed
205
throw new IllegalArgumentException JavaDoc(
206                     "unexpected InvalidSyntaxException: " + e.getMessage()); //$NON-NLS-1$
207
}
208     }
209
210     /**
211      * Create a <code>ServiceTracker</code> object on the specified
212      * <code>Filter</code> object.
213      *
214      * <p>
215      * Services which match the specified <code>Filter</code> object will be
216      * tracked by this <code>ServiceTracker</code> object.
217      *
218      * @param context <code>BundleContext</code> object against which the
219      * tracking is done.
220      * @param filter <code>Filter</code> object to select the services to be
221      * tracked.
222      * @param customizer The customizer object to call when services are added,
223      * modified, or removed in this <code>ServiceTracker</code> object.
224      * If customizer is null, then this <code>ServiceTracker</code>
225      * object will be used as the <code>ServiceTrackerCustomizer</code>
226      * object and the <code>ServiceTracker</code> object will call the
227      * <code>ServiceTrackerCustomizer</code> methods on itself.
228      * @since 1.1
229      */

230     public ServiceTracker(BundleContext context, Filter filter,
231             ServiceTrackerCustomizer customizer) {
232         this.context = context;
233         this.trackReference = null;
234
235         // obtain a required objectClass from the user supplied filter
236
if (filter instanceof FilterImpl) {
237             this.trackClass = ((FilterImpl)filter).getRequiredObjectClass();
238         } else {
239             this.trackClass = null;
240         }
241         
242         if (this.trackClass != null) {
243             this.listenerFilter = FilterImpl.getObjectClassFilterString(this.trackClass);
244             //convert to track by class instead of filter if the user filter is in the form (objectClass=some.Clazz)
245
this.noUserFilter = this.listenerFilter.equals(filter.toString());
246         } else {
247             this.listenerFilter = null;
248             this.noUserFilter = false;
249         }
250
251         this.filter = filter;
252         this.customizer = (customizer == null) ? this : customizer;
253         if ((context == null) || (filter == null)) { // we throw a NPE here
254
// to
255
// be consistent with the
256
// other constructors
257
throw new NullPointerException JavaDoc();
258         }
259     }
260
261     /**
262      * Open this <code>ServiceTracker</code> object and begin tracking
263      * services.
264      *
265      * <p>
266      * This method calls <code>open(false)</code>.
267      *
268      * @throws java.lang.IllegalStateException if the <code>BundleContext</code>
269      * object with which this <code>ServiceTracker</code> object was
270      * created is no longer valid.
271      * @see #open(boolean)
272      */

273     public void open() {
274         open(false);
275     }
276
277     /**
278      * Open this <code>ServiceTracker</code> object and begin tracking
279      * services.
280      *
281      * <p>
282      * Services which match the search criteria specified when this
283      * <code>ServiceTracker</code> object was created are now tracked by this
284      * <code>ServiceTracker</code> object.
285      *
286      * @param trackAllServices If <code>true</code>, then this
287      * <code>ServiceTracker</code> will track all matching services
288      * regardless of class loader accessibility. If <code>false</code>,
289      * then this <code>ServiceTracker</code> will only track matching
290      * services which are class loader accessibile to the bundle whose
291      * <code>BundleContext</code> is used by this
292      * <code>ServiceTracker</code>.
293      * @throws java.lang.IllegalStateException if the <code>BundleContext</code>
294      * object with which this <code>ServiceTracker</code> object was
295      * created is no longer valid.
296      * @since 1.3
297      */

298     public synchronized void open(boolean trackAllServices) {
299         if (tracked != null) {
300             return;
301         }
302         if (DEBUG) {
303             System.out.println("ServiceTracker.open: " + filter); //$NON-NLS-1$
304
}
305         tracked = trackAllServices ? new AllTracked() : new Tracked();
306         trackingCount = 0;
307         synchronized (tracked) {
308             try {
309                 context.addServiceListener(tracked, listenerFilter);
310                 ServiceReference[] references;
311                 
312                 if (trackReference != null) { // tracking a single reference
313
references = new ServiceReference[] {trackReference};
314                 }
315                 else { // tracking a set of references
316
references = getInitialReferences(trackAllServices, trackClass,
317                             noUserFilter ? null: filter.toString());
318                 }
319
320                 tracked.setInitialServices(references); // set tracked with
321
// the initial
322
// references
323
}
324             catch (InvalidSyntaxException e) {
325                 throw new RuntimeException JavaDoc(
326                         "unexpected InvalidSyntaxException: " + e.getMessage()); //$NON-NLS-1$
327
}
328         }
329         /* Call tracked outside of synchronized region */
330         tracked.trackInitialServices(); // process the initial references
331
}
332
333     /**
334      * Returns the list of initial <code>ServiceReference</code> objects that
335      * will be tracked by this <code>ServiceTracker</code> object.
336      *
337      * @param trackAllServices If true, use getAllServiceReferences.
338      * @param trackClass the class name with which the service was registered,
339      * or null for all services.
340      * @param filterString the filter criteria or null for all services.
341      * @return the list of initial <code>ServiceReference</code> objects.
342      * @throws InvalidSyntaxException if the filter uses an invalid syntax.
343      */

344     private ServiceReference[] getInitialReferences(boolean trackAllServices,
345             String JavaDoc trackClass, String JavaDoc filterString)
346             throws InvalidSyntaxException {
347         if (trackAllServices) {
348             return context.getAllServiceReferences(trackClass, filterString);
349         }
350         else {
351             return context.getServiceReferences(trackClass, filterString);
352         }
353     }
354
355     /**
356      * Close this <code>ServiceTracker</code> object.
357      *
358      * <p>
359      * This method should be called when this <code>ServiceTracker</code>
360      * object should end the tracking of services.
361      */

362     public synchronized void close() {
363         if (tracked == null) {
364             return;
365         }
366         if (DEBUG) {
367             System.out.println("ServiceTracker.close: " + filter); //$NON-NLS-1$
368
}
369         tracked.close();
370         ServiceReference[] references = getServiceReferences();
371         Tracked outgoing = tracked;
372         tracked = null;
373         try {
374             context.removeServiceListener(outgoing);
375         }
376         catch (IllegalStateException JavaDoc e) {
377             /* In case the context was stopped. */
378         }
379         if (references != null) {
380             for (int i = 0; i < references.length; i++) {
381                 outgoing.untrack(references[i]);
382             }
383         }
384         trackingCount = -1;
385         if (DEBUG) {
386             if ((cachedReference == null) && (cachedService == null)) {
387                 System.out
388                         .println("ServiceTracker.close[cached cleared]: " + filter); //$NON-NLS-1$
389
}
390         }
391     }
392
393     /**
394      * Default implementation of the
395      * <code>ServiceTrackerCustomizer.addingService</code> method.
396      *
397      * <p>
398      * This method is only called when this <code>ServiceTracker</code> object
399      * has been constructed with a <code>null ServiceTrackerCustomizer</code>
400      * argument.
401      *
402      * The default implementation returns the result of calling
403      * <code>getService</code>, on the <code>BundleContext</code> object
404      * with which this <code>ServiceTracker</code> object was created, passing
405      * the specified <code>ServiceReference</code> object.
406      * <p>
407      * This method can be overridden in a subclass to customize the service
408      * object to be tracked for the service being added. In that case, take care
409      * not to rely on the default implementation of removedService that will
410      * unget the service.
411      *
412      * @param reference Reference to service being added to this
413      * <code>ServiceTracker</code> object.
414      * @return The service object to be tracked for the service added to this
415      * <code>ServiceTracker</code> object.
416      * @see ServiceTrackerCustomizer
417      */

418     public Object JavaDoc addingService(ServiceReference reference) {
419         return context.getService(reference);
420     }
421
422     /**
423      * Default implementation of the
424      * <code>ServiceTrackerCustomizer.modifiedService</code> method.
425      *
426      * <p>
427      * This method is only called when this <code>ServiceTracker</code> object
428      * has been constructed with a <code>null ServiceTrackerCustomizer</code>
429      * argument.
430      *
431      * The default implementation does nothing.
432      *
433      * @param reference Reference to modified service.
434      * @param service The service object for the modified service.
435      * @see ServiceTrackerCustomizer
436      */

437     public void modifiedService(ServiceReference reference, Object JavaDoc service) {
438     }
439
440     /**
441      * Default implementation of the
442      * <code>ServiceTrackerCustomizer.removedService</code> method.
443      *
444      * <p>
445      * This method is only called when this <code>ServiceTracker</code> object
446      * has been constructed with a <code>null ServiceTrackerCustomizer</code>
447      * argument.
448      *
449      * The default implementation calls <code>ungetService</code>, on the
450      * <code>BundleContext</code> object with which this
451      * <code>ServiceTracker</code> object was created, passing the specified
452      * <code>ServiceReference</code> object.
453      * <p>
454      * This method can be overridden in a subclass. If the default
455      * implementation of <code>addingService</code> method was used, this
456      * method must unget the service.
457      *
458      * @param reference Reference to removed service.
459      * @param service The service object for the removed service.
460      * @see ServiceTrackerCustomizer
461      */

462     public void removedService(ServiceReference reference, Object JavaDoc service) {
463         context.ungetService(reference);
464     }
465
466     /**
467      * Wait for at least one service to be tracked by this
468      * <code>ServiceTracker</code> object.
469      * <p>
470      * It is strongly recommended that <code>waitForService</code> is not used
471      * during the calling of the <code>BundleActivator</code> methods.
472      * <code>BundleActivator</code> methods are expected to complete in a
473      * short period of time.
474      *
475      * @param timeout time interval in milliseconds to wait. If zero, the method
476      * will wait indefinately.
477      * @return Returns the result of <code>getService()</code>.
478      * @throws InterruptedException If another thread has interrupted the
479      * current thread.
480      * @throws IllegalArgumentException If the value of timeout is negative.
481      */

482     public Object JavaDoc waitForService(long timeout) throws InterruptedException JavaDoc {
483         if (timeout < 0) {
484             throw new IllegalArgumentException JavaDoc("timeout value is negative"); //$NON-NLS-1$
485
}
486         Object JavaDoc object = getService();
487         while (object == null) {
488             Tracked tracked = this.tracked; /*
489                                              * use local var since we are not
490                                              * synchronized
491                                              */

492             if (tracked == null) { /* if ServiceTracker is not open */
493                 return null;
494             }
495             synchronized (tracked) {
496                 if (tracked.size() == 0) {
497                     tracked.wait(timeout);
498                 }
499             }
500             object = getService();
501             if (timeout > 0) {
502                 return object;
503             }
504         }
505         return object;
506     }
507
508     /**
509      * Return an array of <code>ServiceReference</code> objects for all
510      * services being tracked by this <code>ServiceTracker</code> object.
511      *
512      * @return Array of <code>ServiceReference</code> objects or
513      * <code>null</code> if no service are being tracked.
514      */

515     public ServiceReference[] getServiceReferences() {
516         Tracked tracked = this.tracked; /*
517                                          * use local var since we are not
518                                          * synchronized
519                                          */

520         if (tracked == null) { /* if ServiceTracker is not open */
521             return null;
522         }
523         synchronized (tracked) {
524             int length = tracked.size();
525             if (length == 0) {
526                 return null;
527             }
528             ServiceReference[] references = new ServiceReference[length];
529             Enumeration JavaDoc keys = tracked.keys();
530             for (int i = 0; i < length; i++) {
531                 references[i] = (ServiceReference) keys.nextElement();
532             }
533             return references;
534         }
535     }
536
537     /**
538      * Returns a <code>ServiceReference</code> object for one of the services
539      * being tracked by this <code>ServiceTracker</code> object.
540      *
541      * <p>
542      * If multiple services are being tracked, the service with the highest
543      * ranking (as specified in its <code>service.ranking</code> property) is
544      * returned.
545      *
546      * <p>
547      * If there is a tie in ranking, the service with the lowest service ID (as
548      * specified in its <code>service.id</code> property); that is, the
549      * service that was registered first is returned.
550      * <p>
551      * This is the same algorithm used by
552      * <code>BundleContext.getServiceReference</code>.
553      *
554      * @return <code>ServiceReference</code> object or <code>null</code> if
555      * no service is being tracked.
556      * @since 1.1
557      */

558     public ServiceReference getServiceReference() {
559         ServiceReference reference = cachedReference;
560         if (reference != null) {
561             if (DEBUG) {
562                 System.out
563                         .println("ServiceTracker.getServiceReference[cached]: " + filter); //$NON-NLS-1$
564
}
565             return reference;
566         }
567         if (DEBUG) {
568             System.out.println("ServiceTracker.getServiceReference: " + filter); //$NON-NLS-1$
569
}
570         ServiceReference[] references = getServiceReferences();
571         int length = (references == null) ? 0 : references.length;
572         if (length == 0) /* if no service is being tracked */
573         {
574             return null;
575         }
576         int index = 0;
577         if (length > 1) /* if more than one service, select highest ranking */
578         {
579             int rankings[] = new int[length];
580             int count = 0;
581             int maxRanking = Integer.MIN_VALUE;
582             for (int i = 0; i < length; i++) {
583                 Object JavaDoc property = references[i]
584                         .getProperty(Constants.SERVICE_RANKING);
585                 int ranking = (property instanceof Integer JavaDoc) ? ((Integer JavaDoc) property)
586                         .intValue()
587                         : 0;
588                 rankings[i] = ranking;
589                 if (ranking > maxRanking) {
590                     index = i;
591                     maxRanking = ranking;
592                     count = 1;
593                 }
594                 else {
595                     if (ranking == maxRanking) {
596                         count++;
597                     }
598                 }
599             }
600             if (count > 1) /* if still more than one service, select lowest id */
601             {
602                 long minId = Long.MAX_VALUE;
603                 for (int i = 0; i < length; i++) {
604                     if (rankings[i] == maxRanking) {
605                         long id = ((Long JavaDoc) (references[i]
606                                 .getProperty(Constants.SERVICE_ID)))
607                                 .longValue();
608                         if (id < minId) {
609                             index = i;
610                             minId = id;
611                         }
612                     }
613                 }
614             }
615         }
616         return cachedReference = references[index];
617     }
618
619     /**
620      * Returns the service object for the specified
621      * <code>ServiceReference</code> object if the referenced service is being
622      * tracked by this <code>ServiceTracker</code> object.
623      *
624      * @param reference Reference to the desired service.
625      * @return Service object or <code>null</code> if the service referenced
626      * by the specified <code>ServiceReference</code> object is not
627      * being tracked.
628      */

629     public Object JavaDoc getService(ServiceReference reference) {
630         Tracked tracked = this.tracked; /*
631                                          * use local var since we are not
632                                          * synchronized
633                                          */

634         if (tracked == null) { /* if ServiceTracker is not open */
635             return null;
636         }
637         synchronized (tracked) {
638             return tracked.get(reference);
639         }
640     }
641
642     /**
643      * Return an array of service objects for all services being tracked by this
644      * <code>ServiceTracker</code> object.
645      *
646      * @return Array of service objects or <code>null</code> if no service are
647      * being tracked.
648      */

649     public Object JavaDoc[] getServices() {
650         Tracked tracked = this.tracked; /*
651                                          * use local var since we are not
652                                          * synchronized
653                                          */

654         if (tracked == null) { /* if ServiceTracker is not open */
655             return null;
656         }
657         synchronized (tracked) {
658             ServiceReference[] references = getServiceReferences();
659             int length = (references == null) ? 0 : references.length;
660             if (length == 0) {
661                 return null;
662             }
663             Object JavaDoc[] objects = new Object JavaDoc[length];
664             for (int i = 0; i < length; i++) {
665                 objects[i] = getService(references[i]);
666             }
667             return objects;
668         }
669     }
670
671     /**
672      * Returns a service object for one of the services being tracked by this
673      * <code>ServiceTracker</code> object.
674      *
675      * <p>
676      * If any services are being tracked, this method returns the result of
677      * calling <code>getService(getServiceReference())</code>.
678      *
679      * @return Service object or <code>null</code> if no service is being
680      * tracked.
681      */

682     public Object JavaDoc getService() {
683         Object JavaDoc service = cachedService;
684         if (service != null) {
685             if (DEBUG) {
686                 System.out
687                         .println("ServiceTracker.getService[cached]: " + filter); //$NON-NLS-1$
688
}
689             return service;
690         }
691         if (DEBUG) {
692             System.out.println("ServiceTracker.getService: " + filter); //$NON-NLS-1$
693
}
694         ServiceReference reference = getServiceReference();
695         if (reference == null) {
696             return null;
697         }
698         return cachedService = getService(reference);
699     }
700
701     /**
702      * Remove a service from this <code>ServiceTracker</code> object.
703      *
704      * The specified service will be removed from this
705      * <code>ServiceTracker</code> object. If the specified service was being
706      * tracked then the <code>ServiceTrackerCustomizer.removedService</code>
707      * method will be called for that service.
708      *
709      * @param reference Reference to the service to be removed.
710      */

711     public void remove(ServiceReference reference) {
712         Tracked tracked = this.tracked; /*
713                                          * use local var since we are not
714                                          * synchronized
715                                          */

716         if (tracked == null) { /* if ServiceTracker is not open */
717             return;
718         }
719         tracked.untrack(reference);
720     }
721
722     /**
723      * Return the number of services being tracked by this
724      * <code>ServiceTracker</code> object.
725      *
726      * @return Number of services being tracked.
727      */

728     public int size() {
729         Tracked tracked = this.tracked; /*
730                                          * use local var since we are not
731                                          * synchronized
732                                          */

733         if (tracked == null) { /* if ServiceTracker is not open */
734             return 0;
735         }
736         return tracked.size();
737     }
738
739     /**
740      * Returns the tracking count for this <code>ServiceTracker</code> object.
741      *
742      * The tracking count is initialized to 0 when this
743      * <code>ServiceTracker</code> object is opened. Every time a service is
744      * added, modified or removed from this <code>ServiceTracker</code> object
745      * the tracking count is incremented.
746      *
747      * <p>
748      * The tracking count can be used to determine if this
749      * <code>ServiceTracker</code> object has added, modified or removed a
750      * service by comparing a tracking count value previously collected with the
751      * current tracking count value. If the value has not changed, then no
752      * service has been added, modified or removed from this
753      * <code>ServiceTracker</code> object since the previous tracking count
754      * was collected.
755      *
756      * @since 1.2
757      * @return The tracking count for this <code>ServiceTracker</code> object
758      * or -1 if this <code>ServiceTracker</code> object is not open.
759      */

760     public int getTrackingCount() {
761         return trackingCount;
762     }
763
764     /**
765      * Called by the Tracked object whenever the set of tracked services is
766      * modified. Increments the tracking count and clears the cache.
767      *
768      * @GuardedBy tracked
769      */

770     /*
771      * This method must not be synchronized since it is called by Tracked while
772      * Tracked is synchronized. We don't want synchronization interactions
773      * between the ServiceListener thread and the user thread.
774      */

775     void modified() {
776         trackingCount++; /* increment modification count */
777         cachedReference = null; /* clear cached value */
778         cachedService = null; /* clear cached value */
779         if (DEBUG) {
780             System.out.println("ServiceTracker.modified: " + filter); //$NON-NLS-1$
781
}
782     }
783
784
785     /**
786      * Inner class to track services. If a <code>ServiceTracker</code> object
787      * is reused (closed then reopened), then a new Tracked object is used. This
788      * class is a hashtable mapping <code>ServiceReference</code> object ->
789      * customized Object. This class is the <code>ServiceListener</code>
790      * object for the tracker. This class is used to synchronize access to the
791      * tracked services. This is not a public class. It is only for use by the
792      * implementation of the <code>ServiceTracker</code> class.
793      *
794      * @ThreadSafe
795      */

796     class Tracked extends Hashtable JavaDoc implements ServiceListener {
797         static final long serialVersionUID = -7420065199791006079L;
798         /**
799          * List of ServiceReferences in the process of being added. This is used
800          * to deal with nesting of ServiceEvents. Since ServiceEvents are
801          * synchronously delivered, ServiceEvents can be nested. For example,
802          * when processing the adding of a service and the customizer causes the
803          * service to be unregistered, notification to the nested call to
804          * untrack that the service was unregistered can be made to the track
805          * method.
806          *
807          * Since the ArrayList implementation is not synchronized, all access to
808          * this list must be protected by the same synchronized object for
809          * thread-safety.
810          *
811          * @GuardedBy this
812          */

813         private final ArrayList JavaDoc adding;
814
815         /**
816          * true if the tracked object is closed.
817          *
818          * This field is volatile because it is set by one thread and read by
819          * another.
820          */

821         private volatile boolean closed;
822
823         /**
824          * Initial list of ServiceReferences for the tracker. This is used to
825          * correctly process the initial services which could become
826          * unregistered before they are tracked. This is necessary since the
827          * initial set of tracked services are not "announced" by ServiceEvents
828          * and therefore the ServiceEvent for unregistration could be delivered
829          * before we track the service.
830          *
831          * A service must not be in both the initial and adding lists at the
832          * same time. A service must be moved from the initial list to the
833          * adding list "atomically" before we begin tracking it.
834          *
835          * Since the LinkedList implementation is not synchronized, all access
836          * to this list must be protected by the same synchronized object for
837          * thread-safety.
838          *
839          * @GuardedBy this
840          */

841         private final LinkedList JavaDoc initial;
842
843         /**
844          * Tracked constructor.
845          */

846         protected Tracked() {
847             super();
848             closed = false;
849             adding = new ArrayList JavaDoc(6);
850             initial = new LinkedList JavaDoc();
851         }
852
853         /**
854          * Set initial list of services into tracker before ServiceEvents begin
855          * to be received.
856          *
857          * This method must be called from ServiceTracker.open while
858          * synchronized on this object in the same synchronized block as the
859          * addServiceListener call.
860          *
861          * @param references The initial list of services to be tracked.
862          * @GuardedBy this
863          */

864         protected void setInitialServices(ServiceReference[] references) {
865             if (references == null) {
866                 return;
867             }
868             int size = references.length;
869             for (int i = 0; i < size; i++) {
870                 if (DEBUG) {
871                     System.out
872                             .println("ServiceTracker.Tracked.setInitialServices: " + references[i]); //$NON-NLS-1$
873
}
874                 initial.add(references[i]);
875             }
876         }
877
878         /**
879          * Track the initial list of services. This is called after
880          * ServiceEvents can begin to be received.
881          *
882          * This method must be called from ServiceTracker.open while not
883          * synchronized on this object after the addServiceListener call.
884          *
885          */

886         protected void trackInitialServices() {
887             while (true) {
888                 ServiceReference reference;
889                 synchronized (this) {
890                     if (initial.size() == 0) {
891                         /*
892                          * if there are no more inital services
893                          */

894                         return; /* we are done */
895                     }
896                     /*
897                      * move the first service from the initial list to the
898                      * adding list within this synchronized block.
899                      */

900                     reference = (ServiceReference) initial.removeFirst();
901                     if (this.get(reference) != null) {
902                         /* if we are already tracking this service */
903                         if (DEBUG) {
904                             System.out
905                                     .println("ServiceTracker.Tracked.trackInitialServices[already tracked]: " + reference); //$NON-NLS-1$
906
}
907                         continue; /* skip this service */
908                     }
909                     if (adding.contains(reference)) {
910                         /*
911                          * if this service is already in the process of being
912                          * added.
913                          */

914                         if (DEBUG) {
915                             System.out
916                                     .println("ServiceTracker.Tracked.trackInitialServices[already adding]: " + reference); //$NON-NLS-1$
917
}
918                         continue; /* skip this service */
919                     }
920                     adding.add(reference);
921                 }
922                 if (DEBUG) {
923                     System.out
924                             .println("ServiceTracker.Tracked.trackInitialServices: " + reference); //$NON-NLS-1$
925
}
926                 trackAdding(reference); /*
927                                          * Begin tracking it. We call
928                                          * trackAdding since we have already put
929                                          * the reference in the adding list.
930                                          */

931             }
932         }
933
934         /**
935          * Called by the owning <code>ServiceTracker</code> object when it is
936          * closed.
937          */

938         protected void close() {
939             closed = true;
940         }
941
942         /**
943          * <code>ServiceListener</code> method for the
944          * <code>ServiceTracker</code> class. This method must NOT be
945          * synchronized to avoid deadlock potential.
946          *
947          * @param event <code>ServiceEvent</code> object from the framework.
948          */

949         public void serviceChanged(ServiceEvent event) {
950             /*
951              * Check if we had a delayed call (which could happen when we
952              * close).
953              */

954             if (closed) {
955                 return;
956             }
957             ServiceReference reference = event.getServiceReference();
958             if (DEBUG) {
959                 System.out
960                         .println("ServiceTracker.Tracked.serviceChanged[" + event.getType() + "]: " + reference); //$NON-NLS-1$ //$NON-NLS-2$
961
}
962
963             switch (event.getType()) {
964                 case ServiceEvent.REGISTERED :
965                 case ServiceEvent.MODIFIED :
966                     if (noUserFilter) { // no user supplied filter to be checked
967
track(reference);
968                         /*
969                          * If the customizer throws an unchecked exception, it
970                          * is safe to let it propagate
971                          */

972                     }
973                     else { // filter supplied by user must be checked
974
if (filter.match(reference)) {
975                             track(reference);
976                             /*
977                              * If the customizer throws an unchecked exception,
978                              * it is safe to let it propagate
979                              */

980                         }
981                         else {
982                             untrack(reference);
983                             /*
984                              * If the customizer throws an unchecked exception,
985                              * it is safe to let it propagate
986                              */

987                         }
988                     }
989                     break;
990                 case ServiceEvent.UNREGISTERING :
991                     untrack(reference);
992                     /*
993                      * If the customizer throws an unchecked exception, it is
994                      * safe to let it propagate
995                      */

996                     break;
997             }
998         }
999
1000        /**
1001         * Begin to track the referenced service.
1002         *
1003         * @param reference Reference to a service to be tracked.
1004         */

1005        private void track(ServiceReference reference) {
1006            Object JavaDoc object;
1007            synchronized (this) {
1008                object = this.get(reference);
1009            }
1010            if (object != null) /* we are already tracking the service */
1011            {
1012                if (DEBUG) {
1013                    System.out
1014                            .println("ServiceTracker.Tracked.track[modified]: " + reference); //$NON-NLS-1$
1015
}
1016                synchronized (this) {
1017                    modified(); /* increment modification count */
1018                }
1019                /* Call customizer outside of synchronized region */
1020                customizer.modifiedService(reference, object);
1021                /*
1022                 * If the customizer throws an unchecked exception, it is safe
1023                 * to let it propagate
1024                 */

1025                return;
1026            }
1027            synchronized (this) {
1028                if (adding.contains(reference)) { /*
1029                                                     * if this service is
1030                                                     * already in the process of
1031                                                     * being added.
1032                                                     */

1033                    if (DEBUG) {
1034                        System.out
1035                                .println("ServiceTracker.Tracked.track[already adding]: " + reference); //$NON-NLS-1$
1036
}
1037                    return;
1038                }
1039                adding.add(reference); /* mark this service is being added */
1040            }
1041
1042            trackAdding(reference); /*
1043                                     * call trackAdding now that we have put the
1044                                     * reference in the adding list
1045                                     */

1046        }
1047
1048        /**
1049         * Common logic to add a service to the tracker used by track and
1050         * trackInitialServices. The specified reference must have been placed
1051         * in the adding list before calling this method.
1052         *
1053         * @param reference Reference to a service to be tracked.
1054         */

1055        private void trackAdding(ServiceReference reference) {
1056            if (DEBUG) {
1057                System.out
1058                        .println("ServiceTracker.Tracked.trackAdding: " + reference); //$NON-NLS-1$
1059
}
1060            Object JavaDoc object = null;
1061            boolean becameUntracked = false;
1062            /* Call customizer outside of synchronized region */
1063            try {
1064                object = customizer.addingService(reference);
1065                /*
1066                 * If the customizer throws an unchecked exception, it will
1067                 * propagate after the finally
1068                 */

1069            }
1070            finally {
1071                synchronized (this) {
1072                    if (adding.remove(reference)) { /*
1073                                                     * if the service was not
1074                                                     * untracked during the
1075                                                     * customizer callback
1076                                                     */

1077                        if (object != null) {
1078                            this.put(reference, object);
1079                            modified(); /* increment modification count */
1080                            notifyAll(); /*
1081                                             * notify any waiters in
1082                                             * waitForService
1083                                             */

1084                        }
1085                    }
1086                    else {
1087                        becameUntracked = true;
1088                    }
1089                }
1090            }
1091            /*
1092             * The service became untracked during the customizer callback.
1093             */

1094            if (becameUntracked) {
1095                if (DEBUG) {
1096                    System.out
1097                            .println("ServiceTracker.Tracked.trackAdding[removed]: " + reference); //$NON-NLS-1$
1098
}
1099                /* Call customizer outside of synchronized region */
1100                customizer.removedService(reference, object);
1101                /*
1102                 * If the customizer throws an unchecked exception, it is safe
1103                 * to let it propagate
1104                 */

1105            }
1106        }
1107
1108        /**
1109         * Discontinue tracking the referenced service.
1110         *
1111         * @param reference Reference to the tracked service.
1112         */

1113        protected void untrack(ServiceReference reference) {
1114            Object JavaDoc object;
1115            synchronized (this) {
1116                if (initial.remove(reference)) { /*
1117                                                     * if this service is
1118                                                     * already in the list of
1119                                                     * initial references to
1120                                                     * process
1121                                                     */

1122                    if (DEBUG) {
1123                        System.out
1124                                .println("ServiceTracker.Tracked.untrack[removed from initial]: " + reference); //$NON-NLS-1$
1125
}
1126                    return; /*
1127                             * we have removed it from the list and it will not
1128                             * be processed
1129                             */

1130                }
1131
1132                if (adding.remove(reference)) { /*
1133                                                 * if the service is in the
1134                                                 * process of being added
1135                                                 */

1136                    if (DEBUG) {
1137                        System.out
1138                                .println("ServiceTracker.Tracked.untrack[being added]: " + reference); //$NON-NLS-1$
1139
}
1140                    return; /*
1141                             * in case the service is untracked while in the
1142                             * process of adding
1143                             */

1144                }
1145                object = this.remove(reference); /*
1146                                                     * must remove from tracker
1147                                                     * before calling customizer
1148                                                     * callback
1149                                                     */

1150                if (object == null) { /* are we actually tracking the service */
1151                    return;
1152                }
1153                modified(); /* increment modification count */
1154            }
1155            if (DEBUG) {
1156                System.out
1157                        .println("ServiceTracker.Tracked.untrack[removed]: " + reference); //$NON-NLS-1$
1158
}
1159            /* Call customizer outside of synchronized region */
1160            customizer.removedService(reference, object);
1161            /*
1162             * If the customizer throws an unchecked exception, it is safe to
1163             * let it propagate
1164             */

1165        }
1166    }
1167
1168    /**
1169     * Subclass of Tracked which implements the AllServiceListener interface.
1170     * This class is used by the ServiceTracker if open is called with true.
1171     *
1172     * @since 1.3
1173     * @ThreadSafe
1174     */

1175    class AllTracked extends Tracked implements AllServiceListener {
1176        static final long serialVersionUID = 4050764875305137716L;
1177
1178        /**
1179         * AllTracked constructor.
1180         */

1181        protected AllTracked() {
1182            super();
1183        }
1184    }
1185}
1186
Popular Tags