blob: c0217b6ec4a7f6955a647c492d030ae2d247850f [file] [log] [blame]
Marcel Offermansfaaed472010-09-08 10:07:32 +00001package org.apache.felix.dm.tracker;
Marcel Offermansc854d1a2009-11-24 15:41:41 +00002/*
3 * Copyright (c) OSGi Alliance (2000, 2009). All Rights Reserved.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18import java.security.AccessController;
19import java.security.PrivilegedAction;
Marcel Offermans7f687482010-05-11 09:12:34 +000020import java.util.ArrayList;
21import java.util.List;
Marcel Offermansc854d1a2009-11-24 15:41:41 +000022
Marcel Offermans7f687482010-05-11 09:12:34 +000023import org.apache.felix.dm.DependencyManager;
Marcel Offermans8b93efa2010-07-02 18:27:21 +000024import org.apache.felix.dm.ServiceUtil;
Marcel Offermansc854d1a2009-11-24 15:41:41 +000025import org.osgi.framework.AllServiceListener;
26import org.osgi.framework.BundleContext;
27import org.osgi.framework.Constants;
28import org.osgi.framework.Filter;
29import org.osgi.framework.InvalidSyntaxException;
30import org.osgi.framework.ServiceEvent;
31import org.osgi.framework.ServiceListener;
32import org.osgi.framework.ServiceReference;
33import org.osgi.framework.Version;
34
35/**
36 * The <code>ServiceTracker</code> class simplifies using services from the
37 * Framework's service registry.
38 * <p>
39 * A <code>ServiceTracker</code> object is constructed with search criteria and
40 * a <code>ServiceTrackerCustomizer</code> object. A <code>ServiceTracker</code>
41 * can use a <code>ServiceTrackerCustomizer</code> to customize the service
42 * objects to be tracked. The <code>ServiceTracker</code> can then be opened to
43 * begin tracking all services in the Framework's service registry that match
44 * the specified search criteria. The <code>ServiceTracker</code> correctly
45 * handles all of the details of listening to <code>ServiceEvent</code>s and
46 * getting and ungetting services.
47 * <p>
48 * The <code>getServiceReferences</code> method can be called to get references
49 * to the services being tracked. The <code>getService</code> and
50 * <code>getServices</code> methods can be called to get the service objects for
51 * the tracked service.
52 * <p>
53 * The <code>ServiceTracker</code> class is thread-safe. It does not call a
54 * <code>ServiceTrackerCustomizer</code> while holding any locks.
55 * <code>ServiceTrackerCustomizer</code> implementations must also be
56 * thread-safe.
57 *
58 * @ThreadSafe
59 * @version $Revision: 6386 $
60 */
61public class ServiceTracker implements ServiceTrackerCustomizer {
62 /* set this to true to compile in debug messages */
63 static final boolean DEBUG = false;
64 /**
65 * The Bundle Context used by this <code>ServiceTracker</code>.
66 */
67 protected final BundleContext context;
68 /**
69 * The Filter used by this <code>ServiceTracker</code> which specifies the
70 * search criteria for the services to track.
71 *
72 * @since 1.1
73 */
74 protected final Filter filter;
75 /**
76 * The <code>ServiceTrackerCustomizer</code> for this tracker.
77 */
78 final ServiceTrackerCustomizer customizer;
79 /**
80 * Filter string for use when adding the ServiceListener. If this field is
81 * set, then certain optimizations can be taken since we don't have a user
82 * supplied filter.
83 */
84 final String listenerFilter;
85 /**
86 * Class name to be tracked. If this field is set, then we are tracking by
87 * class name.
88 */
89 private final String trackClass;
90 /**
91 * Reference to be tracked. If this field is set, then we are tracking a
92 * single ServiceReference.
93 */
94 private final ServiceReference trackReference;
95 /**
96 * Tracked services: <code>ServiceReference</code> -> customized Object and
97 * <code>ServiceListener</code> object
98 */
99 private volatile Tracked tracked;
100
101 /**
102 * Accessor method for the current Tracked object. This method is only
103 * intended to be used by the unsynchronized methods which do not modify the
104 * tracked field.
105 *
106 * @return The current Tracked object.
107 */
108 private Tracked tracked() {
109 return tracked;
110 }
111
112 /**
113 * Cached ServiceReference for getServiceReference.
114 *
115 * This field is volatile since it is accessed by multiple threads.
116 */
117 private volatile ServiceReference cachedReference;
118 /**
119 * Cached service object for getService.
120 *
121 * This field is volatile since it is accessed by multiple threads.
122 */
123 private volatile Object cachedService;
124
125 /**
126 * org.osgi.framework package version which introduced
127 * {@link ServiceEvent#MODIFIED_ENDMATCH}
128 */
129 private static final Version endMatchVersion = new Version(1, 5, 0);
130
131 /**
132 * Create a <code>ServiceTracker</code> on the specified
133 * <code>ServiceReference</code>.
134 *
135 * <p>
136 * The service referenced by the specified <code>ServiceReference</code>
137 * will be tracked by this <code>ServiceTracker</code>.
138 *
139 * @param context The <code>BundleContext</code> against which the tracking
140 * is done.
141 * @param reference The <code>ServiceReference</code> for the service to be
142 * tracked.
143 * @param customizer The customizer object to call when services are added,
144 * modified, or removed in this <code>ServiceTracker</code>. If
145 * customizer is <code>null</code>, then this
146 * <code>ServiceTracker</code> will be used as the
147 * <code>ServiceTrackerCustomizer</code> and this
148 * <code>ServiceTracker</code> will call the
149 * <code>ServiceTrackerCustomizer</code> methods on itself.
150 */
151 public ServiceTracker(final BundleContext context,
152 final ServiceReference reference,
153 final 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.SERVICE_ID + "="
159 + reference.getProperty(Constants.SERVICE_ID).toString() + ")";
160 try {
161 this.filter = context.createFilter(listenerFilter);
162 }
163 catch (InvalidSyntaxException e) {
164 /*
165 * we could only get this exception if the ServiceReference was
166 * invalid
167 */
168 IllegalArgumentException iae = new IllegalArgumentException(
169 "unexpected InvalidSyntaxException: " + e.getMessage());
170 iae.initCause(e);
171 throw iae;
172 }
173 }
174
175 /**
176 * Create a <code>ServiceTracker</code> on the specified class name.
177 *
178 * <p>
179 * Services registered under the specified class name will be tracked by
180 * this <code>ServiceTracker</code>.
181 *
182 * @param context The <code>BundleContext</code> against which the tracking
183 * is done.
184 * @param clazz The class name of the services to be tracked.
185 * @param customizer The customizer object to call when services are added,
186 * modified, or removed in this <code>ServiceTracker</code>. If
187 * customizer is <code>null</code>, then this
188 * <code>ServiceTracker</code> will be used as the
189 * <code>ServiceTrackerCustomizer</code> and this
190 * <code>ServiceTracker</code> will call the
191 * <code>ServiceTrackerCustomizer</code> methods on itself.
192 */
193 public ServiceTracker(final BundleContext context, final String clazz,
194 final ServiceTrackerCustomizer customizer) {
195 this.context = context;
196 this.trackReference = null;
197 this.trackClass = clazz;
198 this.customizer = (customizer == null) ? this : customizer;
199 // we call clazz.toString to verify clazz is non-null!
200 this.listenerFilter = "(" + Constants.OBJECTCLASS + "="
201 + clazz.toString() + ")";
202 try {
203 this.filter = context.createFilter(listenerFilter);
204 }
205 catch (InvalidSyntaxException e) {
206 /*
207 * we could only get this exception if the clazz argument was
208 * malformed
209 */
210 IllegalArgumentException iae = new IllegalArgumentException(
211 "unexpected InvalidSyntaxException: " + e.getMessage());
212 iae.initCause(e);
213 throw iae;
214 }
215 }
216
217 /**
218 * Create a <code>ServiceTracker</code> on the specified <code>Filter</code>
219 * object.
220 *
221 * <p>
222 * Services which match the specified <code>Filter</code> object will be
223 * tracked by this <code>ServiceTracker</code>.
224 *
225 * @param context The <code>BundleContext</code> against which the tracking
226 * is done.
227 * @param filter The <code>Filter</code> to select the services to be
228 * tracked.
229 * @param customizer The customizer object to call when services are added,
230 * modified, or removed in this <code>ServiceTracker</code>. If
231 * customizer is null, then this <code>ServiceTracker</code> will be
232 * used as the <code>ServiceTrackerCustomizer</code> and this
233 * <code>ServiceTracker</code> will call the
234 * <code>ServiceTrackerCustomizer</code> methods on itself.
235 * @since 1.1
236 */
237 public ServiceTracker(final BundleContext context, final Filter filter,
238 final ServiceTrackerCustomizer customizer) {
239 this.context = context;
240 this.trackReference = null;
241 this.trackClass = null;
242 final Version frameworkVersion = (Version) AccessController
243 .doPrivileged(new PrivilegedAction() {
244 public Object run() {
245 String version = context
246 .getProperty(Constants.FRAMEWORK_VERSION);
247 return (version == null) ? Version.emptyVersion
248 : new Version(version);
249 }
250 });
251 final boolean endMatchSupported = (frameworkVersion
252 .compareTo(endMatchVersion) >= 0);
253 this.listenerFilter = endMatchSupported ? filter.toString() : null;
254 this.filter = filter;
255 this.customizer = (customizer == null) ? this : customizer;
256 if ((context == null) || (filter == null)) {
257 /*
258 * we throw a NPE here to be consistent with the other constructors
259 */
260 throw new NullPointerException();
261 }
262 }
263
264 /**
265 * Open this <code>ServiceTracker</code> and begin tracking services.
266 *
267 * <p>
268 * This implementation calls <code>open(false)</code>.
269 *
270 * @throws java.lang.IllegalStateException If the <code>BundleContext</code>
271 * with which this <code>ServiceTracker</code> was created is no
272 * longer valid.
273 * @see #open(boolean)
274 */
275 public void open() {
276 open(false);
277 }
278
279 /**
280 * Open this <code>ServiceTracker</code> and begin tracking services.
281 *
282 * <p>
283 * Services which match the search criteria specified when this
284 * <code>ServiceTracker</code> was created are now tracked by this
285 * <code>ServiceTracker</code>.
286 *
287 * @param trackAllServices If <code>true</code>, then this
288 * <code>ServiceTracker</code> will track all matching services
289 * regardless of class loader accessibility. If <code>false</code>,
290 * then this <code>ServiceTracker</code> will only track matching
291 * services which are class loader accessible to the bundle whose
292 * <code>BundleContext</code> is used by this
293 * <code>ServiceTracker</code>.
294 * @throws java.lang.IllegalStateException If the <code>BundleContext</code>
295 * with which this <code>ServiceTracker</code> was created is no
296 * longer valid.
297 * @since 1.3
298 */
299 public void open(boolean trackAllServices) {
300 final Tracked t;
301 synchronized (this) {
302 if (tracked != null) {
303 return;
304 }
305 if (DEBUG) {
306 System.out.println("ServiceTracker.open: " + filter);
307 }
308 t = trackAllServices ? new AllTracked() : new Tracked();
309 synchronized (t) {
310 try {
311 context.addServiceListener(t, listenerFilter);
312 ServiceReference[] references = null;
313 if (trackClass != null) {
314 references = getInitialReferences(trackAllServices,
315 trackClass, null);
316 }
317 else {
318 if (trackReference != null) {
319 if (trackReference.getBundle() != null) {
320 references = new ServiceReference[] {trackReference};
321 }
322 }
323 else { /* user supplied filter */
324 references = getInitialReferences(trackAllServices,
325 null,
326 (listenerFilter != null) ? listenerFilter
327 : filter.toString());
328 }
329 }
330 /* set tracked with the initial references */
331 t.setInitial(references);
332 }
333 catch (InvalidSyntaxException e) {
334 throw new RuntimeException(
335 "unexpected InvalidSyntaxException: "
336 + e.getMessage(), e);
337 }
338 }
339 tracked = t;
340 }
341 /* Call tracked outside of synchronized region */
342 t.trackInitial(); /* process the initial references */
343 }
344
345 /**
346 * Returns the list of initial <code>ServiceReference</code>s that will be
347 * tracked by this <code>ServiceTracker</code>.
348 *
349 * @param trackAllServices If <code>true</code>, use
350 * <code>getAllServiceReferences</code>.
351 * @param className The class name with which the service was registered, or
352 * <code>null</code> for all services.
353 * @param filterString The filter criteria or <code>null</code> for all
354 * services.
355 * @return The list of initial <code>ServiceReference</code>s.
356 * @throws InvalidSyntaxException If the specified filterString has an
357 * invalid syntax.
358 */
359 private ServiceReference[] getInitialReferences(boolean trackAllServices,
360 String className, String filterString)
361 throws InvalidSyntaxException {
362 if (trackAllServices) {
363 return context.getAllServiceReferences(className, filterString);
364 }
365 return context.getServiceReferences(className, filterString);
366 }
367
368 /**
369 * Close this <code>ServiceTracker</code>.
370 *
371 * <p>
372 * This method should be called when this <code>ServiceTracker</code> should
373 * end the tracking of services.
374 *
375 * <p>
376 * This implementation calls {@link #getServiceReferences()} to get the list
377 * of tracked services to remove.
378 */
379 public void close() {
380 final Tracked outgoing;
381 final ServiceReference[] references;
382 synchronized (this) {
383 outgoing = tracked;
384 if (outgoing == null) {
385 return;
386 }
387 if (DEBUG) {
388 System.out.println("ServiceTracker.close: " + filter);
389 }
390 outgoing.close();
391 references = getServiceReferences();
392 tracked = null;
393 try {
394 context.removeServiceListener(outgoing);
395 }
396 catch (IllegalStateException e) {
397 /* In case the context was stopped. */
398 }
399 }
400 modified(); /* clear the cache */
401 synchronized (outgoing) {
402 outgoing.notifyAll(); /* wake up any waiters */
403 }
404 if (references != null) {
405 for (int i = 0; i < references.length; i++) {
406 outgoing.untrack(references[i], null);
407 }
408 }
409 if (DEBUG) {
410 if ((cachedReference == null) && (cachedService == null)) {
411 System.out
412 .println("ServiceTracker.close[cached cleared]: "
413 + filter);
414 }
415 }
416 }
417
418 /**
419 * Default implementation of the
420 * <code>ServiceTrackerCustomizer.addingService</code> method.
421 *
422 * <p>
423 * This method is only called when this <code>ServiceTracker</code> has been
424 * constructed with a <code>null ServiceTrackerCustomizer</code> argument.
425 *
426 * <p>
427 * This implementation returns the result of calling <code>getService</code>
428 * on the <code>BundleContext</code> with which this
429 * <code>ServiceTracker</code> was created passing the specified
430 * <code>ServiceReference</code>.
431 * <p>
432 * This method can be overridden in a subclass to customize the service
433 * object to be tracked for the service being added. In that case, take care
434 * not to rely on the default implementation of
435 * {@link #removedService(ServiceReference, Object) removedService} to unget
436 * the service.
437 *
438 * @param reference The reference to the service being added to this
439 * <code>ServiceTracker</code>.
440 * @return The service object to be tracked for the service added to this
441 * <code>ServiceTracker</code>.
442 * @see ServiceTrackerCustomizer#addingService(ServiceReference)
443 */
444 public Object addingService(ServiceReference reference) {
445 return context.getService(reference);
446 }
447
448 public void addedService(ServiceReference reference, Object service) {
449 /* do nothing */
450 }
451
452 /**
453 * Default implementation of the
454 * <code>ServiceTrackerCustomizer.modifiedService</code> method.
455 *
456 * <p>
457 * This method is only called when this <code>ServiceTracker</code> has been
458 * constructed with a <code>null ServiceTrackerCustomizer</code> argument.
459 *
460 * <p>
461 * This implementation does nothing.
462 *
463 * @param reference The reference to modified service.
464 * @param service The service object for the modified service.
465 * @see ServiceTrackerCustomizer#modifiedService(ServiceReference, Object)
466 */
467 public void modifiedService(ServiceReference reference, Object service) {
468 /* do nothing */
469 }
470
471 /**
472 * Default implementation of the
473 * <code>ServiceTrackerCustomizer.removedService</code> method.
474 *
475 * <p>
476 * This method is only called when this <code>ServiceTracker</code> has been
477 * constructed with a <code>null ServiceTrackerCustomizer</code> argument.
478 *
479 * <p>
480 * This implementation calls <code>ungetService</code>, on the
481 * <code>BundleContext</code> with which this <code>ServiceTracker</code>
482 * was created, passing the specified <code>ServiceReference</code>.
483 * <p>
484 * This method can be overridden in a subclass. If the default
485 * implementation of {@link #addingService(ServiceReference) addingService}
486 * method was used, this method must unget the service.
487 *
488 * @param reference The reference to removed service.
489 * @param service The service object for the removed service.
490 * @see ServiceTrackerCustomizer#removedService(ServiceReference, Object)
491 */
492 public void removedService(ServiceReference reference, Object service) {
493 context.ungetService(reference);
494 }
495
496 /**
497 * Wait for at least one service to be tracked by this
498 * <code>ServiceTracker</code>. This method will also return when this
499 * <code>ServiceTracker</code> is closed.
500 *
501 * <p>
502 * It is strongly recommended that <code>waitForService</code> is not used
503 * during the calling of the <code>BundleActivator</code> methods.
504 * <code>BundleActivator</code> methods are expected to complete in a short
505 * period of time.
506 *
507 * <p>
508 * This implementation calls {@link #getService()} to determine if a service
509 * is being tracked.
510 *
511 * @param timeout The time interval in milliseconds to wait. If zero, the
512 * method will wait indefinitely.
513 * @return Returns the result of {@link #getService()}.
514 * @throws InterruptedException If another thread has interrupted the
515 * current thread.
516 * @throws IllegalArgumentException If the value of timeout is negative.
517 */
518 public Object waitForService(long timeout) throws InterruptedException {
519 if (timeout < 0) {
520 throw new IllegalArgumentException("timeout value is negative");
521 }
522 Object object = getService();
523 while (object == null) {
524 final Tracked t = tracked();
525 if (t == null) { /* if ServiceTracker is not open */
526 return null;
527 }
528 synchronized (t) {
529 if (t.size() == 0) {
530 t.wait(timeout);
531 }
532 }
533 object = getService();
534 if (timeout > 0) {
535 return object;
536 }
537 }
538 return object;
539 }
540
541 /**
542 * Return an array of <code>ServiceReference</code>s for all services being
543 * tracked by this <code>ServiceTracker</code>.
544 *
545 * @return Array of <code>ServiceReference</code>s or <code>null</code> if
546 * no services are being tracked.
547 */
548 public ServiceReference[] getServiceReferences() {
549 final Tracked t = tracked();
550 if (t == null) { /* if ServiceTracker is not open */
551 return null;
552 }
553 synchronized (t) {
554 int length = t.size();
555 if (length == 0) {
556 return null;
557 }
558 return (ServiceReference[]) t
559 .getTracked(new ServiceReference[length]);
560 }
561 }
562
563 /**
564 * Returns a <code>ServiceReference</code> for one of the services being
565 * tracked by this <code>ServiceTracker</code>.
566 *
567 * <p>
568 * If multiple services are being tracked, the service with the highest
569 * ranking (as specified in its <code>service.ranking</code> property) is
570 * returned. If there is a tie in ranking, the service with the lowest
571 * service ID (as specified in its <code>service.id</code> property); that
572 * is, the service that was registered first is returned. This is the same
573 * algorithm used by <code>BundleContext.getServiceReference</code>.
574 *
575 * <p>
576 * This implementation calls {@link #getServiceReferences()} to get the list
577 * of references for the tracked services.
578 *
579 * @return A <code>ServiceReference</code> or <code>null</code> if no
580 * services are being tracked.
581 * @since 1.1
582 */
583 public ServiceReference getServiceReference() {
584 ServiceReference reference = cachedReference;
585 if (reference != null) {
586 if (DEBUG) {
587 System.out
588 .println("ServiceTracker.getServiceReference[cached]: "
589 + filter);
590 }
591 return reference;
592 }
593 if (DEBUG) {
594 System.out.println("ServiceTracker.getServiceReference: " + filter);
595 }
596 ServiceReference[] references = getServiceReferences();
597 int length = (references == null) ? 0 : references.length;
598 if (length == 0) { /* if no service is being tracked */
599 return null;
600 }
601 int index = 0;
602 if (length > 1) { /* if more than one service, select highest ranking */
603 int rankings[] = new int[length];
604 int count = 0;
605 int maxRanking = Integer.MIN_VALUE;
606 for (int i = 0; i < length; i++) {
607 Object property = references[i]
608 .getProperty(Constants.SERVICE_RANKING);
609 int ranking = (property instanceof Integer) ? ((Integer) property)
610 .intValue()
611 : 0;
612 rankings[i] = ranking;
613 if (ranking > maxRanking) {
614 index = i;
615 maxRanking = ranking;
616 count = 1;
617 }
618 else {
619 if (ranking == maxRanking) {
620 count++;
621 }
622 }
623 }
624 if (count > 1) { /* if still more than one service, select lowest id */
625 long minId = Long.MAX_VALUE;
626 for (int i = 0; i < length; i++) {
627 if (rankings[i] == maxRanking) {
628 long id = ((Long) (references[i]
629 .getProperty(Constants.SERVICE_ID)))
630 .longValue();
631 if (id < minId) {
632 index = i;
633 minId = id;
634 }
635 }
636 }
637 }
638 }
639 return cachedReference = references[index];
640 }
641
642 /**
643 * Returns the service object for the specified
644 * <code>ServiceReference</code> if the specified referenced service is
645 * being tracked by this <code>ServiceTracker</code>.
646 *
647 * @param reference The reference to the desired service.
648 * @return A service object or <code>null</code> if the service referenced
649 * by the specified <code>ServiceReference</code> is not being
650 * tracked.
651 */
652 public Object getService(ServiceReference reference) {
653 final Tracked t = tracked();
654 if (t == null) { /* if ServiceTracker is not open */
655 return null;
656 }
657 synchronized (t) {
658 return t.getCustomizedObject(reference);
659 }
660 }
661
662 /**
663 * Return an array of service objects for all services being tracked by this
664 * <code>ServiceTracker</code>.
665 *
666 * <p>
667 * This implementation calls {@link #getServiceReferences()} to get the list
668 * of references for the tracked services and then calls
669 * {@link #getService(ServiceReference)} for each reference to get the
670 * tracked service object.
671 *
672 * @return An array of service objects or <code>null</code> if no services
673 * are being tracked.
674 */
675 public Object[] getServices() {
676 final Tracked t = tracked();
677 if (t == null) { /* if ServiceTracker is not open */
678 return null;
679 }
680 synchronized (t) {
681 ServiceReference[] references = getServiceReferences();
682 int length = (references == null) ? 0 : references.length;
683 if (length == 0) {
684 return null;
685 }
686 Object[] objects = new Object[length];
687 for (int i = 0; i < length; i++) {
688 objects[i] = getService(references[i]);
689 }
690 return objects;
691 }
692 }
693
694 /**
695 * Returns a service object for one of the services being tracked by this
696 * <code>ServiceTracker</code>.
697 *
698 * <p>
699 * If any services are being tracked, this implementation returns the result
700 * of calling <code>getService(getServiceReference())</code>.
701 *
702 * @return A service object or <code>null</code> if no services are being
703 * tracked.
704 */
705 public Object getService() {
706 Object service = cachedService;
707 if (service != null) {
708 if (DEBUG) {
709 System.out
710 .println("ServiceTracker.getService[cached]: "
711 + filter);
712 }
713 return service;
714 }
715 if (DEBUG) {
716 System.out.println("ServiceTracker.getService: " + filter);
717 }
718 ServiceReference reference = getServiceReference();
719 if (reference == null) {
720 return null;
721 }
722 return cachedService = getService(reference);
723 }
724
725 /**
726 * Remove a service from this <code>ServiceTracker</code>.
727 *
728 * The specified service will be removed from this
729 * <code>ServiceTracker</code>. If the specified service was being tracked
730 * then the <code>ServiceTrackerCustomizer.removedService</code> method will
731 * be called for that service.
732 *
733 * @param reference The reference to the service to be removed.
734 */
735 public void remove(ServiceReference reference) {
736 final Tracked t = tracked();
737 if (t == null) { /* if ServiceTracker is not open */
738 return;
739 }
740 t.untrack(reference, null);
741 }
742
743 /**
744 * Return the number of services being tracked by this
745 * <code>ServiceTracker</code>.
746 *
747 * @return The number of services being tracked.
748 */
749 public int size() {
750 final Tracked t = tracked();
751 if (t == null) { /* if ServiceTracker is not open */
752 return 0;
753 }
754 synchronized (t) {
755 return t.size();
756 }
757 }
758
759 /**
760 * Returns the tracking count for this <code>ServiceTracker</code>.
761 *
762 * The tracking count is initialized to 0 when this
763 * <code>ServiceTracker</code> is opened. Every time a service is added,
764 * modified or removed from this <code>ServiceTracker</code>, the tracking
765 * count is incremented.
766 *
767 * <p>
768 * The tracking count can be used to determine if this
769 * <code>ServiceTracker</code> has added, modified or removed a service by
770 * comparing a tracking count value previously collected with the current
771 * tracking count value. If the value has not changed, then no service has
772 * been added, modified or removed from this <code>ServiceTracker</code>
773 * since the previous tracking count was collected.
774 *
775 * @since 1.2
776 * @return The tracking count for this <code>ServiceTracker</code> or -1 if
777 * this <code>ServiceTracker</code> is not open.
778 */
779 public int getTrackingCount() {
780 final Tracked t = tracked();
781 if (t == null) { /* if ServiceTracker is not open */
782 return -1;
783 }
784 synchronized (t) {
785 return t.getTrackingCount();
786 }
787 }
788
789 /**
790 * Called by the Tracked object whenever the set of tracked services is
791 * modified. Clears the cache.
792 */
793 /*
794 * This method must not be synchronized since it is called by Tracked while
795 * Tracked is synchronized. We don't want synchronization interactions
796 * between the listener thread and the user thread.
797 */
798 void modified() {
799 cachedReference = null; /* clear cached value */
800 cachedService = null; /* clear cached value */
801 if (DEBUG) {
802 System.out.println("ServiceTracker.modified: " + filter);
803 }
804 }
805
806 /**
807 * Inner class which subclasses AbstractTracked. This class is the
808 * <code>ServiceListener</code> object for the tracker.
809 *
810 * @ThreadSafe
811 */
812 class Tracked extends AbstractTracked implements ServiceListener {
Marcel Offermans7f687482010-05-11 09:12:34 +0000813 /** A list of services that are currently hidden because there is an aspect available with a higher ranking. */
814 private final List m_hidden = new ArrayList();
Marcel Offermans1c944ec2010-10-11 13:55:02 +0000815
Marcel Offermans7f687482010-05-11 09:12:34 +0000816 /**
817 * Returns the highest hidden aspect for the specified service ID.
818 *
819 * @param serviceId the service ID
820 * @return a service reference, or <code>null</code> if there was no such service
821 */
822 private ServiceReference highestHidden(long serviceId) {
823 ServiceReference result = null;
824 int max = Integer.MIN_VALUE;
Marcel Offermansf9a7e342010-10-07 12:22:46 +0000825 synchronized (m_hidden) {
826 for (int i = 0; i < m_hidden.size(); i++) {
827 ServiceReference ref = (ServiceReference) m_hidden.get(i);
828 Long sid = (Long) ref.getProperty(Constants.SERVICE_ID);
829 Long aid = (Long) ref.getProperty(DependencyManager.ASPECT);
830 if ((aid != null && aid.longValue() == serviceId)
831 || (aid == null && sid != null && sid.longValue() == serviceId)) {
832 Integer ranking = (Integer) ref.getProperty(Constants.SERVICE_RANKING);
833 int r = 0;
834 if (ranking != null) {
835 r = ranking.intValue();
836 }
837 if (r > max) {
838 max = r;
839 result = ref;
840 }
841 }
842 }
Marcel Offermans7f687482010-05-11 09:12:34 +0000843 }
844 return result;
845 }
846
847 /**
848 * Returns the highest tracked service for the specified service ID.
849 *
850 * @param serviceId the service ID
851 * @return a service reference, or <code>null</code> if there was no such service
852 */
853 private ServiceReference highestTracked(long serviceId) {
854 ServiceReference result = null;
855 int max = Integer.MIN_VALUE;
856 Object[] trackedServices = getTracked(new Object[] {});
857 for (int i = 0; i < trackedServices.length; i++) {
858 ServiceReference ref = (ServiceReference) trackedServices[i];
859 Long sid = (Long) ref.getProperty(Constants.SERVICE_ID);
860 Long aid = (Long) ref.getProperty(DependencyManager.ASPECT);
861 if ((aid != null && aid.longValue() == serviceId)
862 || (aid == null && sid != null && sid.longValue() == serviceId)) {
863 Integer ranking = (Integer) ref.getProperty(Constants.SERVICE_RANKING);
864 int r = 0;
865 if (ranking != null) {
866 r = ranking.intValue();
867 }
868 if (r > max) {
869 max = r;
870 result = ref;
871 }
872 }
873 }
874 return result;
875 }
876
877 /**
878 * Hide a service reference, placing it in the list of hidden services.
879 *
880 * @param ref the service reference to add to the hidden list
881 */
882 private void hide(ServiceReference ref) {
Marcel Offermanse5187f62010-08-20 12:12:49 +0000883 if (DEBUG) { System.out.println("ServiceTracker.Tracked.hide " + ServiceUtil.toString(ref)); }
Marcel Offermansf9a7e342010-10-07 12:22:46 +0000884 synchronized (m_hidden) {
885 if (DEBUG) { if (m_hidden.contains(ref)) { System.out.println("ServiceTracker.Tracked.hide ERROR: " + ServiceUtil.toString(ref)); }};
886 m_hidden.add(ref);
887 }
Marcel Offermans7f687482010-05-11 09:12:34 +0000888 }
889
890 /**
891 * Unhide a service reference, removing it from the list of hidden services.
892 *
893 * @param ref the service reference to remove from the hidden list
894 */
895 private void unhide(ServiceReference ref) {
Marcel Offermanse5187f62010-08-20 12:12:49 +0000896 if (DEBUG) { System.out.println("ServiceTracker.Tracked.unhide " + ServiceUtil.toString(ref)); }
Marcel Offermansf9a7e342010-10-07 12:22:46 +0000897 synchronized (m_hidden) {
898 if (DEBUG) { if (!m_hidden.contains(ref)) { System.out.println("ServiceTracker.Tracked.unhide ERROR: " + ServiceUtil.toString(ref)); }};
899 m_hidden.remove(ref);
900 }
Marcel Offermans7f687482010-05-11 09:12:34 +0000901 }
902
Marcel Offermansc854d1a2009-11-24 15:41:41 +0000903 /**
904 * Tracked constructor.
905 */
906 Tracked() {
907 super();
908 }
Marcel Offermanse5187f62010-08-20 12:12:49 +0000909
910 void setInitial(Object[] list) {
911 if (list == null) {
912 return;
913 }
914 // we need to split this list into the highest matching service references for each aspect
915 // and a list of 'hidden' service references
916 int counter = list.length;
917 for (int i = 0; i < list.length; i++) {
918 ServiceReference sr = (ServiceReference) list[i];
919 if (sr != null) {
920 for (int j = 0; j < list.length; j++) {
921 ServiceReference sr2 = (ServiceReference) list[j];
922 if (sr2 != null && j != i) {
923 long sid = ServiceUtil.getServiceId(sr);
924 long sid2 = ServiceUtil.getServiceId(sr2);
925 if (sid == sid2) {
926 long r = ServiceUtil.getRanking(sr);
927 long r2 = ServiceUtil.getRanking(sr2);
928 if (r > r2) {
929 if (DEBUG) { System.out.println("ServiceTracker.Tracked.setInitial: hiding " + ServiceUtil.toString(sr2)); }
930 hide(sr2);
931 list[j] = null;
932 counter--;
933 }
934 else {
935 if (DEBUG) { System.out.println("ServiceTracker.Tracked.setInitial: hiding " + ServiceUtil.toString(sr)); }
936 hide(sr);
937 list[i] = null;
938 counter--;
939 break;
940 }
941 }
942 }
943 }
944 }
945 }
946 if (counter > 0) {
947 Object[] result = new Object[counter];
948 int index = 0;
949 for (int i = 0; i < list.length; i++) {
950 if (list[i] != null) {
951 if (DEBUG) { System.out.println("ServiceTracker.Tracked.setInitial: propagating " + ServiceUtil.toString((ServiceReference) list[i])); }
952 result[index] = list[i];
953 index++;
954 }
955 }
956 // we only invoke super if we actually have
957 // results in our initial list
958 super.setInitial(result);
959 }
960 }
Marcel Offermansc854d1a2009-11-24 15:41:41 +0000961
962 /**
963 * <code>ServiceListener</code> method for the
964 * <code>ServiceTracker</code> class. This method must NOT be
965 * synchronized to avoid deadlock potential.
966 *
967 * @param event <code>ServiceEvent</code> object from the framework.
968 */
969 public void serviceChanged(final ServiceEvent event) {
970 /*
971 * Check if we had a delayed call (which could happen when we
972 * close).
973 */
974 if (closed) {
975 return;
976 }
977 final ServiceReference reference = event.getServiceReference();
978 if (DEBUG) {
979 System.out
980 .println("ServiceTracker.Tracked.serviceChanged["
981 + event.getType() + "]: " + reference);
982 }
983
Marcel Offermans1c944ec2010-10-11 13:55:02 +0000984 long sid = ServiceUtil.getServiceId(reference);
Marcel Offermansc854d1a2009-11-24 15:41:41 +0000985 switch (event.getType()) {
986 case ServiceEvent.REGISTERED :
987 case ServiceEvent.MODIFIED :
Marcel Offermans7f687482010-05-11 09:12:34 +0000988 ServiceReference higher = null;
989 ServiceReference lower = null;
Marcel Offermans1c944ec2010-10-11 13:55:02 +0000990 ServiceReference sr = highestTracked(sid);
991 if (sr != null) {
992 int ranking = ServiceUtil.getRanking(reference);
993 int trackedRanking = ServiceUtil.getRanking(sr);
994 if (ranking > trackedRanking) {
995 // found a higher ranked one!
996 if (DEBUG) {
997 System.out.println("ServiceTracker.Tracked.serviceChanged[" + event.getType() + "]: Found a higher ranked aspect: " + ServiceUtil.toString(reference) + " vs " + ServiceUtil.toString(sr));
998 }
999 higher = sr;
1000 }
1001 else {
1002 // found lower ranked one!
1003 if (DEBUG) {
1004 System.out.println("ServiceTracker.Tracked.serviceChanged[" + event.getType() + "]: Found a lower ranked aspect: " + ServiceUtil.toString(reference) + " vs " + ServiceUtil.toString(sr));
1005 }
1006 lower = sr;
1007 }
Marcel Offermans7f687482010-05-11 09:12:34 +00001008 }
1009
1010 if (listenerFilter != null) { // service listener added with filter
1011 if (lower != null) {
1012 hide(reference);
1013 }
1014 else {
1015 try {
1016 track(reference, event);
1017 }
1018 finally {
1019 if (higher != null) {
1020 try {
1021 untrack(higher, null);
1022 }
1023 finally {
1024 hide(higher);
1025 }
1026 }
1027 }
1028 }
Marcel Offermansc854d1a2009-11-24 15:41:41 +00001029 /*
1030 * If the customizer throws an unchecked exception, it
1031 * is safe to let it propagate
1032 */
1033 }
1034 else { // service listener added without filter
1035 if (filter.match(reference)) {
Marcel Offermans7f687482010-05-11 09:12:34 +00001036 if (lower != null) {
1037 hide(reference);
1038 }
1039 else {
1040 try {
1041 track(reference, event);
1042 }
1043 finally {
1044 if (higher != null) {
1045 try {
1046 untrack(higher, null);
1047 }
1048 finally {
1049 hide(higher);
1050 }
1051 }
1052 }
1053 }
Marcel Offermansc854d1a2009-11-24 15:41:41 +00001054 /*
1055 * If the customizer throws an unchecked exception,
1056 * it is safe to let it propagate
1057 */
1058 }
1059 else {
Marcel Offermans1c944ec2010-10-11 13:55:02 +00001060 ServiceReference ht = highestTracked(sid);
1061 ServiceReference hh = highestHidden(sid);
1062 if (reference.equals(ht)) {
1063 try {
1064 if (hh != null) {
1065 unhide(hh);
1066 track(hh, null);
1067 }
1068 }
1069 finally {
1070 untrack(reference, event);
Marcel Offermans7f687482010-05-11 09:12:34 +00001071 }
1072 }
Marcel Offermans1c944ec2010-10-11 13:55:02 +00001073 else {
1074 unhide(reference);
Marcel Offermans7f687482010-05-11 09:12:34 +00001075 }
Marcel Offermansc854d1a2009-11-24 15:41:41 +00001076 /*
1077 * If the customizer throws an unchecked exception,
1078 * it is safe to let it propagate
1079 */
1080 }
1081 }
1082 break;
Marcel Offermans001db052009-12-08 08:58:40 +00001083 case 8 /* ServiceEvent.MODIFIED_ENDMATCH */ :
Marcel Offermansc854d1a2009-11-24 15:41:41 +00001084 case ServiceEvent.UNREGISTERING :
Marcel Offermans1c944ec2010-10-11 13:55:02 +00001085 ServiceReference ht = highestTracked(sid);
1086 ServiceReference hh = highestHidden(sid);
1087 if (reference.equals(ht)) {
1088 try {
1089 if (hh != null) {
1090 unhide(hh);
1091 track(hh, null);
1092 }
1093 }
1094 finally {
1095 untrack(reference, event);
1096 }
1097 }
1098 else {
1099 unhide(reference);
1100 }
Marcel Offermansc854d1a2009-11-24 15:41:41 +00001101 /*
1102 * If the customizer throws an unchecked exception, it is
1103 * safe to let it propagate
1104 */
1105 break;
1106 }
1107 }
1108
1109 /**
1110 * Increment the tracking count and tell the tracker there was a
1111 * modification.
1112 *
1113 * @GuardedBy this
1114 */
1115 void modified() {
1116 super.modified(); /* increment the modification count */
1117 ServiceTracker.this.modified();
1118 }
1119
1120 /**
1121 * Call the specific customizer adding method. This method must not be
1122 * called while synchronized on this object.
1123 *
1124 * @param item Item to be tracked.
1125 * @param related Action related object.
1126 * @return Customized object for the tracked item or <code>null</code>
1127 * if the item is not to be tracked.
1128 */
1129 Object customizerAdding(final Object item,
1130 final Object related) {
1131 return customizer.addingService((ServiceReference) item);
1132 }
1133
1134 void customizerAdded(final Object item, final Object related, final Object object) {
1135 customizer.addedService((ServiceReference) item, object);
1136 }
1137
1138 /**
1139 * Call the specific customizer modified method. This method must not be
1140 * called while synchronized on this object.
1141 *
1142 * @param item Tracked item.
1143 * @param related Action related object.
1144 * @param object Customized object for the tracked item.
1145 */
1146 void customizerModified(final Object item,
1147 final Object related, final Object object) {
1148 customizer.modifiedService((ServiceReference) item, object);
1149 }
1150
1151 /**
1152 * Call the specific customizer removed method. This method must not be
1153 * called while synchronized on this object.
1154 *
1155 * @param item Tracked item.
1156 * @param related Action related object.
1157 * @param object Customized object for the tracked item.
1158 */
1159 void customizerRemoved(final Object item,
1160 final Object related, final Object object) {
1161 customizer.removedService((ServiceReference) item, object);
1162 }
1163 }
1164
1165 /**
1166 * Subclass of Tracked which implements the AllServiceListener interface.
1167 * This class is used by the ServiceTracker if open is called with true.
1168 *
1169 * @since 1.3
1170 * @ThreadSafe
1171 */
1172 class AllTracked extends Tracked implements AllServiceListener {
1173 /**
1174 * AllTracked constructor.
1175 */
1176 AllTracked() {
1177 super();
1178 }
1179 }
1180}