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