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