blob: 97458d2afbec4af38297fc6c3b112556cdc07f7a [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;
Marcel Offermans227dd712011-04-19 07:14:22 +000021import java.util.HashMap;
22import java.util.Iterator;
Marcel Offermans7f687482010-05-11 09:12:34 +000023import java.util.List;
Marcel Offermans227dd712011-04-19 07:14:22 +000024import java.util.Map;
Marcel Offermansab4f4082011-11-16 09:30:41 +000025import java.util.Map.Entry;
Marcel Offermans227dd712011-04-19 07:14:22 +000026import java.util.TreeSet;
Marcel Offermansc854d1a2009-11-24 15:41:41 +000027
Marcel Offermans7f687482010-05-11 09:12:34 +000028import org.apache.felix.dm.DependencyManager;
Marcel Offermans8b93efa2010-07-02 18:27:21 +000029import org.apache.felix.dm.ServiceUtil;
Marcel Offermansc854d1a2009-11-24 15:41:41 +000030import org.osgi.framework.AllServiceListener;
31import org.osgi.framework.BundleContext;
32import org.osgi.framework.Constants;
33import org.osgi.framework.Filter;
34import org.osgi.framework.InvalidSyntaxException;
35import org.osgi.framework.ServiceEvent;
36import org.osgi.framework.ServiceListener;
37import org.osgi.framework.ServiceReference;
38import org.osgi.framework.Version;
39
40/**
41 * The <code>ServiceTracker</code> class simplifies using services from the
42 * Framework's service registry.
43 * <p>
44 * A <code>ServiceTracker</code> object is constructed with search criteria and
45 * a <code>ServiceTrackerCustomizer</code> object. A <code>ServiceTracker</code>
46 * can use a <code>ServiceTrackerCustomizer</code> to customize the service
47 * objects to be tracked. The <code>ServiceTracker</code> can then be opened to
48 * begin tracking all services in the Framework's service registry that match
49 * the specified search criteria. The <code>ServiceTracker</code> correctly
50 * handles all of the details of listening to <code>ServiceEvent</code>s and
51 * getting and ungetting services.
52 * <p>
53 * The <code>getServiceReferences</code> method can be called to get references
54 * to the services being tracked. The <code>getService</code> and
55 * <code>getServices</code> methods can be called to get the service objects for
56 * the tracked service.
57 * <p>
58 * The <code>ServiceTracker</code> class is thread-safe. It does not call a
59 * <code>ServiceTrackerCustomizer</code> while holding any locks.
60 * <code>ServiceTrackerCustomizer</code> implementations must also be
61 * thread-safe.
62 *
63 * @ThreadSafe
64 * @version $Revision: 6386 $
65 */
66public class ServiceTracker implements ServiceTrackerCustomizer {
67 /* set this to true to compile in debug messages */
68 static final boolean DEBUG = false;
69 /**
70 * The Bundle Context used by this <code>ServiceTracker</code>.
71 */
72 protected final BundleContext context;
73 /**
74 * The Filter used by this <code>ServiceTracker</code> which specifies the
75 * search criteria for the services to track.
76 *
77 * @since 1.1
78 */
79 protected final Filter filter;
80 /**
81 * The <code>ServiceTrackerCustomizer</code> for this tracker.
82 */
83 final ServiceTrackerCustomizer customizer;
84 /**
85 * Filter string for use when adding the ServiceListener. If this field is
86 * set, then certain optimizations can be taken since we don't have a user
87 * supplied filter.
88 */
89 final String listenerFilter;
90 /**
91 * Class name to be tracked. If this field is set, then we are tracking by
92 * class name.
93 */
94 private final String trackClass;
95 /**
96 * Reference to be tracked. If this field is set, then we are tracking a
97 * single ServiceReference.
98 */
99 private final ServiceReference trackReference;
100 /**
101 * Tracked services: <code>ServiceReference</code> -> customized Object and
102 * <code>ServiceListener</code> object
103 */
104 private volatile Tracked tracked;
105
106 /**
107 * Accessor method for the current Tracked object. This method is only
108 * intended to be used by the unsynchronized methods which do not modify the
109 * tracked field.
110 *
111 * @return The current Tracked object.
112 */
113 private Tracked tracked() {
114 return tracked;
115 }
116
117 /**
118 * Cached ServiceReference for getServiceReference.
119 *
120 * This field is volatile since it is accessed by multiple threads.
121 */
122 private volatile ServiceReference cachedReference;
123 /**
124 * Cached service object for getService.
125 *
126 * This field is volatile since it is accessed by multiple threads.
127 */
128 private volatile Object cachedService;
129
130 /**
131 * org.osgi.framework package version which introduced
132 * {@link ServiceEvent#MODIFIED_ENDMATCH}
133 */
134 private static final Version endMatchVersion = new Version(1, 5, 0);
135
136 /**
Marcel Offermanscdd0f292011-05-10 09:58:52 +0000137 * Flag that gets set when opening the tracker, determines if the tracker should
138 * track all aspects or just the highest ranked ones.
139 */
140 public boolean m_trackAllAspects;
141
142 /**
Marcel Offermansc854d1a2009-11-24 15:41:41 +0000143 * Create a <code>ServiceTracker</code> on the specified
144 * <code>ServiceReference</code>.
145 *
146 * <p>
147 * The service referenced by the specified <code>ServiceReference</code>
148 * will be tracked by this <code>ServiceTracker</code>.
149 *
150 * @param context The <code>BundleContext</code> against which the tracking
151 * is done.
152 * @param reference The <code>ServiceReference</code> for the service to be
153 * tracked.
154 * @param customizer The customizer object to call when services are added,
155 * modified, or removed in this <code>ServiceTracker</code>. If
156 * customizer is <code>null</code>, then this
157 * <code>ServiceTracker</code> will be used as the
158 * <code>ServiceTrackerCustomizer</code> and this
159 * <code>ServiceTracker</code> will call the
160 * <code>ServiceTrackerCustomizer</code> methods on itself.
161 */
162 public ServiceTracker(final BundleContext context,
163 final ServiceReference reference,
164 final ServiceTrackerCustomizer customizer) {
165 this.context = context;
166 this.trackReference = reference;
167 this.trackClass = null;
168 this.customizer = (customizer == null) ? this : customizer;
169 this.listenerFilter = "(" + Constants.SERVICE_ID + "="
170 + reference.getProperty(Constants.SERVICE_ID).toString() + ")";
171 try {
172 this.filter = context.createFilter(listenerFilter);
173 }
174 catch (InvalidSyntaxException e) {
175 /*
176 * we could only get this exception if the ServiceReference was
177 * invalid
178 */
179 IllegalArgumentException iae = new IllegalArgumentException(
180 "unexpected InvalidSyntaxException: " + e.getMessage());
181 iae.initCause(e);
182 throw iae;
183 }
184 }
185
186 /**
187 * Create a <code>ServiceTracker</code> on the specified class name.
188 *
189 * <p>
190 * Services registered under the specified class name will be tracked by
191 * this <code>ServiceTracker</code>.
192 *
193 * @param context The <code>BundleContext</code> against which the tracking
194 * is done.
195 * @param clazz The class name of the services to be tracked.
196 * @param customizer The customizer object to call when services are added,
197 * modified, or removed in this <code>ServiceTracker</code>. If
198 * customizer is <code>null</code>, then this
199 * <code>ServiceTracker</code> will be used as the
200 * <code>ServiceTrackerCustomizer</code> and this
201 * <code>ServiceTracker</code> will call the
202 * <code>ServiceTrackerCustomizer</code> methods on itself.
203 */
204 public ServiceTracker(final BundleContext context, final String clazz,
205 final ServiceTrackerCustomizer customizer) {
206 this.context = context;
207 this.trackReference = null;
208 this.trackClass = clazz;
209 this.customizer = (customizer == null) ? this : customizer;
210 // we call clazz.toString to verify clazz is non-null!
211 this.listenerFilter = "(" + Constants.OBJECTCLASS + "="
212 + clazz.toString() + ")";
213 try {
214 this.filter = context.createFilter(listenerFilter);
215 }
216 catch (InvalidSyntaxException e) {
217 /*
218 * we could only get this exception if the clazz argument was
219 * malformed
220 */
221 IllegalArgumentException iae = new IllegalArgumentException(
222 "unexpected InvalidSyntaxException: " + e.getMessage());
223 iae.initCause(e);
224 throw iae;
225 }
226 }
227
228 /**
229 * Create a <code>ServiceTracker</code> on the specified <code>Filter</code>
230 * object.
231 *
232 * <p>
233 * Services which match the specified <code>Filter</code> object will be
234 * tracked by this <code>ServiceTracker</code>.
235 *
236 * @param context The <code>BundleContext</code> against which the tracking
237 * is done.
238 * @param filter The <code>Filter</code> to select the services to be
239 * tracked.
240 * @param customizer The customizer object to call when services are added,
241 * modified, or removed in this <code>ServiceTracker</code>. If
242 * customizer is null, then this <code>ServiceTracker</code> will be
243 * used as the <code>ServiceTrackerCustomizer</code> and this
244 * <code>ServiceTracker</code> will call the
245 * <code>ServiceTrackerCustomizer</code> methods on itself.
246 * @since 1.1
247 */
248 public ServiceTracker(final BundleContext context, final Filter filter,
249 final ServiceTrackerCustomizer customizer) {
250 this.context = context;
251 this.trackReference = null;
252 this.trackClass = null;
253 final Version frameworkVersion = (Version) AccessController
254 .doPrivileged(new PrivilegedAction() {
255 public Object run() {
256 String version = context
257 .getProperty(Constants.FRAMEWORK_VERSION);
258 return (version == null) ? Version.emptyVersion
259 : new Version(version);
260 }
261 });
262 final boolean endMatchSupported = (frameworkVersion
263 .compareTo(endMatchVersion) >= 0);
264 this.listenerFilter = endMatchSupported ? filter.toString() : null;
265 this.filter = filter;
266 this.customizer = (customizer == null) ? this : customizer;
267 if ((context == null) || (filter == null)) {
268 /*
269 * we throw a NPE here to be consistent with the other constructors
270 */
271 throw new NullPointerException();
272 }
273 }
274
275 /**
276 * Open this <code>ServiceTracker</code> and begin tracking services.
277 *
278 * <p>
279 * This implementation calls <code>open(false)</code>.
280 *
281 * @throws java.lang.IllegalStateException If the <code>BundleContext</code>
282 * with which this <code>ServiceTracker</code> was created is no
283 * longer valid.
284 * @see #open(boolean)
285 */
286 public void open() {
287 open(false);
288 }
289
290 /**
291 * Open this <code>ServiceTracker</code> and begin tracking services.
292 *
293 * <p>
294 * Services which match the search criteria specified when this
295 * <code>ServiceTracker</code> was created are now tracked by this
296 * <code>ServiceTracker</code>.
297 *
298 * @param trackAllServices If <code>true</code>, then this
299 * <code>ServiceTracker</code> will track all matching services
300 * regardless of class loader accessibility. If <code>false</code>,
301 * then this <code>ServiceTracker</code> will only track matching
302 * services which are class loader accessible to the bundle whose
303 * <code>BundleContext</code> is used by this
304 * <code>ServiceTracker</code>.
305 * @throws java.lang.IllegalStateException If the <code>BundleContext</code>
306 * with which this <code>ServiceTracker</code> was created is no
307 * longer valid.
308 * @since 1.3
309 */
Marcel Offermanscdd0f292011-05-10 09:58:52 +0000310 public void open(boolean trackAllServices) {
311 open(trackAllServices, false);
312 }
313
314 /**
315 * Open this <code>ServiceTracker</code> and begin tracking services.
316 *
317 * <p>
318 * Services which match the search criteria specified when this
319 * <code>ServiceTracker</code> was created are now tracked by this
320 * <code>ServiceTracker</code>.
321 *
322 * @param trackAllServices If <code>true</code>, then this
323 * <code>ServiceTracker</code> will track all matching services
324 * regardless of class loader accessibility. If <code>false</code>,
325 * then this <code>ServiceTracker</code> will only track matching
326 * services which are class loader accessible to the bundle whose
327 * <code>BundleContext</code> is used by this
328 * <code>ServiceTracker</code>.
329 * @param trackAllAspects If <code>true</code> then this
330 * <code>ServiceTracker</code> will track all aspects regardless
331 * of their rank. If <code>false</code> only the highest ranked
332 * aspects (or the original service if there are no aspects) will
333 * be tracked. The latter is the default.
334 * @throws java.lang.IllegalStateException If the <code>BundleContext</code>
335 * with which this <code>ServiceTracker</code> was created is no
336 * longer valid.
337 */
338 public void open(boolean trackAllServices, boolean trackAllAspects) {
Marcel Offermansc854d1a2009-11-24 15:41:41 +0000339 final Tracked t;
340 synchronized (this) {
341 if (tracked != null) {
342 return;
343 }
344 if (DEBUG) {
345 System.out.println("ServiceTracker.open: " + filter);
346 }
Marcel Offermanscdd0f292011-05-10 09:58:52 +0000347 m_trackAllAspects = trackAllAspects;
Marcel Offermansc854d1a2009-11-24 15:41:41 +0000348 t = trackAllServices ? new AllTracked() : new Tracked();
349 synchronized (t) {
350 try {
351 context.addServiceListener(t, listenerFilter);
352 ServiceReference[] references = null;
353 if (trackClass != null) {
354 references = getInitialReferences(trackAllServices,
355 trackClass, null);
356 }
357 else {
358 if (trackReference != null) {
359 if (trackReference.getBundle() != null) {
360 references = new ServiceReference[] {trackReference};
361 }
362 }
363 else { /* user supplied filter */
364 references = getInitialReferences(trackAllServices,
365 null,
366 (listenerFilter != null) ? listenerFilter
367 : filter.toString());
368 }
369 }
370 /* set tracked with the initial references */
371 t.setInitial(references);
372 }
373 catch (InvalidSyntaxException e) {
374 throw new RuntimeException(
375 "unexpected InvalidSyntaxException: "
376 + e.getMessage(), e);
377 }
378 }
379 tracked = t;
380 }
381 /* Call tracked outside of synchronized region */
382 t.trackInitial(); /* process the initial references */
383 }
384
385 /**
386 * Returns the list of initial <code>ServiceReference</code>s that will be
387 * tracked by this <code>ServiceTracker</code>.
388 *
389 * @param trackAllServices If <code>true</code>, use
390 * <code>getAllServiceReferences</code>.
391 * @param className The class name with which the service was registered, or
392 * <code>null</code> for all services.
393 * @param filterString The filter criteria or <code>null</code> for all
394 * services.
395 * @return The list of initial <code>ServiceReference</code>s.
396 * @throws InvalidSyntaxException If the specified filterString has an
397 * invalid syntax.
398 */
399 private ServiceReference[] getInitialReferences(boolean trackAllServices,
400 String className, String filterString)
401 throws InvalidSyntaxException {
402 if (trackAllServices) {
403 return context.getAllServiceReferences(className, filterString);
404 }
405 return context.getServiceReferences(className, filterString);
406 }
407
408 /**
409 * Close this <code>ServiceTracker</code>.
410 *
411 * <p>
412 * This method should be called when this <code>ServiceTracker</code> should
413 * end the tracking of services.
414 *
415 * <p>
416 * This implementation calls {@link #getServiceReferences()} to get the list
417 * of tracked services to remove.
418 */
419 public void close() {
420 final Tracked outgoing;
421 final ServiceReference[] references;
422 synchronized (this) {
423 outgoing = tracked;
424 if (outgoing == null) {
425 return;
426 }
427 if (DEBUG) {
428 System.out.println("ServiceTracker.close: " + filter);
429 }
430 outgoing.close();
431 references = getServiceReferences();
432 tracked = null;
433 try {
434 context.removeServiceListener(outgoing);
435 }
436 catch (IllegalStateException e) {
437 /* In case the context was stopped. */
438 }
439 }
440 modified(); /* clear the cache */
441 synchronized (outgoing) {
442 outgoing.notifyAll(); /* wake up any waiters */
443 }
444 if (references != null) {
445 for (int i = 0; i < references.length; i++) {
446 outgoing.untrack(references[i], null);
447 }
448 }
449 if (DEBUG) {
450 if ((cachedReference == null) && (cachedService == null)) {
451 System.out
452 .println("ServiceTracker.close[cached cleared]: "
453 + filter);
454 }
455 }
456 }
457
458 /**
459 * Default implementation of the
460 * <code>ServiceTrackerCustomizer.addingService</code> method.
461 *
462 * <p>
463 * This method is only called when this <code>ServiceTracker</code> has been
464 * constructed with a <code>null ServiceTrackerCustomizer</code> argument.
465 *
466 * <p>
467 * This implementation returns the result of calling <code>getService</code>
468 * on the <code>BundleContext</code> with which this
469 * <code>ServiceTracker</code> was created passing the specified
470 * <code>ServiceReference</code>.
471 * <p>
472 * This method can be overridden in a subclass to customize the service
473 * object to be tracked for the service being added. In that case, take care
474 * not to rely on the default implementation of
475 * {@link #removedService(ServiceReference, Object) removedService} to unget
476 * the service.
477 *
478 * @param reference The reference to the service being added to this
479 * <code>ServiceTracker</code>.
480 * @return The service object to be tracked for the service added to this
481 * <code>ServiceTracker</code>.
482 * @see ServiceTrackerCustomizer#addingService(ServiceReference)
483 */
484 public Object addingService(ServiceReference reference) {
485 return context.getService(reference);
486 }
487
488 public void addedService(ServiceReference reference, Object service) {
489 /* do nothing */
490 }
491
492 /**
493 * Default implementation of the
494 * <code>ServiceTrackerCustomizer.modifiedService</code> method.
495 *
496 * <p>
497 * This method is only called when this <code>ServiceTracker</code> has been
498 * constructed with a <code>null ServiceTrackerCustomizer</code> argument.
499 *
500 * <p>
501 * This implementation does nothing.
502 *
503 * @param reference The reference to modified service.
504 * @param service The service object for the modified service.
505 * @see ServiceTrackerCustomizer#modifiedService(ServiceReference, Object)
506 */
507 public void modifiedService(ServiceReference reference, Object service) {
508 /* do nothing */
509 }
510
511 /**
512 * Default implementation of the
513 * <code>ServiceTrackerCustomizer.removedService</code> method.
514 *
515 * <p>
516 * This method is only called when this <code>ServiceTracker</code> has been
517 * constructed with a <code>null ServiceTrackerCustomizer</code> argument.
518 *
519 * <p>
520 * This implementation calls <code>ungetService</code>, on the
521 * <code>BundleContext</code> with which this <code>ServiceTracker</code>
522 * was created, passing the specified <code>ServiceReference</code>.
523 * <p>
524 * This method can be overridden in a subclass. If the default
525 * implementation of {@link #addingService(ServiceReference) addingService}
526 * method was used, this method must unget the service.
527 *
528 * @param reference The reference to removed service.
529 * @param service The service object for the removed service.
530 * @see ServiceTrackerCustomizer#removedService(ServiceReference, Object)
531 */
532 public void removedService(ServiceReference reference, Object service) {
533 context.ungetService(reference);
534 }
535
536 /**
537 * Wait for at least one service to be tracked by this
538 * <code>ServiceTracker</code>. This method will also return when this
539 * <code>ServiceTracker</code> is closed.
540 *
541 * <p>
542 * It is strongly recommended that <code>waitForService</code> is not used
543 * during the calling of the <code>BundleActivator</code> methods.
544 * <code>BundleActivator</code> methods are expected to complete in a short
545 * period of time.
546 *
547 * <p>
548 * This implementation calls {@link #getService()} to determine if a service
549 * is being tracked.
550 *
551 * @param timeout The time interval in milliseconds to wait. If zero, the
552 * method will wait indefinitely.
553 * @return Returns the result of {@link #getService()}.
554 * @throws InterruptedException If another thread has interrupted the
555 * current thread.
556 * @throws IllegalArgumentException If the value of timeout is negative.
557 */
558 public Object waitForService(long timeout) throws InterruptedException {
559 if (timeout < 0) {
560 throw new IllegalArgumentException("timeout value is negative");
561 }
562 Object object = getService();
563 while (object == null) {
564 final Tracked t = tracked();
565 if (t == null) { /* if ServiceTracker is not open */
566 return null;
567 }
568 synchronized (t) {
569 if (t.size() == 0) {
570 t.wait(timeout);
571 }
572 }
573 object = getService();
574 if (timeout > 0) {
575 return object;
576 }
577 }
578 return object;
579 }
580
581 /**
582 * Return an array of <code>ServiceReference</code>s for all services being
583 * tracked by this <code>ServiceTracker</code>.
584 *
585 * @return Array of <code>ServiceReference</code>s or <code>null</code> if
586 * no services are being tracked.
587 */
588 public ServiceReference[] getServiceReferences() {
589 final Tracked t = tracked();
590 if (t == null) { /* if ServiceTracker is not open */
591 return null;
592 }
593 synchronized (t) {
594 int length = t.size();
595 if (length == 0) {
596 return null;
597 }
598 return (ServiceReference[]) t
599 .getTracked(new ServiceReference[length]);
600 }
601 }
Xander Uiterlinden652065a2012-07-23 07:00:27 +0000602
603 /**
604 * Returns a boolean indicating whether this <code>ServiceTracker</code> is tracking any services.
605 *
606 * @return true if services are being tracked, false if no services are being tracked.
607 */
608 public boolean hasReference() {
609 if (cachedReference != null) {
610 return true;
611 }
612 final Tracked t = tracked();
613 if (t == null) { /* if ServiceTracker is not open */
614 return false;
615 }
616 synchronized (t) {
617 int length = t.size();
618 return length > 0;
619 }
620 }
Marcel Offermansc854d1a2009-11-24 15:41:41 +0000621
622 /**
623 * Returns a <code>ServiceReference</code> for one of the services being
624 * tracked by this <code>ServiceTracker</code>.
625 *
626 * <p>
627 * If multiple services are being tracked, the service with the highest
628 * ranking (as specified in its <code>service.ranking</code> property) is
629 * returned. If there is a tie in ranking, the service with the lowest
630 * service ID (as specified in its <code>service.id</code> property); that
631 * is, the service that was registered first is returned. This is the same
632 * algorithm used by <code>BundleContext.getServiceReference</code>.
633 *
634 * <p>
635 * This implementation calls {@link #getServiceReferences()} to get the list
636 * of references for the tracked services.
637 *
638 * @return A <code>ServiceReference</code> or <code>null</code> if no
639 * services are being tracked.
640 * @since 1.1
641 */
642 public ServiceReference getServiceReference() {
643 ServiceReference reference = cachedReference;
644 if (reference != null) {
645 if (DEBUG) {
646 System.out
647 .println("ServiceTracker.getServiceReference[cached]: "
648 + filter);
649 }
650 return reference;
651 }
652 if (DEBUG) {
653 System.out.println("ServiceTracker.getServiceReference: " + filter);
654 }
655 ServiceReference[] references = getServiceReferences();
656 int length = (references == null) ? 0 : references.length;
657 if (length == 0) { /* if no service is being tracked */
658 return null;
659 }
660 int index = 0;
661 if (length > 1) { /* if more than one service, select highest ranking */
662 int rankings[] = new int[length];
663 int count = 0;
664 int maxRanking = Integer.MIN_VALUE;
665 for (int i = 0; i < length; i++) {
666 Object property = references[i]
667 .getProperty(Constants.SERVICE_RANKING);
668 int ranking = (property instanceof Integer) ? ((Integer) property)
669 .intValue()
670 : 0;
671 rankings[i] = ranking;
672 if (ranking > maxRanking) {
673 index = i;
674 maxRanking = ranking;
675 count = 1;
676 }
677 else {
678 if (ranking == maxRanking) {
679 count++;
680 }
681 }
682 }
683 if (count > 1) { /* if still more than one service, select lowest id */
684 long minId = Long.MAX_VALUE;
685 for (int i = 0; i < length; i++) {
686 if (rankings[i] == maxRanking) {
687 long id = ((Long) (references[i]
688 .getProperty(Constants.SERVICE_ID)))
689 .longValue();
690 if (id < minId) {
691 index = i;
692 minId = id;
693 }
694 }
695 }
696 }
697 }
698 return cachedReference = references[index];
699 }
700
701 /**
702 * Returns the service object for the specified
703 * <code>ServiceReference</code> if the specified referenced service is
704 * being tracked by this <code>ServiceTracker</code>.
705 *
706 * @param reference The reference to the desired service.
707 * @return A service object or <code>null</code> if the service referenced
708 * by the specified <code>ServiceReference</code> is not being
709 * tracked.
710 */
711 public Object getService(ServiceReference reference) {
712 final Tracked t = tracked();
713 if (t == null) { /* if ServiceTracker is not open */
714 return null;
715 }
716 synchronized (t) {
717 return t.getCustomizedObject(reference);
718 }
719 }
720
721 /**
722 * Return an array of service objects for all services being tracked by this
723 * <code>ServiceTracker</code>.
724 *
725 * <p>
726 * This implementation calls {@link #getServiceReferences()} to get the list
727 * of references for the tracked services and then calls
728 * {@link #getService(ServiceReference)} for each reference to get the
729 * tracked service object.
730 *
731 * @return An array of service objects or <code>null</code> if no services
732 * are being tracked.
733 */
734 public Object[] getServices() {
735 final Tracked t = tracked();
736 if (t == null) { /* if ServiceTracker is not open */
737 return null;
738 }
739 synchronized (t) {
740 ServiceReference[] references = getServiceReferences();
741 int length = (references == null) ? 0 : references.length;
742 if (length == 0) {
743 return null;
744 }
745 Object[] objects = new Object[length];
746 for (int i = 0; i < length; i++) {
747 objects[i] = getService(references[i]);
748 }
749 return objects;
750 }
751 }
752
753 /**
754 * Returns a service object for one of the services being tracked by this
755 * <code>ServiceTracker</code>.
756 *
757 * <p>
758 * If any services are being tracked, this implementation returns the result
759 * of calling <code>getService(getServiceReference())</code>.
760 *
761 * @return A service object or <code>null</code> if no services are being
762 * tracked.
763 */
764 public Object getService() {
765 Object service = cachedService;
766 if (service != null) {
767 if (DEBUG) {
768 System.out
769 .println("ServiceTracker.getService[cached]: "
770 + filter);
771 }
772 return service;
773 }
774 if (DEBUG) {
775 System.out.println("ServiceTracker.getService: " + filter);
776 }
777 ServiceReference reference = getServiceReference();
778 if (reference == null) {
779 return null;
780 }
781 return cachedService = getService(reference);
782 }
783
784 /**
785 * Remove a service from this <code>ServiceTracker</code>.
786 *
787 * The specified service will be removed from this
788 * <code>ServiceTracker</code>. If the specified service was being tracked
789 * then the <code>ServiceTrackerCustomizer.removedService</code> method will
790 * be called for that service.
791 *
792 * @param reference The reference to the service to be removed.
793 */
794 public void remove(ServiceReference reference) {
795 final Tracked t = tracked();
796 if (t == null) { /* if ServiceTracker is not open */
797 return;
798 }
799 t.untrack(reference, null);
800 }
801
802 /**
803 * Return the number of services being tracked by this
804 * <code>ServiceTracker</code>.
805 *
806 * @return The number of services being tracked.
807 */
808 public int size() {
809 final Tracked t = tracked();
810 if (t == null) { /* if ServiceTracker is not open */
811 return 0;
812 }
813 synchronized (t) {
814 return t.size();
815 }
816 }
817
818 /**
819 * Returns the tracking count for this <code>ServiceTracker</code>.
820 *
821 * The tracking count is initialized to 0 when this
822 * <code>ServiceTracker</code> is opened. Every time a service is added,
823 * modified or removed from this <code>ServiceTracker</code>, the tracking
824 * count is incremented.
825 *
826 * <p>
827 * The tracking count can be used to determine if this
828 * <code>ServiceTracker</code> has added, modified or removed a service by
829 * comparing a tracking count value previously collected with the current
830 * tracking count value. If the value has not changed, then no service has
831 * been added, modified or removed from this <code>ServiceTracker</code>
832 * since the previous tracking count was collected.
833 *
834 * @since 1.2
835 * @return The tracking count for this <code>ServiceTracker</code> or -1 if
836 * this <code>ServiceTracker</code> is not open.
837 */
838 public int getTrackingCount() {
839 final Tracked t = tracked();
840 if (t == null) { /* if ServiceTracker is not open */
841 return -1;
842 }
843 synchronized (t) {
844 return t.getTrackingCount();
845 }
846 }
847
848 /**
849 * Called by the Tracked object whenever the set of tracked services is
850 * modified. Clears the cache.
851 */
852 /*
853 * This method must not be synchronized since it is called by Tracked while
854 * Tracked is synchronized. We don't want synchronization interactions
855 * between the listener thread and the user thread.
856 */
857 void modified() {
858 cachedReference = null; /* clear cached value */
859 cachedService = null; /* clear cached value */
860 if (DEBUG) {
861 System.out.println("ServiceTracker.modified: " + filter);
862 }
863 }
864
865 /**
866 * Inner class which subclasses AbstractTracked. This class is the
867 * <code>ServiceListener</code> object for the tracker.
868 *
869 * @ThreadSafe
870 */
871 class Tracked extends AbstractTracked implements ServiceListener {
Marcel Offermansfcbc6232010-10-14 14:40:32 +0000872 /**
873 * A list of services that are currently hidden because there is an aspect available with a higher ranking.
874 * @GuardedBy this
875 */
Marcel Offermans7f687482010-05-11 09:12:34 +0000876 private final List m_hidden = new ArrayList();
Marcel Offermans1c944ec2010-10-11 13:55:02 +0000877
Marcel Offermans7f687482010-05-11 09:12:34 +0000878 /**
879 * Returns the highest hidden aspect for the specified service ID.
880 *
881 * @param serviceId the service ID
882 * @return a service reference, or <code>null</code> if there was no such service
883 */
884 private ServiceReference highestHidden(long serviceId) {
885 ServiceReference result = null;
886 int max = Integer.MIN_VALUE;
Marcel Offermansfcbc6232010-10-14 14:40:32 +0000887 synchronized (this) {
Marcel Offermansf9a7e342010-10-07 12:22:46 +0000888 for (int i = 0; i < m_hidden.size(); i++) {
889 ServiceReference ref = (ServiceReference) m_hidden.get(i);
890 Long sid = (Long) ref.getProperty(Constants.SERVICE_ID);
891 Long aid = (Long) ref.getProperty(DependencyManager.ASPECT);
892 if ((aid != null && aid.longValue() == serviceId)
893 || (aid == null && sid != null && sid.longValue() == serviceId)) {
894 Integer ranking = (Integer) ref.getProperty(Constants.SERVICE_RANKING);
895 int r = 0;
896 if (ranking != null) {
897 r = ranking.intValue();
898 }
899 if (r > max) {
900 max = r;
901 result = ref;
902 }
903 }
904 }
Marcel Offermans7f687482010-05-11 09:12:34 +0000905 }
906 return result;
907 }
908
909 /**
910 * Returns the highest tracked service for the specified service ID.
911 *
912 * @param serviceId the service ID
913 * @return a service reference, or <code>null</code> if there was no such service
914 */
915 private ServiceReference highestTracked(long serviceId) {
916 ServiceReference result = null;
917 int max = Integer.MIN_VALUE;
Marcel Offermansfcbc6232010-10-14 14:40:32 +0000918
919 synchronized (this) {
920 int length = size();
921 if (length == 0) {
922 return null;
923 }
924 Object[] trackedServices = getTracked(new ServiceReference[length]);
925 for (int i = 0; i < trackedServices.length; i++) {
926 ServiceReference ref = (ServiceReference) trackedServices[i];
927 Long sid = (Long) ref.getProperty(Constants.SERVICE_ID);
928 Long aid = (Long) ref.getProperty(DependencyManager.ASPECT);
929 if ((aid != null && aid.longValue() == serviceId)
930 || (aid == null && sid != null && sid.longValue() == serviceId)) {
931 Integer ranking = (Integer) ref.getProperty(Constants.SERVICE_RANKING);
932 int r = 0;
933 if (ranking != null) {
934 r = ranking.intValue();
935 }
936 if (r > max) {
937 max = r;
938 result = ref;
939 }
Marcel Offermans7f687482010-05-11 09:12:34 +0000940 }
941 }
Marcel Offermansfcbc6232010-10-14 14:40:32 +0000942 return result;
Marcel Offermans7f687482010-05-11 09:12:34 +0000943 }
Marcel Offermans7f687482010-05-11 09:12:34 +0000944 }
Marcel Offermans227dd712011-04-19 07:14:22 +0000945
946 private final HashMap m_highestTrackedCache = new HashMap();
947
948 private ServiceReference highestTrackedCache(long serviceId) {
949 Long sid = Long.valueOf(serviceId);
Xander Uiterlindena879ebe2012-03-21 14:09:19 +0000950 synchronized (this) {
951 TreeSet services = (TreeSet) m_highestTrackedCache.get(sid);
952 if (services != null && services.size() > 0) {
953 ServiceReference result = (ServiceReference) services.last();
954 return result;
955 }
956 }
Marcel Offermans227dd712011-04-19 07:14:22 +0000957 return null;
958 }
959
960 private void addHighestTrackedCache(ServiceReference reference) {
961 Long serviceId = ServiceUtil.getServiceIdObject(reference);
Xander Uiterlindena879ebe2012-03-21 14:09:19 +0000962 synchronized (this) {
963 TreeSet services = (TreeSet) m_highestTrackedCache.get(serviceId);
964 if (services == null) {
965 services = new TreeSet();
966 m_highestTrackedCache.put(serviceId, services);
967 }
968 services.add(reference);
969 }
Marcel Offermans227dd712011-04-19 07:14:22 +0000970 }
971
972 private void removeHighestTrackedCache(ServiceReference reference) {
973 Long serviceId = ServiceUtil.getServiceIdObject(reference);
Xander Uiterlindena879ebe2012-03-21 14:09:19 +0000974 synchronized (this) {
975 TreeSet services = (TreeSet) m_highestTrackedCache.get(serviceId);
976 if (services != null) {
977 services.remove(reference);
978 }
979 }
Marcel Offermans227dd712011-04-19 07:14:22 +0000980 }
981
982 private void clearHighestTrackedCache() {
Xander Uiterlindena879ebe2012-03-21 14:09:19 +0000983 synchronized (this) {
984 m_highestTrackedCache.clear();
985 }
Marcel Offermans227dd712011-04-19 07:14:22 +0000986 }
987
988 private final HashMap m_highestHiddenCache = new HashMap();
989
990 private ServiceReference highestHiddenCache(long serviceId) {
991 Long sid = Long.valueOf(serviceId);
Xander Uiterlindena879ebe2012-03-21 14:09:19 +0000992 synchronized (this) {
993 TreeSet services = (TreeSet) m_highestHiddenCache.get(sid);
994 if (services != null && services.size() > 0) {
995 ServiceReference result = (ServiceReference) services.last();
996 return result;
997 }
Marcel Offermans227dd712011-04-19 07:14:22 +0000998 }
999 return null;
1000 }
1001
1002 private void addHighestHiddenCache(ServiceReference reference) {
Marcel Offermans227dd712011-04-19 07:14:22 +00001003 Long serviceId = ServiceUtil.getServiceIdObject(reference);
Xander Uiterlindena879ebe2012-03-21 14:09:19 +00001004 synchronized (this) {
1005 TreeSet services = (TreeSet) m_highestHiddenCache.get(serviceId);
1006 if (services == null) {
1007 services = new TreeSet();
1008 m_highestHiddenCache.put(serviceId, services);
1009 }
1010 services.add(reference);
1011 }
Marcel Offermans227dd712011-04-19 07:14:22 +00001012 }
1013
1014 private void removeHighestHiddenCache(ServiceReference reference) {
Marcel Offermans227dd712011-04-19 07:14:22 +00001015 Long serviceId = ServiceUtil.getServiceIdObject(reference);
Xander Uiterlindena879ebe2012-03-21 14:09:19 +00001016 synchronized (this) {
1017 TreeSet services = (TreeSet) m_highestHiddenCache.get(serviceId);
1018 if (services != null) {
1019 services.remove(reference);
1020 }
1021 }
Marcel Offermans227dd712011-04-19 07:14:22 +00001022 }
Marcel Offermans7f687482010-05-11 09:12:34 +00001023
1024 /**
1025 * Hide a service reference, placing it in the list of hidden services.
1026 *
1027 * @param ref the service reference to add to the hidden list
1028 */
1029 private void hide(ServiceReference ref) {
Marcel Offermans227dd712011-04-19 07:14:22 +00001030 addHighestHiddenCache(ref);
Marcel Offermans7f687482010-05-11 09:12:34 +00001031 }
1032
1033 /**
1034 * Unhide a service reference, removing it from the list of hidden services.
1035 *
1036 * @param ref the service reference to remove from the hidden list
1037 */
1038 private void unhide(ServiceReference ref) {
Marcel Offermans227dd712011-04-19 07:14:22 +00001039 removeHighestHiddenCache(ref);
Marcel Offermans7f687482010-05-11 09:12:34 +00001040 }
1041
Marcel Offermansc854d1a2009-11-24 15:41:41 +00001042 /**
1043 * Tracked constructor.
1044 */
1045 Tracked() {
1046 super();
Marcel Offermans227dd712011-04-19 07:14:22 +00001047 setTracked(new HashMapCache());
Marcel Offermansc854d1a2009-11-24 15:41:41 +00001048 }
Marcel Offermanse5187f62010-08-20 12:12:49 +00001049
1050 void setInitial(Object[] list) {
1051 if (list == null) {
1052 return;
1053 }
Xander Uiterlinden5f9bab12013-10-11 09:23:15 +00001054 if (m_trackAllAspects) {
1055 // not hiding aspects
1056 super.setInitial(list);
1057 } else {
1058 Map highestRankedServiceMap = new HashMap(); // <Long, RankedService>
1059 for (int i = 0; i < list.length; i++) {
1060 ServiceReference sr = (ServiceReference) list[i];
1061 if (sr != null) {
1062 Long serviceId = ServiceUtil.getServiceIdAsLong(sr);
1063 int ranking = ServiceUtil.getRanking(sr);
1064
1065 RankedService rs = (RankedService) highestRankedServiceMap.get(serviceId);
1066 if (rs == null) {
1067 // the service did not exist yet in our map
1068 highestRankedServiceMap.put(serviceId, new RankedService(ranking, sr));
1069 }
1070 else if (ranking > rs.getRanking()) {
1071 // the service replaces a lower ranked one
1072 hide(rs.getServiceReference());
1073 rs.update(ranking, sr);
1074 }
1075 else {
1076 // the service does NOT replace a lower ranked one
1077 hide(sr);
1078 }
Marcel Offermansab4f4082011-11-16 09:30:41 +00001079 }
Xander Uiterlinden5f9bab12013-10-11 09:23:15 +00001080 }
1081 if (highestRankedServiceMap.size() > 0) {
1082 Object[] result = new Object[highestRankedServiceMap.size()];
1083 int index = 0;
1084 for(Iterator it = highestRankedServiceMap.entrySet().iterator(); it.hasNext(); ) {
1085 Entry entry = (Entry) it.next();
1086 result[index] = ((RankedService)entry.getValue()).getServiceReference();
1087 index++;
1088 }
1089 super.setInitial(result);
1090 }
Marcel Offermanse5187f62010-08-20 12:12:49 +00001091 }
1092 }
Marcel Offermansc854d1a2009-11-24 15:41:41 +00001093
1094 /**
1095 * <code>ServiceListener</code> method for the
1096 * <code>ServiceTracker</code> class. This method must NOT be
1097 * synchronized to avoid deadlock potential.
1098 *
1099 * @param event <code>ServiceEvent</code> object from the framework.
1100 */
1101 public void serviceChanged(final ServiceEvent event) {
Marcel Offermanscdd0f292011-05-10 09:58:52 +00001102 if (m_trackAllAspects) {
1103 serviceChangedIncludeAspects(event);
1104 }
1105 else {
1106 serviceChangedHideAspects(event);
1107 }
1108 }
1109
1110 public void serviceChangedIncludeAspects(final ServiceEvent event) {
1111 /*
1112 * Check if we had a delayed call (which could happen when we
1113 * close).
1114 */
1115 if (closed) {
1116 return;
1117 }
1118 final ServiceReference reference = event.getServiceReference();
1119 if (DEBUG) {
1120 System.out
1121 .println("ServiceTracker.Tracked.serviceChanged["
1122 + event.getType() + "]: " + reference);
1123 }
1124
1125 switch (event.getType()) {
1126 case ServiceEvent.REGISTERED :
1127 case ServiceEvent.MODIFIED :
1128 if (listenerFilter != null) { // service listener added with
1129 // filter
1130 track(reference, event);
1131 /*
1132 * If the customizer throws an unchecked exception, it
1133 * is safe to let it propagate
1134 */
1135 }
1136 else { // service listener added without filter
1137 if (filter.match(reference)) {
1138 track(reference, event);
1139 /*
1140 * If the customizer throws an unchecked exception,
1141 * it is safe to let it propagate
1142 */
1143 }
1144 else {
1145 untrack(reference, event);
1146 /*
1147 * If the customizer throws an unchecked exception,
1148 * it is safe to let it propagate
1149 */
1150 }
1151 }
1152 break;
1153 case 8 /* ServiceEvent.MODIFIED_ENDMATCH */ :
1154 case ServiceEvent.UNREGISTERING :
1155 untrack(reference, event);
1156 /*
1157 * If the customizer throws an unchecked exception, it is
1158 * safe to let it propagate
1159 */
1160 break;
1161 }
1162 }
1163
1164 public void serviceChangedHideAspects(final ServiceEvent event) {
Marcel Offermansc854d1a2009-11-24 15:41:41 +00001165 /*
1166 * Check if we had a delayed call (which could happen when we
1167 * close).
1168 */
1169 if (closed) {
1170 return;
1171 }
1172 final ServiceReference reference = event.getServiceReference();
1173 if (DEBUG) {
1174 System.out
1175 .println("ServiceTracker.Tracked.serviceChanged["
1176 + event.getType() + "]: " + reference);
1177 }
1178
Marcel Offermans1c944ec2010-10-11 13:55:02 +00001179 long sid = ServiceUtil.getServiceId(reference);
Marcel Offermansc854d1a2009-11-24 15:41:41 +00001180 switch (event.getType()) {
1181 case ServiceEvent.REGISTERED :
1182 case ServiceEvent.MODIFIED :
Marcel Offermans7f687482010-05-11 09:12:34 +00001183 ServiceReference higher = null;
1184 ServiceReference lower = null;
Marcel Offermans227dd712011-04-19 07:14:22 +00001185 ServiceReference sr = highestTrackedCache(sid);
Marcel Offermans1c944ec2010-10-11 13:55:02 +00001186 if (sr != null) {
1187 int ranking = ServiceUtil.getRanking(reference);
1188 int trackedRanking = ServiceUtil.getRanking(sr);
1189 if (ranking > trackedRanking) {
1190 // found a higher ranked one!
1191 if (DEBUG) {
1192 System.out.println("ServiceTracker.Tracked.serviceChanged[" + event.getType() + "]: Found a higher ranked aspect: " + ServiceUtil.toString(reference) + " vs " + ServiceUtil.toString(sr));
1193 }
1194 higher = sr;
1195 }
Marcel Offermansab4f4082011-11-16 09:30:41 +00001196 else if (ranking < trackedRanking) {
Marcel Offermans1c944ec2010-10-11 13:55:02 +00001197 // found lower ranked one!
1198 if (DEBUG) {
1199 System.out.println("ServiceTracker.Tracked.serviceChanged[" + event.getType() + "]: Found a lower ranked aspect: " + ServiceUtil.toString(reference) + " vs " + ServiceUtil.toString(sr));
1200 }
1201 lower = sr;
1202 }
Marcel Offermans7f687482010-05-11 09:12:34 +00001203 }
1204
1205 if (listenerFilter != null) { // service listener added with filter
1206 if (lower != null) {
1207 hide(reference);
1208 }
1209 else {
1210 try {
1211 track(reference, event);
1212 }
1213 finally {
1214 if (higher != null) {
1215 try {
1216 untrack(higher, null);
1217 }
1218 finally {
1219 hide(higher);
1220 }
1221 }
1222 }
1223 }
Marcel Offermansc854d1a2009-11-24 15:41:41 +00001224 /*
1225 * If the customizer throws an unchecked exception, it
1226 * is safe to let it propagate
1227 */
1228 }
1229 else { // service listener added without filter
1230 if (filter.match(reference)) {
Marcel Offermans7f687482010-05-11 09:12:34 +00001231 if (lower != null) {
1232 hide(reference);
1233 }
1234 else {
1235 try {
1236 track(reference, event);
1237 }
1238 finally {
1239 if (higher != null) {
1240 try {
1241 untrack(higher, null);
1242 }
1243 finally {
1244 hide(higher);
1245 }
1246 }
1247 }
1248 }
Marcel Offermansc854d1a2009-11-24 15:41:41 +00001249 /*
1250 * If the customizer throws an unchecked exception,
1251 * it is safe to let it propagate
1252 */
1253 }
1254 else {
Marcel Offermans227dd712011-04-19 07:14:22 +00001255 ServiceReference ht = highestTrackedCache(sid);
Marcel Offermans1c944ec2010-10-11 13:55:02 +00001256 if (reference.equals(ht)) {
1257 try {
Marcel Offermans227dd712011-04-19 07:14:22 +00001258 ServiceReference hh = highestHiddenCache(sid);
Marcel Offermans1c944ec2010-10-11 13:55:02 +00001259 if (hh != null) {
1260 unhide(hh);
1261 track(hh, null);
1262 }
1263 }
1264 finally {
1265 untrack(reference, event);
Marcel Offermans7f687482010-05-11 09:12:34 +00001266 }
1267 }
Marcel Offermans1c944ec2010-10-11 13:55:02 +00001268 else {
1269 unhide(reference);
Marcel Offermans7f687482010-05-11 09:12:34 +00001270 }
Marcel Offermansc854d1a2009-11-24 15:41:41 +00001271 /*
1272 * If the customizer throws an unchecked exception,
1273 * it is safe to let it propagate
1274 */
1275 }
1276 }
1277 break;
Marcel Offermans001db052009-12-08 08:58:40 +00001278 case 8 /* ServiceEvent.MODIFIED_ENDMATCH */ :
Marcel Offermansc854d1a2009-11-24 15:41:41 +00001279 case ServiceEvent.UNREGISTERING :
Marcel Offermans227dd712011-04-19 07:14:22 +00001280 ServiceReference ht = highestTrackedCache(sid);
Marcel Offermans1c944ec2010-10-11 13:55:02 +00001281 if (reference.equals(ht)) {
1282 try {
Marcel Offermans227dd712011-04-19 07:14:22 +00001283 ServiceReference hh = highestHiddenCache(sid);
Marcel Offermans1c944ec2010-10-11 13:55:02 +00001284 if (hh != null) {
1285 unhide(hh);
1286 track(hh, null);
1287 }
1288 }
1289 finally {
1290 untrack(reference, event);
1291 }
1292 }
1293 else {
1294 unhide(reference);
1295 }
Marcel Offermansc854d1a2009-11-24 15:41:41 +00001296 /*
1297 * If the customizer throws an unchecked exception, it is
1298 * safe to let it propagate
1299 */
1300 break;
1301 }
1302 }
1303
1304 /**
1305 * Increment the tracking count and tell the tracker there was a
1306 * modification.
1307 *
1308 * @GuardedBy this
1309 */
1310 void modified() {
1311 super.modified(); /* increment the modification count */
1312 ServiceTracker.this.modified();
1313 }
1314
1315 /**
1316 * Call the specific customizer adding method. This method must not be
1317 * called while synchronized on this object.
1318 *
1319 * @param item Item to be tracked.
1320 * @param related Action related object.
1321 * @return Customized object for the tracked item or <code>null</code>
1322 * if the item is not to be tracked.
1323 */
1324 Object customizerAdding(final Object item,
1325 final Object related) {
1326 return customizer.addingService((ServiceReference) item);
1327 }
1328
1329 void customizerAdded(final Object item, final Object related, final Object object) {
1330 customizer.addedService((ServiceReference) item, object);
1331 }
1332
1333 /**
1334 * Call the specific customizer modified method. This method must not be
1335 * called while synchronized on this object.
1336 *
1337 * @param item Tracked item.
1338 * @param related Action related object.
1339 * @param object Customized object for the tracked item.
1340 */
1341 void customizerModified(final Object item,
1342 final Object related, final Object object) {
1343 customizer.modifiedService((ServiceReference) item, object);
1344 }
1345
1346 /**
1347 * Call the specific customizer removed method. This method must not be
1348 * called while synchronized on this object.
1349 *
1350 * @param item Tracked item.
1351 * @param related Action related object.
1352 * @param object Customized object for the tracked item.
1353 */
1354 void customizerRemoved(final Object item,
1355 final Object related, final Object object) {
1356 customizer.removedService((ServiceReference) item, object);
1357 }
Marcel Offermans227dd712011-04-19 07:14:22 +00001358
1359 class HashMapCache extends HashMap {
1360 public Object put(Object key, Object value) {
1361 addHighestTrackedCache((ServiceReference) key);
1362 return super.put(key, value);
1363 }
1364
1365 public void putAll(Map m) {
1366 Iterator i = m.keySet().iterator();
1367 while (i.hasNext()) {
1368 addHighestTrackedCache((ServiceReference) i.next());
1369 }
1370 super.putAll(m);
1371 }
1372
1373 public Object remove(Object key) {
1374 removeHighestTrackedCache((ServiceReference) key);
1375 return super.remove(key);
1376 }
1377
1378 public void clear() {
1379 clearHighestTrackedCache();
1380 super.clear();
1381 }
1382 }
Marcel Offermansc854d1a2009-11-24 15:41:41 +00001383 }
1384
1385 /**
1386 * Subclass of Tracked which implements the AllServiceListener interface.
1387 * This class is used by the ServiceTracker if open is called with true.
1388 *
1389 * @since 1.3
1390 * @ThreadSafe
1391 */
1392 class AllTracked extends Tracked implements AllServiceListener {
1393 /**
1394 * AllTracked constructor.
1395 */
1396 AllTracked() {
1397 super();
Marcel Offermans227dd712011-04-19 07:14:22 +00001398 setTracked(new HashMapCache());
Marcel Offermansc854d1a2009-11-24 15:41:41 +00001399 }
1400 }
Marcel Offermansab4f4082011-11-16 09:30:41 +00001401
1402 /**
1403 * Holds a ranking and a service reference that can be updated if necessary.
1404 */
1405 private static final class RankedService {
1406 private int m_ranking;
1407 private ServiceReference m_serviceReference;
1408
1409 public RankedService(int ranking, ServiceReference serviceReference) {
1410 m_ranking = ranking;
1411 m_serviceReference = serviceReference;
1412 }
1413
1414 public void update(int ranking, ServiceReference serviceReference) {
1415 m_ranking = ranking;
1416 m_serviceReference = serviceReference;
1417 }
1418
1419 public int getRanking() {
1420 return m_ranking;
1421 }
1422
1423 public ServiceReference getServiceReference() {
1424 return m_serviceReference;
1425 }
1426 }
Marcel Offermansc854d1a2009-11-24 15:41:41 +00001427}