ServiceTracker.java

00001 /*
00002  * $Header: /cvshome/build/org.osgi.util.tracker/src/org/osgi/util/tracker/ServiceTracker.java,v 1.20 2006/06/16 16:31:13 hargrave Exp $
00003  * 
00004  * Copyright (c) OSGi Alliance (2000, 2006). All Rights Reserved.
00005  * 
00006  * Licensed under the Apache License, Version 2.0 (the "License");
00007  * you may not use this file except in compliance with the License.
00008  * You may obtain a copy of the License at
00009  *
00010  *      http://www.apache.org/licenses/LICENSE-2.0
00011  *
00012  * Unless required by applicable law or agreed to in writing, software
00013  * distributed under the License is distributed on an "AS IS" BASIS,
00014  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00015  * See the License for the specific language governing permissions and
00016  * limitations under the License.
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         /* set this to true to compile in debug messages */
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() + ")"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
00132                 try {
00133                         this.filter = context.createFilter(listenerFilter);
00134                 }
00135                 catch (InvalidSyntaxException e) { // we could only get this exception
00136                         // if the ServiceReference was
00137                         // invalid
00138                         throw new IllegalArgumentException(
00139                                         "unexpected InvalidSyntaxException: " + e.getMessage()); //$NON-NLS-1$
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() + ")"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
00169                 try {
00170                         this.filter = context.createFilter(listenerFilter);
00171                 }
00172                 catch (InvalidSyntaxException e) { // we could only get this exception
00173                         // if the clazz argument was
00174                         // malformed
00175                         throw new IllegalArgumentException(
00176                                         "unexpected InvalidSyntaxException: " + e.getMessage()); //$NON-NLS-1$
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)) { // we throw a NPE here
00209                         // to
00210                         // be consistent with the
00211                         // other constructors
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); //$NON-NLS-1$
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) { // user supplied filter
00267                                         references = getInitialReferences(trackAllServices, null,
00268                                                         filter.toString());
00269                                 }
00270                                 else { // constructor supplied filter
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); // set tracked with
00281                                 // the initial
00282                                 // references
00283                         }
00284                         catch (InvalidSyntaxException e) {
00285                                 throw new RuntimeException(
00286                                                 "unexpected InvalidSyntaxException: " + e.getMessage()); //$NON-NLS-1$
00287                         }
00288                 }
00289                 /* Call tracked outside of synchronized region */
00290                 tracked.trackInitialServices(); // process the initial references
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); //$NON-NLS-1$
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                         /* In case the context was stopped. */
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); //$NON-NLS-1$
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"); //$NON-NLS-1$
00445                 }
00446                 Object object = getService();
00447                 while (object == null) {
00448                         Tracked tracked = this.tracked; /*
00449                                                                                          * use local var since we are not
00450                                                                                          * synchronized
00451                                                                                          */
00452                         if (tracked == null) { /* if ServiceTracker is not open */
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                                                                                  * use local var since we are not
00478                                                                                  * synchronized
00479                                                                                  */
00480                 if (tracked == null) { /* if ServiceTracker is not open */
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); //$NON-NLS-1$
00524                         }
00525                         return reference;
00526                 }
00527                 if (DEBUG) {
00528                         System.out.println("ServiceTracker.getServiceReference: " + filter); //$NON-NLS-1$
00529                 }
00530                 ServiceReference[] references = getServiceReferences();
00531                 int length = (references == null) ? 0 : references.length;
00532                 if (length == 0) /* if no service is being tracked */
00533                 {
00534                         return null;
00535                 }
00536                 int index = 0;
00537                 if (length > 1) /* if more than one service, select highest ranking */
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) /* if still more than one service, select lowest id */
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                                                                                  * use local var since we are not
00592                                                                                  * synchronized
00593                                                                                  */
00594                 if (tracked == null) { /* if ServiceTracker is not open */
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                                                                                  * use local var since we are not
00612                                                                                  * synchronized
00613                                                                                  */
00614                 if (tracked == null) { /* if ServiceTracker is not open */
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); //$NON-NLS-1$
00648                         }
00649                         return service;
00650                 }
00651                 if (DEBUG) {
00652                         System.out.println("ServiceTracker.getService: " + filter); //$NON-NLS-1$
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                                                                                  * use local var since we are not
00674                                                                                  * synchronized
00675                                                                                  */
00676                 if (tracked == null) { /* if ServiceTracker is not open */
00677                         return;
00678                 }
00679                 tracked.untrack(reference);
00680         }
00681 
00688         public int size() {
00689                 Tracked tracked = this.tracked; /*
00690                                                                                  * use local var since we are not
00691                                                                                  * synchronized
00692                                                                                  */
00693                 if (tracked == null) { /* if ServiceTracker is not open */
00694                         return 0;
00695                 }
00696                 return tracked.size();
00697         }
00698 
00719         public int getTrackingCount() {
00720                 return trackingCount;
00721         }
00722 
00727         /*
00728          * This method must not be synchronized since it is called by Tracked while
00729          * Tracked is synchronized. We don't want synchronization interactions
00730          * between the ServiceListener thread and the user thread.
00731          */
00732         void modified() {
00733                 trackingCount++; /* increment modification count */
00734                 cachedReference = null; /* clear cached value */
00735                 cachedService = null; /* clear cached value */
00736                 if (DEBUG) {
00737                         System.out.println("ServiceTracker.modified: " + filter); //$NON-NLS-1$
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]); //$NON-NLS-1$
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                                                  * if there are no more inital services
00850                                                  */
00851                                                 return; /* we are done */
00852                                         }
00853                                         /*
00854                                          * move the first service from the initial list to the
00855                                          * adding list within this synchronized block.
00856                                          */
00857                                         reference = (ServiceReference) initial.removeFirst();
00858                                         if (this.get(reference) != null) {
00859                                                 /* if we are already tracking this service */
00860                                                 if (DEBUG) {
00861                                                         System.out
00862                                                                         .println("ServiceTracker.Tracked.trackInitialServices[already tracked]: " + reference); //$NON-NLS-1$
00863                                                 }
00864                                                 continue; /* skip this service */
00865                                         }
00866                                         if (adding.contains(reference)) {
00867                                                 /*
00868                                                  * if this service is already in the process of being
00869                                                  * added.
00870                                                  */
00871                                                 if (DEBUG) {
00872                                                         System.out
00873                                                                         .println("ServiceTracker.Tracked.trackInitialServices[already adding]: " + reference); //$NON-NLS-1$
00874                                                 }
00875                                                 continue; /* skip this service */
00876                                         }
00877                                         adding.add(reference);
00878                                 }
00879                                 if (DEBUG) {
00880                                         System.out
00881                                                         .println("ServiceTracker.Tracked.trackInitialServices: " + reference); //$NON-NLS-1$
00882                                 }
00883                                 trackAdding(reference); /*
00884                                                                                  * Begin tracking it. We call
00885                                                                                  * trackAdding since we have already put
00886                                                                                  * the reference in the adding list.
00887                                                                                  */
00888                         }
00889                 }
00890 
00895                 protected void close() {
00896                         closed = true;
00897                 }
00898 
00906                 public void serviceChanged(ServiceEvent event) {
00907                         /*
00908                          * Check if we had a delayed call (which could happen when we
00909                          * close).
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); //$NON-NLS-1$ //$NON-NLS-2$
00918                         }
00919 
00920                         switch (event.getType()) {
00921                                 case ServiceEvent.REGISTERED :
00922                                 case ServiceEvent.MODIFIED :
00923                                         if (listenerFilter != null) { // constructor supplied
00924                                                                                                         // filter
00925                                                 track(reference);
00926                                                 /*
00927                                                  * If the customizer throws an unchecked exception, it
00928                                                  * is safe to let it propagate
00929                                                  */
00930                                         }
00931                                         else { // user supplied filter
00932                                                 if (filter.match(reference)) {
00933                                                         track(reference);
00934                                                         /*
00935                                                          * If the customizer throws an unchecked exception,
00936                                                          * it is safe to let it propagate
00937                                                          */
00938                                                 }
00939                                                 else {
00940                                                         untrack(reference);
00941                                                         /*
00942                                                          * If the customizer throws an unchecked exception,
00943                                                          * it is safe to let it propagate
00944                                                          */
00945                                                 }
00946                                         }
00947                                         break;
00948                                 case ServiceEvent.UNREGISTERING :
00949                                         untrack(reference);
00950                                         /*
00951                                          * If the customizer throws an unchecked exception, it is
00952                                          * safe to let it propagate
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) /* we are already tracking the service */
00969                         {
00970                                 if (DEBUG) {
00971                                         System.out
00972                                                         .println("ServiceTracker.Tracked.track[modified]: " + reference); //$NON-NLS-1$
00973                                 }
00974                                 synchronized (this) {
00975                                         modified(); /* increment modification count */
00976                                 }
00977                                 /* Call customizer outside of synchronized region */
00978                                 customizer.modifiedService(reference, object);
00979                                 /*
00980                                  * If the customizer throws an unchecked exception, it is safe
00981                                  * to let it propagate
00982                                  */
00983                                 return;
00984                         }
00985                         synchronized (this) {
00986                                 if (adding.contains(reference)) { /*
00987                                                                                                          * if this service is
00988                                                                                                          * already in the process of
00989                                                                                                          * being added.
00990                                                                                                          */
00991                                         if (DEBUG) {
00992                                                 System.out
00993                                                                 .println("ServiceTracker.Tracked.track[already adding]: " + reference); //$NON-NLS-1$
00994                                         }
00995                                         return;
00996                                 }
00997                                 adding.add(reference); /* mark this service is being added */
00998                         }
00999 
01000                         trackAdding(reference); /*
01001                                                                          * call trackAdding now that we have put the
01002                                                                          * reference in the adding list
01003                                                                          */
01004                 }
01005 
01013                 private void trackAdding(ServiceReference reference) {
01014                         if (DEBUG) {
01015                                 System.out
01016                                                 .println("ServiceTracker.Tracked.trackAdding: " + reference); //$NON-NLS-1$
01017                         }
01018                         Object object = null;
01019                         boolean becameUntracked = false;
01020                         /* Call customizer outside of synchronized region */
01021                         try {
01022                                 object = customizer.addingService(reference);
01023                                 /*
01024                                  * If the customizer throws an unchecked exception, it will
01025                                  * propagate after the finally
01026                                  */
01027                         }
01028                         finally {
01029                                 synchronized (this) {
01030                                         if (adding.remove(reference)) { /*
01031                                                                                                          * if the service was not
01032                                                                                                          * untracked during the
01033                                                                                                          * customizer callback
01034                                                                                                          */
01035                                                 if (object != null) {
01036                                                         this.put(reference, object);
01037                                                         modified(); /* increment modification count */
01038                                                         notifyAll(); /*
01039                                                                                          * notify any waiters in
01040                                                                                          * waitForService
01041                                                                                          */
01042                                                 }
01043                                         }
01044                                         else {
01045                                                 becameUntracked = true;
01046                                         }
01047                                 }
01048                         }
01049                         /*
01050                          * The service became untracked during the customizer callback.
01051                          */
01052                         if (becameUntracked) {
01053                                 if (DEBUG) {
01054                                         System.out
01055                                                         .println("ServiceTracker.Tracked.trackAdding[removed]: " + reference); //$NON-NLS-1$
01056                                 }
01057                                 /* Call customizer outside of synchronized region */
01058                                 customizer.removedService(reference, object);
01059                                 /*
01060                                  * If the customizer throws an unchecked exception, it is safe
01061                                  * to let it propagate
01062                                  */
01063                         }
01064                 }
01065 
01071                 protected void untrack(ServiceReference reference) {
01072                         Object object;
01073                         synchronized (this) {
01074                                 if (initial.remove(reference)) { /*
01075                                                                                                          * if this service is
01076                                                                                                          * already in the list of
01077                                                                                                          * initial references to
01078                                                                                                          * process
01079                                                                                                          */
01080                                         if (DEBUG) {
01081                                                 System.out
01082                                                                 .println("ServiceTracker.Tracked.untrack[removed from initial]: " + reference); //$NON-NLS-1$
01083                                         }
01084                                         return; /*
01085                                                          * we have removed it from the list and it will not
01086                                                          * be processed
01087                                                          */
01088                                 }
01089 
01090                                 if (adding.remove(reference)) { /*
01091                                                                                                  * if the service is in the
01092                                                                                                  * process of being added
01093                                                                                                  */
01094                                         if (DEBUG) {
01095                                                 System.out
01096                                                                 .println("ServiceTracker.Tracked.untrack[being added]: " + reference); //$NON-NLS-1$
01097                                         }
01098                                         return; /*
01099                                                          * in case the service is untracked while in the
01100                                                          * process of adding
01101                                                          */
01102                                 }
01103                                 object = this.remove(reference); /*
01104                                                                                                          * must remove from tracker
01105                                                                                                          * before calling customizer
01106                                                                                                          * callback
01107                                                                                                          */
01108                                 if (object == null) { /* are we actually tracking the service */
01109                                         return;
01110                                 }
01111                                 modified(); /* increment modification count */
01112                         }
01113                         if (DEBUG) {
01114                                 System.out
01115                                                 .println("ServiceTracker.Tracked.untrack[removed]: " + reference); //$NON-NLS-1$
01116                         }
01117                         /* Call customizer outside of synchronized region */
01118                         customizer.removedService(reference, object);
01119                         /*
01120                          * If the customizer throws an unchecked exception, it is safe to
01121                          * let it propagate
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 }

Generated on Mon Jan 11 21:19:16 2010 for OpenMobileIS by  doxygen 1.5.4