00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 package org.osgi.util.tracker;
00020
00021 import java.util.*;
00022
00023 import org.osgi.framework.*;
00024
00046 public class ServiceTracker implements ServiceTrackerCustomizer {
00047
00048 static final boolean DEBUG = false;
00053 protected final BundleContext context;
00059 protected final Filter filter;
00063 final ServiceTrackerCustomizer customizer;
00069 final String listenerFilter;
00074 private final String trackClass;
00079 private final ServiceReference trackReference;
00084 private Tracked tracked;
00091 private volatile int trackingCount = -1;
00097 private volatile ServiceReference cachedReference;
00103 private volatile Object cachedService;
00104
00125 public ServiceTracker(BundleContext context, ServiceReference reference,
00126 ServiceTrackerCustomizer customizer) {
00127 this.context = context;
00128 this.trackReference = reference;
00129 this.trackClass = null;
00130 this.customizer = (customizer == null) ? this : customizer;
00131 this.listenerFilter = "(" + Constants.SERVICE_ID + "=" + reference.getProperty(Constants.SERVICE_ID).toString() + ")";
00132 try {
00133 this.filter = context.createFilter(listenerFilter);
00134 }
00135 catch (InvalidSyntaxException e) {
00136
00137
00138 throw new IllegalArgumentException(
00139 "unexpected InvalidSyntaxException: " + e.getMessage());
00140 }
00141 }
00142
00162 public ServiceTracker(BundleContext context, String clazz,
00163 ServiceTrackerCustomizer customizer) {
00164 this.context = context;
00165 this.trackReference = null;
00166 this.trackClass = clazz;
00167 this.customizer = (customizer == null) ? this : customizer;
00168 this.listenerFilter = "(" + Constants.OBJECTCLASS + "=" + clazz.toString() + ")";
00169 try {
00170 this.filter = context.createFilter(listenerFilter);
00171 }
00172 catch (InvalidSyntaxException e) {
00173
00174
00175 throw new IllegalArgumentException(
00176 "unexpected InvalidSyntaxException: " + e.getMessage());
00177 }
00178 }
00179
00200 public ServiceTracker(BundleContext context, Filter filter,
00201 ServiceTrackerCustomizer customizer) {
00202 this.context = context;
00203 this.trackReference = null;
00204 this.trackClass = null;
00205 this.listenerFilter = null;
00206 this.filter = filter;
00207 this.customizer = (customizer == null) ? this : customizer;
00208 if ((context == null) || (filter == null)) {
00209
00210
00211
00212 throw new NullPointerException();
00213 }
00214 }
00215
00228 public void open() {
00229 open(false);
00230 }
00231
00253 public synchronized void open(boolean trackAllServices) {
00254 if (tracked != null) {
00255 return;
00256 }
00257 if (DEBUG) {
00258 System.out.println("ServiceTracker.open: " + filter);
00259 }
00260 tracked = trackAllServices ? new AllTracked() : new Tracked();
00261 trackingCount = 0;
00262 synchronized (tracked) {
00263 try {
00264 context.addServiceListener(tracked, listenerFilter);
00265 ServiceReference[] references;
00266 if (listenerFilter == null) {
00267 references = getInitialReferences(trackAllServices, null,
00268 filter.toString());
00269 }
00270 else {
00271 if (trackClass == null) {
00272 references = new ServiceReference[] {trackReference};
00273 }
00274 else {
00275 references = getInitialReferences(trackAllServices,
00276 trackClass, null);
00277 }
00278 }
00279
00280 tracked.setInitialServices(references);
00281
00282
00283 }
00284 catch (InvalidSyntaxException e) {
00285 throw new RuntimeException(
00286 "unexpected InvalidSyntaxException: " + e.getMessage());
00287 }
00288 }
00289
00290 tracked.trackInitialServices();
00291 }
00292
00304 private ServiceReference[] getInitialReferences(boolean trackAllServices,
00305 String trackClass, String filterString)
00306 throws InvalidSyntaxException {
00307 if (trackAllServices) {
00308 return context.getAllServiceReferences(trackClass, filterString);
00309 }
00310 else {
00311 return context.getServiceReferences(trackClass, filterString);
00312 }
00313 }
00314
00322 public synchronized void close() {
00323 if (tracked == null) {
00324 return;
00325 }
00326 if (DEBUG) {
00327 System.out.println("ServiceTracker.close: " + filter);
00328 }
00329 tracked.close();
00330 ServiceReference[] references = getServiceReferences();
00331 Tracked outgoing = tracked;
00332 tracked = null;
00333 try {
00334 context.removeServiceListener(outgoing);
00335 }
00336 catch (IllegalStateException e) {
00337
00338 }
00339 if (references != null) {
00340 for (int i = 0; i < references.length; i++) {
00341 outgoing.untrack(references[i]);
00342 }
00343 }
00344 trackingCount = -1;
00345 if (DEBUG) {
00346 if ((cachedReference == null) && (cachedService == null)) {
00347 System.out
00348 .println("ServiceTracker.close[cached cleared]: " + filter);
00349 }
00350 }
00351 }
00352
00378 public Object addingService(ServiceReference reference) {
00379 return context.getService(reference);
00380 }
00381
00397 public void modifiedService(ServiceReference reference, Object service) {
00398 }
00399
00422 public void removedService(ServiceReference reference, Object service) {
00423 context.ungetService(reference);
00424 }
00425
00442 public Object waitForService(long timeout) throws InterruptedException {
00443 if (timeout < 0) {
00444 throw new IllegalArgumentException("timeout value is negative");
00445 }
00446 Object object = getService();
00447 while (object == null) {
00448 Tracked tracked = this.tracked;
00449
00450
00451
00452 if (tracked == null) {
00453 return null;
00454 }
00455 synchronized (tracked) {
00456 if (tracked.size() == 0) {
00457 tracked.wait(timeout);
00458 }
00459 }
00460 object = getService();
00461 if (timeout > 0) {
00462 return object;
00463 }
00464 }
00465 return object;
00466 }
00467
00475 public ServiceReference[] getServiceReferences() {
00476 Tracked tracked = this.tracked;
00477
00478
00479
00480 if (tracked == null) {
00481 return null;
00482 }
00483 synchronized (tracked) {
00484 int length = tracked.size();
00485 if (length == 0) {
00486 return null;
00487 }
00488 ServiceReference[] references = new ServiceReference[length];
00489 Enumeration keys = tracked.keys();
00490 for (int i = 0; i < length; i++) {
00491 references[i] = (ServiceReference) keys.nextElement();
00492 }
00493 return references;
00494 }
00495 }
00496
00518 public ServiceReference getServiceReference() {
00519 ServiceReference reference = cachedReference;
00520 if (reference != null) {
00521 if (DEBUG) {
00522 System.out
00523 .println("ServiceTracker.getServiceReference[cached]: " + filter);
00524 }
00525 return reference;
00526 }
00527 if (DEBUG) {
00528 System.out.println("ServiceTracker.getServiceReference: " + filter);
00529 }
00530 ServiceReference[] references = getServiceReferences();
00531 int length = (references == null) ? 0 : references.length;
00532 if (length == 0)
00533 {
00534 return null;
00535 }
00536 int index = 0;
00537 if (length > 1)
00538 {
00539 int rankings[] = new int[length];
00540 int count = 0;
00541 int maxRanking = Integer.MIN_VALUE;
00542 for (int i = 0; i < length; i++) {
00543 Object property = references[i]
00544 .getProperty(Constants.SERVICE_RANKING);
00545 int ranking = (property instanceof Integer) ? ((Integer) property)
00546 .intValue()
00547 : 0;
00548 rankings[i] = ranking;
00549 if (ranking > maxRanking) {
00550 index = i;
00551 maxRanking = ranking;
00552 count = 1;
00553 }
00554 else {
00555 if (ranking == maxRanking) {
00556 count++;
00557 }
00558 }
00559 }
00560 if (count > 1)
00561 {
00562 long minId = Long.MAX_VALUE;
00563 for (int i = 0; i < length; i++) {
00564 if (rankings[i] == maxRanking) {
00565 long id = ((Long) (references[i]
00566 .getProperty(Constants.SERVICE_ID)))
00567 .longValue();
00568 if (id < minId) {
00569 index = i;
00570 minId = id;
00571 }
00572 }
00573 }
00574 }
00575 }
00576 return cachedReference = references[index];
00577 }
00578
00589 public Object getService(ServiceReference reference) {
00590 Tracked tracked = this.tracked;
00591
00592
00593
00594 if (tracked == null) {
00595 return null;
00596 }
00597 synchronized (tracked) {
00598 return tracked.get(reference);
00599 }
00600 }
00601
00609 public Object[] getServices() {
00610 Tracked tracked = this.tracked;
00611
00612
00613
00614 if (tracked == null) {
00615 return null;
00616 }
00617 synchronized (tracked) {
00618 ServiceReference[] references = getServiceReferences();
00619 int length = (references == null) ? 0 : references.length;
00620 if (length == 0) {
00621 return null;
00622 }
00623 Object[] objects = new Object[length];
00624 for (int i = 0; i < length; i++) {
00625 objects[i] = getService(references[i]);
00626 }
00627 return objects;
00628 }
00629 }
00630
00642 public Object getService() {
00643 Object service = cachedService;
00644 if (service != null) {
00645 if (DEBUG) {
00646 System.out
00647 .println("ServiceTracker.getService[cached]: " + filter);
00648 }
00649 return service;
00650 }
00651 if (DEBUG) {
00652 System.out.println("ServiceTracker.getService: " + filter);
00653 }
00654 ServiceReference reference = getServiceReference();
00655 if (reference == null) {
00656 return null;
00657 }
00658 return cachedService = getService(reference);
00659 }
00660
00671 public void remove(ServiceReference reference) {
00672 Tracked tracked = this.tracked;
00673
00674
00675
00676 if (tracked == null) {
00677 return;
00678 }
00679 tracked.untrack(reference);
00680 }
00681
00688 public int size() {
00689 Tracked tracked = this.tracked;
00690
00691
00692
00693 if (tracked == null) {
00694 return 0;
00695 }
00696 return tracked.size();
00697 }
00698
00719 public int getTrackingCount() {
00720 return trackingCount;
00721 }
00722
00727
00728
00729
00730
00731
00732 void modified() {
00733 trackingCount++;
00734 cachedReference = null;
00735 cachedService = null;
00736 if (DEBUG) {
00737 System.out.println("ServiceTracker.modified: " + filter);
00738 }
00739 }
00740
00745 protected void finalize() throws Throwable {
00746 }
00747
00758 class Tracked extends Hashtable implements ServiceListener {
00759 static final long serialVersionUID = -7420065199791006079L;
00773 private ArrayList adding;
00774
00781 private volatile boolean closed;
00782
00799 private LinkedList initial;
00800
00804 protected Tracked() {
00805 super();
00806 closed = false;
00807 adding = new ArrayList(6);
00808 initial = new LinkedList();
00809 }
00810
00821 protected void setInitialServices(ServiceReference[] references) {
00822 if (references == null) {
00823 return;
00824 }
00825 int size = references.length;
00826 for (int i = 0; i < size; i++) {
00827 if (DEBUG) {
00828 System.out
00829 .println("ServiceTracker.Tracked.setInitialServices: " + references[i]);
00830 }
00831 initial.add(references[i]);
00832 }
00833 }
00834
00843 protected void trackInitialServices() {
00844 while (true) {
00845 ServiceReference reference;
00846 synchronized (this) {
00847 if (initial.size() == 0) {
00848
00849
00850
00851 return;
00852 }
00853
00854
00855
00856
00857 reference = (ServiceReference) initial.removeFirst();
00858 if (this.get(reference) != null) {
00859
00860 if (DEBUG) {
00861 System.out
00862 .println("ServiceTracker.Tracked.trackInitialServices[already tracked]: " + reference);
00863 }
00864 continue;
00865 }
00866 if (adding.contains(reference)) {
00867
00868
00869
00870
00871 if (DEBUG) {
00872 System.out
00873 .println("ServiceTracker.Tracked.trackInitialServices[already adding]: " + reference);
00874 }
00875 continue;
00876 }
00877 adding.add(reference);
00878 }
00879 if (DEBUG) {
00880 System.out
00881 .println("ServiceTracker.Tracked.trackInitialServices: " + reference);
00882 }
00883 trackAdding(reference);
00884
00885
00886
00887
00888 }
00889 }
00890
00895 protected void close() {
00896 closed = true;
00897 }
00898
00906 public void serviceChanged(ServiceEvent event) {
00907
00908
00909
00910
00911 if (closed) {
00912 return;
00913 }
00914 ServiceReference reference = event.getServiceReference();
00915 if (DEBUG) {
00916 System.out
00917 .println("ServiceTracker.Tracked.serviceChanged[" + event.getType() + "]: " + reference);
00918 }
00919
00920 switch (event.getType()) {
00921 case ServiceEvent.REGISTERED :
00922 case ServiceEvent.MODIFIED :
00923 if (listenerFilter != null) {
00924
00925 track(reference);
00926
00927
00928
00929
00930 }
00931 else {
00932 if (filter.match(reference)) {
00933 track(reference);
00934
00935
00936
00937
00938 }
00939 else {
00940 untrack(reference);
00941
00942
00943
00944
00945 }
00946 }
00947 break;
00948 case ServiceEvent.UNREGISTERING :
00949 untrack(reference);
00950
00951
00952
00953
00954 break;
00955 }
00956 }
00957
00963 protected void track(ServiceReference reference) {
00964 Object object;
00965 synchronized (this) {
00966 object = this.get(reference);
00967 }
00968 if (object != null)
00969 {
00970 if (DEBUG) {
00971 System.out
00972 .println("ServiceTracker.Tracked.track[modified]: " + reference);
00973 }
00974 synchronized (this) {
00975 modified();
00976 }
00977
00978 customizer.modifiedService(reference, object);
00979
00980
00981
00982
00983 return;
00984 }
00985 synchronized (this) {
00986 if (adding.contains(reference)) {
00987
00988
00989
00990
00991 if (DEBUG) {
00992 System.out
00993 .println("ServiceTracker.Tracked.track[already adding]: " + reference);
00994 }
00995 return;
00996 }
00997 adding.add(reference);
00998 }
00999
01000 trackAdding(reference);
01001
01002
01003
01004 }
01005
01013 private void trackAdding(ServiceReference reference) {
01014 if (DEBUG) {
01015 System.out
01016 .println("ServiceTracker.Tracked.trackAdding: " + reference);
01017 }
01018 Object object = null;
01019 boolean becameUntracked = false;
01020
01021 try {
01022 object = customizer.addingService(reference);
01023
01024
01025
01026
01027 }
01028 finally {
01029 synchronized (this) {
01030 if (adding.remove(reference)) {
01031
01032
01033
01034
01035 if (object != null) {
01036 this.put(reference, object);
01037 modified();
01038 notifyAll();
01039
01040
01041
01042 }
01043 }
01044 else {
01045 becameUntracked = true;
01046 }
01047 }
01048 }
01049
01050
01051
01052 if (becameUntracked) {
01053 if (DEBUG) {
01054 System.out
01055 .println("ServiceTracker.Tracked.trackAdding[removed]: " + reference);
01056 }
01057
01058 customizer.removedService(reference, object);
01059
01060
01061
01062
01063 }
01064 }
01065
01071 protected void untrack(ServiceReference reference) {
01072 Object object;
01073 synchronized (this) {
01074 if (initial.remove(reference)) {
01075
01076
01077
01078
01079
01080 if (DEBUG) {
01081 System.out
01082 .println("ServiceTracker.Tracked.untrack[removed from initial]: " + reference);
01083 }
01084 return;
01085
01086
01087
01088 }
01089
01090 if (adding.remove(reference)) {
01091
01092
01093
01094 if (DEBUG) {
01095 System.out
01096 .println("ServiceTracker.Tracked.untrack[being added]: " + reference);
01097 }
01098 return;
01099
01100
01101
01102 }
01103 object = this.remove(reference);
01104
01105
01106
01107
01108 if (object == null) {
01109 return;
01110 }
01111 modified();
01112 }
01113 if (DEBUG) {
01114 System.out
01115 .println("ServiceTracker.Tracked.untrack[removed]: " + reference);
01116 }
01117
01118 customizer.removedService(reference, object);
01119
01120
01121
01122
01123 }
01124 }
01125
01132 class AllTracked extends Tracked implements AllServiceListener {
01133 static final long serialVersionUID = 4050764875305137716L;
01134
01138 protected AllTracked() {
01139 super();
01140 }
01141 }
01142 }