1 /* 2 * Copyright (c) OSGi Alliance (2000, 2017). All Rights Reserved. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package org.osgi.util.tracker; 18 19 import java.lang.reflect.Array; 20 import java.util.Collections; 21 import java.util.SortedMap; 22 import java.util.TreeMap; 23 24 import org.osgi.annotation.versioning.ConsumerType; 25 import org.osgi.framework.AllServiceListener; 26 import org.osgi.framework.BundleContext; 27 import org.osgi.framework.Constants; 28 import org.osgi.framework.Filter; 29 import org.osgi.framework.InvalidSyntaxException; 30 import org.osgi.framework.ServiceEvent; 31 import org.osgi.framework.ServiceListener; 32 import org.osgi.framework.ServiceReference; 33 34 /** 35 * The {@code ServiceTracker} class simplifies using services from the 36 * Framework's service registry. 37 * <p> 38 * A {@code ServiceTracker} object is constructed with search criteria and a 39 * {@code ServiceTrackerCustomizer} object. A {@code ServiceTracker} can use a 40 * {@code ServiceTrackerCustomizer} to customize the service objects to be 41 * tracked. The {@code ServiceTracker} can then be opened to begin tracking all 42 * services in the Framework's service registry that match the specified search 43 * criteria. The {@code ServiceTracker} correctly handles all of the details of 44 * listening to {@code ServiceEvent}s and getting and ungetting services. 45 * <p> 46 * The {@code getServiceReferences} method can be called to get references to 47 * the services being tracked. The {@code getService} and {@code getServices} 48 * methods can be called to get the service objects for the tracked service. 49 * <p> 50 * The {@code ServiceTracker} class is thread-safe. It does not call a 51 * {@code ServiceTrackerCustomizer} while holding any locks. 52 * {@code ServiceTrackerCustomizer} implementations must also be thread-safe. 53 * 54 * @param <S> The type of the service being tracked. 55 * @param <T> The type of the tracked object. 56 * @ThreadSafe 57 * @author $Id: 3c9016c43c6289259f97470eff4c9986b6fb887a $ 58 */ 59 @ConsumerType 60 public class ServiceTracker<S, T> implements ServiceTrackerCustomizer<S, T> { 61 /* set this to true to compile in debug messages */ 62 static final boolean DEBUG = false; 63 /** 64 * The Bundle Context used by this {@code ServiceTracker}. 65 */ 66 protected final BundleContext context; 67 /** 68 * The Filter used by this {@code ServiceTracker} which specifies the search 69 * criteria for the services to track. 70 * 71 * @since 1.1 72 */ 73 protected final Filter filter; 74 /** 75 * The {@code ServiceTrackerCustomizer} for this tracker. 76 */ 77 final ServiceTrackerCustomizer<S, T> customizer; 78 /** 79 * Filter string for use when adding the ServiceListener. If this field is 80 * set, then certain optimizations can be taken since we don't have a user 81 * supplied filter. 82 */ 83 final String listenerFilter; 84 /** 85 * Class name to be tracked. If this field is set, then we are tracking by 86 * class name. 87 */ 88 private final String trackClass; 89 /** 90 * Reference to be tracked. If this field is set, then we are tracking a 91 * single ServiceReference. 92 */ 93 private final ServiceReference<S> trackReference; 94 /** 95 * Tracked services: {@code ServiceReference} -> customized Object and 96 * {@code ServiceListener} object 97 */ 98 private volatile Tracked tracked; 99 100 /** 101 * Accessor method for the current Tracked object. This method is only 102 * intended to be used by the unsynchronized methods which do not modify the 103 * tracked field. 104 * 105 * @return The current Tracked object. 106 */ tracked()107 private Tracked tracked() { 108 return tracked; 109 } 110 111 /** 112 * Cached ServiceReference for getServiceReference. 113 * 114 * This field is volatile since it is accessed by multiple threads. 115 */ 116 private volatile ServiceReference<S> cachedReference; 117 /** 118 * Cached service object for getService. 119 * 120 * This field is volatile since it is accessed by multiple threads. 121 */ 122 private volatile T cachedService; 123 124 /** 125 * Create a {@code ServiceTracker} on the specified {@code ServiceReference} 126 * . 127 * 128 * <p> 129 * The service referenced by the specified {@code ServiceReference} will be 130 * tracked by this {@code ServiceTracker}. 131 * 132 * @param context The {@code BundleContext} against which the tracking is 133 * done. 134 * @param reference The {@code ServiceReference} for the service to be 135 * tracked. 136 * @param customizer The customizer object to call when services are added, 137 * modified, or removed in this {@code ServiceTracker}. If customizer 138 * is {@code null}, then this {@code ServiceTracker} will be used as 139 * the {@code ServiceTrackerCustomizer} and this 140 * {@code ServiceTracker} will call the 141 * {@code ServiceTrackerCustomizer} methods on itself. 142 */ ServiceTracker(final BundleContext context, final ServiceReference<S> reference, final ServiceTrackerCustomizer<S, T> customizer)143 public ServiceTracker(final BundleContext context, final ServiceReference<S> reference, final ServiceTrackerCustomizer<S, T> customizer) { 144 this.context = context; 145 this.trackReference = reference; 146 this.trackClass = null; 147 this.customizer = (customizer == null) ? this : customizer; 148 this.listenerFilter = "(" + Constants.SERVICE_ID + "=" + reference.getProperty(Constants.SERVICE_ID).toString() + ")"; 149 try { 150 this.filter = context.createFilter(listenerFilter); 151 } catch (InvalidSyntaxException e) { 152 /* 153 * we could only get this exception if the ServiceReference was 154 * invalid 155 */ 156 IllegalArgumentException iae = new IllegalArgumentException("unexpected InvalidSyntaxException: " + e.getMessage()); 157 iae.initCause(e); 158 throw iae; 159 } 160 } 161 162 /** 163 * Create a {@code ServiceTracker} on the specified class name. 164 * 165 * <p> 166 * Services registered under the specified class name will be tracked by 167 * this {@code ServiceTracker}. 168 * 169 * @param context The {@code BundleContext} against which the tracking is 170 * done. 171 * @param clazz The class name of the services to be tracked. 172 * @param customizer The customizer object to call when services are added, 173 * modified, or removed in this {@code ServiceTracker}. If customizer 174 * is {@code null}, then this {@code ServiceTracker} will be used as 175 * the {@code ServiceTrackerCustomizer} and this 176 * {@code ServiceTracker} will call the 177 * {@code ServiceTrackerCustomizer} methods on itself. 178 */ ServiceTracker(final BundleContext context, final String clazz, final ServiceTrackerCustomizer<S, T> customizer)179 public ServiceTracker(final BundleContext context, final String clazz, final ServiceTrackerCustomizer<S, T> customizer) { 180 this.context = context; 181 this.trackReference = null; 182 this.trackClass = clazz; 183 this.customizer = (customizer == null) ? this : customizer; 184 // we call clazz.toString to verify clazz is non-null! 185 this.listenerFilter = "(" + Constants.OBJECTCLASS + "=" + clazz.toString() + ")"; 186 try { 187 this.filter = context.createFilter(listenerFilter); 188 } catch (InvalidSyntaxException e) { 189 /* 190 * we could only get this exception if the clazz argument was 191 * malformed 192 */ 193 IllegalArgumentException iae = new IllegalArgumentException("unexpected InvalidSyntaxException: " + e.getMessage()); 194 iae.initCause(e); 195 throw iae; 196 } 197 } 198 199 /** 200 * Create a {@code ServiceTracker} on the specified {@code Filter} object. 201 * 202 * <p> 203 * Services which match the specified {@code Filter} object will be tracked 204 * by this {@code ServiceTracker}. 205 * 206 * @param context The {@code BundleContext} against which the tracking is 207 * done. 208 * @param filter The {@code Filter} to select the services to be tracked. 209 * @param customizer The customizer object to call when services are added, 210 * modified, or removed in this {@code ServiceTracker}. If customizer 211 * is null, then this {@code ServiceTracker} will be used as the 212 * {@code ServiceTrackerCustomizer} and this {@code ServiceTracker} 213 * will call the {@code ServiceTrackerCustomizer} methods on itself. 214 * @since 1.1 215 */ ServiceTracker(final BundleContext context, final Filter filter, final ServiceTrackerCustomizer<S, T> customizer)216 public ServiceTracker(final BundleContext context, final Filter filter, final ServiceTrackerCustomizer<S, T> customizer) { 217 this.context = context; 218 this.trackReference = null; 219 this.trackClass = null; 220 this.listenerFilter = filter.toString(); 221 this.filter = filter; 222 this.customizer = (customizer == null) ? this : customizer; 223 if ((context == null) || (filter == null)) { 224 /* 225 * we throw a NPE here to be consistent with the other constructors 226 */ 227 throw new NullPointerException(); 228 } 229 } 230 231 /** 232 * Create a {@code ServiceTracker} on the specified class. 233 * 234 * <p> 235 * Services registered under the name of the specified class will be tracked 236 * by this {@code ServiceTracker}. 237 * 238 * @param context The {@code BundleContext} against which the tracking is 239 * done. 240 * @param clazz The class of the services to be tracked. 241 * @param customizer The customizer object to call when services are added, 242 * modified, or removed in this {@code ServiceTracker}. If customizer 243 * is {@code null}, then this {@code ServiceTracker} will be used as 244 * the {@code ServiceTrackerCustomizer} and this 245 * {@code ServiceTracker} will call the 246 * {@code ServiceTrackerCustomizer} methods on itself. 247 * @since 1.5 248 */ ServiceTracker(final BundleContext context, final Class<S> clazz, final ServiceTrackerCustomizer<S, T> customizer)249 public ServiceTracker(final BundleContext context, final Class<S> clazz, final ServiceTrackerCustomizer<S, T> customizer) { 250 this(context, clazz.getName(), customizer); 251 } 252 253 /** 254 * Open this {@code ServiceTracker} and begin tracking services. 255 * 256 * <p> 257 * This implementation calls {@code open(false)}. 258 * 259 * @throws java.lang.IllegalStateException If the {@code BundleContext} with 260 * which this {@code ServiceTracker} was created is no longer valid. 261 * @see #open(boolean) 262 */ open()263 public void open() { 264 open(false); 265 } 266 267 /** 268 * Open this {@code ServiceTracker} and begin tracking services. 269 * 270 * <p> 271 * Services which match the search criteria specified when this 272 * {@code ServiceTracker} was created are now tracked by this 273 * {@code ServiceTracker}. 274 * 275 * @param trackAllServices If {@code true}, then this {@code ServiceTracker} 276 * will track all matching services regardless of class loader 277 * accessibility. If {@code false}, then this {@code ServiceTracker} 278 * will only track matching services which are class loader 279 * accessible to the bundle whose {@code BundleContext} is used by 280 * this {@code ServiceTracker}. 281 * @throws java.lang.IllegalStateException If the {@code BundleContext} with 282 * which this {@code ServiceTracker} was created is no longer valid. 283 * @since 1.3 284 */ open(boolean trackAllServices)285 public void open(boolean trackAllServices) { 286 final Tracked t; 287 synchronized (this) { 288 if (tracked != null) { 289 return; 290 } 291 if (DEBUG) { 292 System.out.println("ServiceTracker.open: " + filter); 293 } 294 t = trackAllServices ? new AllTracked() : new Tracked(); 295 synchronized (t) { 296 try { 297 context.addServiceListener(t, listenerFilter); 298 ServiceReference<S>[] references = null; 299 if (trackClass != null) { 300 references = getInitialReferences(trackAllServices, trackClass, null); 301 } else { 302 if (trackReference != null) { 303 if (trackReference.getBundle() != null) { 304 @SuppressWarnings("unchecked") 305 ServiceReference<S>[] single = new ServiceReference[] {trackReference}; 306 references = single; 307 } 308 } else { /* user supplied filter */ 309 references = getInitialReferences(trackAllServices, null, listenerFilter); 310 } 311 } 312 /* set tracked with the initial references */ 313 t.setInitial(references); 314 } catch (InvalidSyntaxException e) { 315 throw new RuntimeException("unexpected InvalidSyntaxException: " + e.getMessage(), e); 316 } 317 } 318 tracked = t; 319 } 320 /* Call tracked outside of synchronized region */ 321 t.trackInitial(); /* process the initial references */ 322 } 323 324 /** 325 * Returns the list of initial {@code ServiceReference}s that will be 326 * tracked by this {@code ServiceTracker}. 327 * 328 * @param trackAllServices If {@code true}, use 329 * {@code getAllServiceReferences}. 330 * @param className The class name with which the service was registered, or 331 * {@code null} for all services. 332 * @param filterString The filter criteria or {@code null} for all services. 333 * @return The list of initial {@code ServiceReference}s. 334 * @throws InvalidSyntaxException If the specified filterString has an 335 * invalid syntax. 336 */ getInitialReferences(boolean trackAllServices, String className, String filterString)337 private ServiceReference<S>[] getInitialReferences(boolean trackAllServices, String className, String filterString) throws InvalidSyntaxException { 338 @SuppressWarnings("unchecked") 339 ServiceReference<S>[] result = (ServiceReference<S>[]) ((trackAllServices) ? context.getAllServiceReferences(className, filterString) : context.getServiceReferences(className, filterString)); 340 return result; 341 } 342 343 /** 344 * Close this {@code ServiceTracker}. 345 * 346 * <p> 347 * This method should be called when this {@code ServiceTracker} should end 348 * the tracking of services. 349 * 350 * <p> 351 * This implementation calls {@link #getServiceReferences()} to get the list 352 * of tracked services to remove. 353 */ close()354 public void close() { 355 final Tracked outgoing; 356 final ServiceReference<S>[] references; 357 synchronized (this) { 358 outgoing = tracked; 359 if (outgoing == null) { 360 return; 361 } 362 if (DEBUG) { 363 System.out.println("ServiceTracker.close: " + filter); 364 } 365 outgoing.close(); 366 references = getServiceReferences(); 367 tracked = null; 368 try { 369 context.removeServiceListener(outgoing); 370 } catch (IllegalStateException e) { 371 /* In case the context was stopped. */ 372 } 373 } 374 modified(); /* clear the cache */ 375 synchronized (outgoing) { 376 outgoing.notifyAll(); /* wake up any waiters */ 377 } 378 if (references != null) { 379 for (int i = 0; i < references.length; i++) { 380 outgoing.untrack(references[i], null); 381 } 382 } 383 if (DEBUG) { 384 if ((cachedReference == null) && (cachedService == null)) { 385 System.out.println("ServiceTracker.close[cached cleared]: " + filter); 386 } 387 } 388 } 389 390 /** 391 * Default implementation of the 392 * {@code ServiceTrackerCustomizer.addingService} method. 393 * <p> 394 * This method is only called when this {@code ServiceTracker} has been 395 * constructed with a {@code null ServiceTrackerCustomizer} argument. 396 * <p> 397 * This implementation returns the result of calling {@code getService}, on 398 * the {@code BundleContext} with which this {@code ServiceTracker} was 399 * created, passing the specified {@code ServiceReference}. 400 * <p> 401 * This method can be overridden in a subclass to customize the service 402 * object to be tracked for the service being added. In that case, take care 403 * not to rely on the default implementation of 404 * {@link #removedService(ServiceReference, Object) removedService} to unget 405 * the service. 406 * 407 * @param reference The reference to the service being added to this 408 * {@code ServiceTracker}. 409 * @return The service object to be tracked for the service added to this 410 * {@code ServiceTracker}. 411 * @see ServiceTrackerCustomizer#addingService(ServiceReference) 412 */ 413 @Override addingService(ServiceReference<S> reference)414 public T addingService(ServiceReference<S> reference) { 415 @SuppressWarnings("unchecked") 416 T result = (T) context.getService(reference); 417 return result; 418 } 419 420 /** 421 * Default implementation of the 422 * {@code ServiceTrackerCustomizer.modifiedService} method. 423 * 424 * <p> 425 * This method is only called when this {@code ServiceTracker} has been 426 * constructed with a {@code null ServiceTrackerCustomizer} argument. 427 * 428 * <p> 429 * This implementation does nothing. 430 * 431 * @param reference The reference to modified service. 432 * @param service The service object for the modified service. 433 * @see ServiceTrackerCustomizer#modifiedService(ServiceReference, Object) 434 */ 435 @Override modifiedService(ServiceReference<S> reference, T service)436 public void modifiedService(ServiceReference<S> reference, T service) { 437 /* do nothing */ 438 } 439 440 /** 441 * Default implementation of the 442 * {@code ServiceTrackerCustomizer.removedService} method. 443 * <p> 444 * This method is only called when this {@code ServiceTracker} has been 445 * constructed with a {@code null ServiceTrackerCustomizer} argument. 446 * <p> 447 * This implementation calls {@code ungetService}, on the 448 * {@code BundleContext} with which this {@code ServiceTracker} was created, 449 * passing the specified {@code ServiceReference}. 450 * <p> 451 * This method can be overridden in a subclass. If the default 452 * implementation of {@link #addingService(ServiceReference) addingService} 453 * method was used, this method must unget the service. 454 * 455 * @param reference The reference to removed service. 456 * @param service The service object for the removed service. 457 * @see ServiceTrackerCustomizer#removedService(ServiceReference, Object) 458 */ 459 @Override removedService(ServiceReference<S> reference, T service)460 public void removedService(ServiceReference<S> reference, T service) { 461 context.ungetService(reference); 462 } 463 464 /** 465 * Wait for at least one service to be tracked by this 466 * {@code ServiceTracker}. This method will also return when this 467 * {@code ServiceTracker} is closed. 468 * 469 * <p> 470 * It is strongly recommended that {@code waitForService} is not used during 471 * the calling of the {@code BundleActivator} methods. 472 * {@code BundleActivator} methods are expected to complete in a short 473 * period of time. 474 * 475 * <p> 476 * This implementation calls {@link #getService()} to determine if a service 477 * is being tracked. 478 * 479 * @param timeout The time interval in milliseconds to wait. If zero, the 480 * method will wait indefinitely. 481 * @return Returns the result of {@link #getService()}. 482 * @throws InterruptedException If another thread has interrupted the 483 * current thread. 484 * @throws IllegalArgumentException If the value of timeout is negative. 485 */ waitForService(long timeout)486 public T waitForService(long timeout) throws InterruptedException { 487 if (timeout < 0) { 488 throw new IllegalArgumentException("timeout value is negative"); 489 } 490 491 T object = getService(); 492 if (object != null) { 493 return object; 494 } 495 496 final long endTime = (timeout == 0) ? 0 : (System.currentTimeMillis() + timeout); 497 do { 498 final Tracked t = tracked(); 499 if (t == null) { /* if ServiceTracker is not open */ 500 return null; 501 } 502 synchronized (t) { 503 if (t.size() == 0) { 504 t.wait(timeout); 505 } 506 } 507 object = getService(); 508 if (endTime > 0) { // if we have a timeout 509 timeout = endTime - System.currentTimeMillis(); 510 if (timeout <= 0) { // that has expired 511 break; 512 } 513 } 514 } while (object == null); 515 return object; 516 } 517 518 /** 519 * Return an array of {@code ServiceReference}s for all services being 520 * tracked by this {@code ServiceTracker}. 521 * 522 * @return Array of {@code ServiceReference}s or {@code null} if no services 523 * are being tracked. 524 */ getServiceReferences()525 public ServiceReference<S>[] getServiceReferences() { 526 final Tracked t = tracked(); 527 if (t == null) { /* if ServiceTracker is not open */ 528 return null; 529 } 530 synchronized (t) { 531 if (t.isEmpty()) { 532 return null; 533 } 534 @SuppressWarnings("unchecked") 535 ServiceReference<S>[] result = new ServiceReference[0]; 536 return t.copyKeys(result); 537 } 538 } 539 540 /** 541 * Returns a {@code ServiceReference} for one of the services being tracked 542 * by this {@code ServiceTracker}. 543 * 544 * <p> 545 * If multiple services are being tracked, the service with the highest 546 * ranking (as specified in its {@code service.ranking} property) is 547 * returned. If there is a tie in ranking, the service with the lowest 548 * service id (as specified in its {@code service.id} property); that is, 549 * the service that was registered first is returned. This is the same 550 * algorithm used by {@code BundleContext.getServiceReference}. 551 * 552 * <p> 553 * This implementation calls {@link #getServiceReferences()} to get the list 554 * of references for the tracked services. 555 * 556 * @return A {@code ServiceReference} or {@code null} if no services are 557 * being tracked. 558 * @since 1.1 559 */ getServiceReference()560 public ServiceReference<S> getServiceReference() { 561 ServiceReference<S> reference = cachedReference; 562 if (reference != null) { 563 if (DEBUG) { 564 System.out.println("ServiceTracker.getServiceReference[cached]: " + filter); 565 } 566 return reference; 567 } 568 if (DEBUG) { 569 System.out.println("ServiceTracker.getServiceReference: " + filter); 570 } 571 ServiceReference<S>[] references = getServiceReferences(); 572 int length = (references == null) ? 0 : references.length; 573 if (length == 0) { /* if no service is being tracked */ 574 return null; 575 } 576 int index = 0; 577 if (length > 1) { /* if more than one service, select highest ranking */ 578 int rankings[] = new int[length]; 579 int count = 0; 580 int maxRanking = Integer.MIN_VALUE; 581 for (int i = 0; i < length; i++) { 582 Object property = references[i].getProperty(Constants.SERVICE_RANKING); 583 int ranking = (property instanceof Integer) ? ((Integer) property).intValue() : 0; 584 rankings[i] = ranking; 585 if (ranking > maxRanking) { 586 index = i; 587 maxRanking = ranking; 588 count = 1; 589 } else { 590 if (ranking == maxRanking) { 591 count++; 592 } 593 } 594 } 595 if (count > 1) { /* if still more than one service, select lowest id */ 596 long minId = Long.MAX_VALUE; 597 for (int i = 0; i < length; i++) { 598 if (rankings[i] == maxRanking) { 599 long id = ((Long) (references[i].getProperty(Constants.SERVICE_ID))).longValue(); 600 if (id < minId) { 601 index = i; 602 minId = id; 603 } 604 } 605 } 606 } 607 } 608 return cachedReference = references[index]; 609 } 610 611 /** 612 * Returns the service object for the specified {@code ServiceReference} if 613 * the specified referenced service is being tracked by this 614 * {@code ServiceTracker}. 615 * 616 * @param reference The reference to the desired service. 617 * @return A service object or {@code null} if the service referenced by the 618 * specified {@code ServiceReference} is not being tracked. 619 */ getService(ServiceReference<S> reference)620 public T getService(ServiceReference<S> reference) { 621 final Tracked t = tracked(); 622 if (t == null) { /* if ServiceTracker is not open */ 623 return null; 624 } 625 synchronized (t) { 626 return t.getCustomizedObject(reference); 627 } 628 } 629 630 /** 631 * Return an array of service objects for all services being tracked by this 632 * {@code ServiceTracker}. 633 * 634 * <p> 635 * This implementation calls {@link #getServiceReferences()} to get the list 636 * of references for the tracked services and then calls 637 * {@link #getService(ServiceReference)} for each reference to get the 638 * tracked service object. 639 * 640 * @return An array of service objects or {@code null} if no services are 641 * being tracked. 642 */ getServices()643 public Object[] getServices() { 644 final Tracked t = tracked(); 645 if (t == null) { /* if ServiceTracker is not open */ 646 return null; 647 } 648 synchronized (t) { 649 ServiceReference<S>[] references = getServiceReferences(); 650 int length = (references == null) ? 0 : references.length; 651 if (length == 0) { 652 return null; 653 } 654 Object[] objects = new Object[length]; 655 for (int i = 0; i < length; i++) { 656 objects[i] = getService(references[i]); 657 } 658 return objects; 659 } 660 } 661 662 /** 663 * Returns a service object for one of the services being tracked by this 664 * {@code ServiceTracker}. 665 * 666 * <p> 667 * If any services are being tracked, this implementation returns the result 668 * of calling {@code getService(getServiceReference())}. 669 * 670 * @return A service object or {@code null} if no services are being 671 * tracked. 672 */ getService()673 public T getService() { 674 T service = cachedService; 675 if (service != null) { 676 if (DEBUG) { 677 System.out.println("ServiceTracker.getService[cached]: " + filter); 678 } 679 return service; 680 } 681 if (DEBUG) { 682 System.out.println("ServiceTracker.getService: " + filter); 683 } 684 ServiceReference<S> reference = getServiceReference(); 685 if (reference == null) { 686 return null; 687 } 688 return cachedService = getService(reference); 689 } 690 691 /** 692 * Remove a service from this {@code ServiceTracker}. 693 * 694 * The specified service will be removed from this {@code ServiceTracker}. 695 * If the specified service was being tracked then the 696 * {@code ServiceTrackerCustomizer.removedService} method will be called for 697 * that service. 698 * 699 * @param reference The reference to the service to be removed. 700 */ remove(ServiceReference<S> reference)701 public void remove(ServiceReference<S> reference) { 702 final Tracked t = tracked(); 703 if (t == null) { /* if ServiceTracker is not open */ 704 return; 705 } 706 t.untrack(reference, null); 707 } 708 709 /** 710 * Return the number of services being tracked by this 711 * {@code ServiceTracker}. 712 * 713 * @return The number of services being tracked. 714 */ size()715 public int size() { 716 final Tracked t = tracked(); 717 if (t == null) { /* if ServiceTracker is not open */ 718 return 0; 719 } 720 synchronized (t) { 721 return t.size(); 722 } 723 } 724 725 /** 726 * Returns the tracking count for this {@code ServiceTracker}. 727 * 728 * The tracking count is initialized to 0 when this {@code ServiceTracker} 729 * is opened. Every time a service is added, modified or removed from this 730 * {@code ServiceTracker}, the tracking count is incremented. 731 * 732 * <p> 733 * The tracking count can be used to determine if this 734 * {@code ServiceTracker} has added, modified or removed a service by 735 * comparing a tracking count value previously collected with the current 736 * tracking count value. If the value has not changed, then no service has 737 * been added, modified or removed from this {@code ServiceTracker} since 738 * the previous tracking count was collected. 739 * 740 * @since 1.2 741 * @return The tracking count for this {@code ServiceTracker} or -1 if this 742 * {@code ServiceTracker} is not open. 743 */ getTrackingCount()744 public int getTrackingCount() { 745 final Tracked t = tracked(); 746 if (t == null) { /* if ServiceTracker is not open */ 747 return -1; 748 } 749 synchronized (t) { 750 return t.getTrackingCount(); 751 } 752 } 753 754 /** 755 * Called by the Tracked object whenever the set of tracked services is 756 * modified. Clears the cache. 757 */ 758 /* 759 * This method must not be synchronized since it is called by Tracked while 760 * Tracked is synchronized. We don't want synchronization interactions 761 * between the listener thread and the user thread. 762 */ modified()763 void modified() { 764 cachedReference = null; /* clear cached value */ 765 cachedService = null; /* clear cached value */ 766 if (DEBUG) { 767 System.out.println("ServiceTracker.modified: " + filter); 768 } 769 } 770 771 /** 772 * Return a {@code SortedMap} of the {@code ServiceReference}s and service 773 * objects for all services being tracked by this {@code ServiceTracker}. 774 * The map is sorted in reverse natural order of {@code ServiceReference}. 775 * That is, the first entry is the service with the highest ranking and the 776 * lowest service id. 777 * 778 * @return A {@code SortedMap} with the {@code ServiceReference}s and 779 * service objects for all services being tracked by this 780 * {@code ServiceTracker}. If no services are being tracked, then 781 * the returned map is empty. 782 * @since 1.5 783 */ getTracked()784 public SortedMap<ServiceReference<S>, T> getTracked() { 785 SortedMap<ServiceReference<S>, T> map = new TreeMap<ServiceReference<S>, T>(Collections.reverseOrder()); 786 final Tracked t = tracked(); 787 if (t == null) { /* if ServiceTracker is not open */ 788 return map; 789 } 790 synchronized (t) { 791 return t.copyEntries(map); 792 } 793 } 794 795 /** 796 * Return if this {@code ServiceTracker} is empty. 797 * 798 * @return {@code true} if this {@code ServiceTracker} is not tracking any 799 * services. 800 * @since 1.5 801 */ isEmpty()802 public boolean isEmpty() { 803 final Tracked t = tracked(); 804 if (t == null) { /* if ServiceTracker is not open */ 805 return true; 806 } 807 synchronized (t) { 808 return t.isEmpty(); 809 } 810 } 811 812 /** 813 * Return an array of service objects for all services being tracked by this 814 * {@code ServiceTracker}. The runtime type of the returned array is that of 815 * the specified array. 816 * 817 * <p> 818 * This implementation calls {@link #getServiceReferences()} to get the list 819 * of references for the tracked services and then calls 820 * {@link #getService(ServiceReference)} for each reference to get the 821 * tracked service object. 822 * 823 * @param array An array into which the tracked service objects will be 824 * stored, if the array is large enough. 825 * @return An array of service objects being tracked. If the specified array 826 * is large enough to hold the result, then the specified array is 827 * returned. If the specified array is longer then necessary to hold 828 * the result, the array element after the last service object is 829 * set to {@code null}. If the specified array is not large enough 830 * to hold the result, a new array is created and returned. 831 * @since 1.5 832 */ getServices(T[] array)833 public T[] getServices(T[] array) { 834 final Tracked t = tracked(); 835 if (t == null) { /* if ServiceTracker is not open */ 836 if (array.length > 0) { 837 array[0] = null; 838 } 839 return array; 840 } 841 synchronized (t) { 842 ServiceReference<S>[] references = getServiceReferences(); 843 int length = (references == null) ? 0 : references.length; 844 if (length == 0) { 845 if (array.length > 0) { 846 array[0] = null; 847 } 848 return array; 849 } 850 if (length > array.length) { 851 @SuppressWarnings("unchecked") 852 T[] newInstance = (T[]) Array.newInstance(array.getClass().getComponentType(), length); 853 array = newInstance; 854 } 855 for (int i = 0; i < length; i++) { 856 array[i] = getService(references[i]); 857 } 858 if (array.length > length) { 859 array[length] = null; 860 } 861 return array; 862 } 863 } 864 865 /** 866 * Inner class which subclasses AbstractTracked. This class is the 867 * {@code ServiceListener} object for the tracker. 868 * 869 * @ThreadSafe 870 */ 871 private class Tracked extends AbstractTracked<ServiceReference<S>, T, ServiceEvent> implements ServiceListener { 872 /** 873 * Tracked constructor. 874 */ Tracked()875 Tracked() { 876 super(); 877 } 878 879 /** 880 * {@code ServiceListener} method for the {@code ServiceTracker} class. 881 * This method must NOT be synchronized to avoid deadlock potential. 882 * 883 * @param event {@code ServiceEvent} object from the framework. 884 */ 885 @Override serviceChanged(final ServiceEvent event)886 final public void serviceChanged(final ServiceEvent event) { 887 /* 888 * Check if we had a delayed call (which could happen when we 889 * close). 890 */ 891 if (closed) { 892 return; 893 } 894 @SuppressWarnings("unchecked") 895 final ServiceReference<S> reference = (ServiceReference<S>) event.getServiceReference(); 896 if (DEBUG) { 897 System.out.println("ServiceTracker.Tracked.serviceChanged[" + event.getType() + "]: " + reference); 898 } 899 900 switch (event.getType()) { 901 case ServiceEvent.REGISTERED : 902 case ServiceEvent.MODIFIED : 903 track(reference, event); 904 /* 905 * If the customizer throws an unchecked exception, it is 906 * safe to let it propagate 907 */ 908 break; 909 case ServiceEvent.MODIFIED_ENDMATCH : 910 case ServiceEvent.UNREGISTERING : 911 untrack(reference, event); 912 /* 913 * If the customizer throws an unchecked exception, it is 914 * safe to let it propagate 915 */ 916 break; 917 } 918 } 919 920 /** 921 * Increment the tracking count and tell the tracker there was a 922 * modification. 923 * 924 * @GuardedBy this 925 */ 926 @Override modified()927 final void modified() { 928 super.modified(); /* increment the modification count */ 929 ServiceTracker.this.modified(); 930 } 931 932 /** 933 * Call the specific customizer adding method. This method must not be 934 * called while synchronized on this object. 935 * 936 * @param item Item to be tracked. 937 * @param related Action related object. 938 * @return Customized object for the tracked item or {@code null} if the 939 * item is not to be tracked. 940 */ 941 @Override customizerAdding(final ServiceReference<S> item, final ServiceEvent related)942 final T customizerAdding(final ServiceReference<S> item, final ServiceEvent related) { 943 return customizer.addingService(item); 944 } 945 946 /** 947 * Call the specific customizer modified method. This method must not be 948 * called while synchronized on this object. 949 * 950 * @param item Tracked item. 951 * @param related Action related object. 952 * @param object Customized object for the tracked item. 953 */ 954 @Override customizerModified(final ServiceReference<S> item, final ServiceEvent related, final T object)955 final void customizerModified(final ServiceReference<S> item, final ServiceEvent related, final T object) { 956 customizer.modifiedService(item, object); 957 } 958 959 /** 960 * Call the specific customizer removed method. This method must not be 961 * called while synchronized on this object. 962 * 963 * @param item Tracked item. 964 * @param related Action related object. 965 * @param object Customized object for the tracked item. 966 */ 967 @Override customizerRemoved(final ServiceReference<S> item, final ServiceEvent related, final T object)968 final void customizerRemoved(final ServiceReference<S> item, final ServiceEvent related, final T object) { 969 customizer.removedService(item, object); 970 } 971 } 972 973 /** 974 * Subclass of Tracked which implements the AllServiceListener interface. 975 * This class is used by the ServiceTracker if open is called with true. 976 * 977 * @since 1.3 978 * @ThreadSafe 979 */ 980 private class AllTracked extends Tracked implements AllServiceListener { 981 /** 982 * AllTracked constructor. 983 */ AllTracked()984 AllTracked() { 985 super(); 986 } 987 } 988 } 989