blob: 73c3a49fd6c1d3826c34a67b2dba8749621ac554 [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/*
4 * Copyright (c) OSGi Alliance (2007, 2008). All Rights Reserved.
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18
19import org.osgi.framework.Bundle;
20import org.osgi.framework.BundleContext;
21import org.osgi.framework.BundleEvent;
22import org.osgi.framework.SynchronousBundleListener;
23
24/**
25 * The <code>BundleTracker</code> class simplifies tracking bundles much like
26 * the <code>ServiceTracker</code> simplifies tracking services.
27 * <p>
28 * A <code>BundleTracker</code> is constructed with state criteria and a
29 * <code>BundleTrackerCustomizer</code> object. A <code>BundleTracker</code> can
30 * use the <code>BundleTrackerCustomizer</code> to select which bundles are
31 * tracked and to create a customized object to be tracked with the bundle. The
32 * <code>BundleTracker</code> can then be opened to begin tracking all bundles
33 * whose state matches the specified state criteria.
34 * <p>
35 * The <code>getBundles</code> method can be called to get the
36 * <code>Bundle</code> objects of the bundles being tracked. The
37 * <code>getObject</code> method can be called to get the customized object for
38 * a tracked bundle.
39 * <p>
40 * The <code>BundleTracker</code> class is thread-safe. It does not call a
41 * <code>BundleTrackerCustomizer</code> while holding any locks.
42 * <code>BundleTrackerCustomizer</code> implementations must also be
43 * thread-safe.
44 *
45 * @ThreadSafe
46 * @version $Revision: 5894 $
47 * @since 1.4
48 */
49public class BundleTracker implements BundleTrackerCustomizer {
50 /* set this to true to compile in debug messages */
51 static final boolean DEBUG = false;
52
53 /**
54 * The Bundle Context used by this <code>BundleTracker</code>.
55 */
56 protected final BundleContext context;
57
58 /**
59 * The <code>BundleTrackerCustomizer</code> object for this tracker.
60 */
61 final BundleTrackerCustomizer customizer;
62
63 /**
64 * Tracked bundles: <code>Bundle</code> object -> customized Object and
65 * <code>BundleListener</code> object
66 */
67 private volatile Tracked tracked;
68
69 /**
70 * Accessor method for the current Tracked object. This method is only
71 * intended to be used by the unsynchronized methods which do not modify the
72 * tracked field.
73 *
74 * @return The current Tracked object.
75 */
76 private Tracked tracked() {
77 return tracked;
78 }
79
80 /**
81 * State mask for bundles being tracked. This field contains the ORed values
82 * of the bundle states being tracked.
83 */
84 final int mask;
85
86 /**
87 * Create a <code>BundleTracker</code> for bundles whose state is present in
88 * the specified state mask.
89 *
90 * <p>
91 * Bundles whose state is present on the specified state mask will be
92 * tracked by this <code>BundleTracker</code>.
93 *
94 * @param context The <code>BundleContext</code> against which the tracking
95 * is done.
96 * @param stateMask The bit mask of the <code>OR</code>ing of the bundle
97 * states to be tracked.
98 * @param customizer The customizer object to call when bundles are added,
99 * modified, or removed in this <code>BundleTracker</code>. If
100 * customizer is <code>null</code>, then this
101 * <code>BundleTracker</code> will be used as the
102 * <code>BundleTrackerCustomizer</code> and this
103 * <code>BundleTracker</code> will call the
104 * <code>BundleTrackerCustomizer</code> methods on itself.
105 * @see Bundle#getState()
106 */
107 public BundleTracker(BundleContext context, int stateMask,
108 BundleTrackerCustomizer customizer) {
109 this.context = context;
110 this.mask = stateMask;
111 this.customizer = (customizer == null) ? this : customizer;
112 }
113
114 /**
115 * Open this <code>BundleTracker</code> and begin tracking bundles.
116 *
117 * <p>
118 * Bundle which match the state criteria specified when this
119 * <code>BundleTracker</code> was created are now tracked by this
120 * <code>BundleTracker</code>.
121 *
122 * @throws java.lang.IllegalStateException If the <code>BundleContext</code>
123 * with which this <code>BundleTracker</code> was created is no
124 * longer valid.
125 * @throws java.lang.SecurityException If the caller and this class do not
126 * have the appropriate
127 * <code>AdminPermission[context bundle,LISTENER]</code>, and the
128 * Java Runtime Environment supports permissions.
129 */
130 public void open() {
131 final Tracked t;
132 synchronized (this) {
133 if (tracked != null) {
134 return;
135 }
136 if (DEBUG) {
137 System.out.println("BundleTracker.open"); //$NON-NLS-1$
138 }
139 t = new Tracked();
140 synchronized (t) {
141 context.addBundleListener(t);
142 Bundle[] bundles = context.getBundles();
143 if (bundles != null) {
144 int length = bundles.length;
145 for (int i = 0; i < length; i++) {
146 int state = bundles[i].getState();
147 if ((state & mask) == 0) {
148 /* null out bundles whose states are not interesting */
149 bundles[i] = null;
150 }
151 }
152 /* set tracked with the initial bundles */
153 t.setInitial(bundles);
154 }
155 }
156 tracked = t;
157 }
158 /* Call tracked outside of synchronized region */
159 t.trackInitial(); /* process the initial references */
160 }
161
162 /**
163 * Close this <code>BundleTracker</code>.
164 *
165 * <p>
166 * This method should be called when this <code>BundleTracker</code> should
167 * end the tracking of bundles.
168 *
169 * <p>
170 * This implementation calls {@link #getBundles()} to get the list of
171 * tracked bundles to remove.
172 */
173 public void close() {
174 final Bundle[] bundles;
175 final Tracked outgoing;
176 synchronized (this) {
177 outgoing = tracked;
178 if (outgoing == null) {
179 return;
180 }
181 if (DEBUG) {
182 System.out.println("BundleTracker.close"); //$NON-NLS-1$
183 }
184 outgoing.close();
185 bundles = getBundles();
186 tracked = null;
187 try {
188 context.removeBundleListener(outgoing);
189 }
190 catch (IllegalStateException e) {
191 /* In case the context was stopped. */
192 }
193 }
194 if (bundles != null) {
195 for (int i = 0; i < bundles.length; i++) {
196 outgoing.untrack(bundles[i], null);
197 }
198 }
199 }
200
201 /**
202 * Default implementation of the
203 * <code>BundleTrackerCustomizer.addingBundle</code> method.
204 *
205 * <p>
206 * This method is only called when this <code>BundleTracker</code> has been
207 * constructed with a <code>null BundleTrackerCustomizer</code> argument.
208 *
209 * <p>
210 * This implementation simply returns the specified <code>Bundle</code>.
211 *
212 * <p>
213 * This method can be overridden in a subclass to customize the object to be
214 * tracked for the bundle being added.
215 *
216 * @param bundle The <code>Bundle</code> being added to this
217 * <code>BundleTracker</code> object.
218 * @param event The bundle event which caused this customizer method to be
219 * called or <code>null</code> if there is no bundle event associated
220 * with the call to this method.
221 * @return The specified bundle.
222 * @see BundleTrackerCustomizer#addingBundle(Bundle, BundleEvent)
223 */
224 public Object addingBundle(Bundle bundle, BundleEvent event) {
225 return bundle;
226 }
227
228 public void addedBundle(Bundle bundle, BundleEvent event, Object object) {
229 /* do nothing */
230 }
231
232 /**
233 * Default implementation of the
234 * <code>BundleTrackerCustomizer.modifiedBundle</code> method.
235 *
236 * <p>
237 * This method is only called when this <code>BundleTracker</code> has been
238 * constructed with a <code>null BundleTrackerCustomizer</code> argument.
239 *
240 * <p>
241 * This implementation does nothing.
242 *
243 * @param bundle The <code>Bundle</code> whose state has been modified.
244 * @param event The bundle event which caused this customizer method to be
245 * called or <code>null</code> if there is no bundle event associated
246 * with the call to this method.
247 * @param object The customized object for the specified Bundle.
248 * @see BundleTrackerCustomizer#modifiedBundle(Bundle, BundleEvent, Object)
249 */
250 public void modifiedBundle(Bundle bundle, BundleEvent event, Object object) {
251 /* do nothing */
252 }
253
254 /**
255 * Default implementation of the
256 * <code>BundleTrackerCustomizer.removedBundle</code> method.
257 *
258 * <p>
259 * This method is only called when this <code>BundleTracker</code> has been
260 * constructed with a <code>null BundleTrackerCustomizer</code> argument.
261 *
262 * <p>
263 * This implementation does nothing.
264 *
265 * @param bundle The <code>Bundle</code> being removed.
266 * @param event The bundle event which caused this customizer method to be
267 * called or <code>null</code> if there is no bundle event associated
268 * with the call to this method.
269 * @param object The customized object for the specified bundle.
270 * @see BundleTrackerCustomizer#removedBundle(Bundle, BundleEvent, Object)
271 */
272 public void removedBundle(Bundle bundle, BundleEvent event, Object object) {
273 /* do nothing */
274 }
275
276 /**
277 * Return an array of <code>Bundle</code>s for all bundles being tracked by
278 * this <code>BundleTracker</code>.
279 *
280 * @return An array of <code>Bundle</code>s or <code>null</code> if no
281 * bundles are being tracked.
282 */
283 public Bundle[] getBundles() {
284 final Tracked t = tracked();
285 if (t == null) { /* if BundleTracker is not open */
286 return null;
287 }
288 synchronized (t) {
289 int length = t.size();
290 if (length == 0) {
291 return null;
292 }
293 return (Bundle[]) t.getTracked(new Bundle[length]);
294 }
295 }
296
297 /**
298 * Returns the customized object for the specified <code>Bundle</code> if
299 * the specified bundle is being tracked by this <code>BundleTracker</code>.
300 *
301 * @param bundle The <code>Bundle</code> being tracked.
302 * @return The customized object for the specified <code>Bundle</code> or
303 * <code>null</code> if the specified <code>Bundle</code> is not
304 * being tracked.
305 */
306 public Object getObject(Bundle bundle) {
307 final Tracked t = tracked();
308 if (t == null) { /* if BundleTracker is not open */
309 return null;
310 }
311 synchronized (t) {
312 return t.getCustomizedObject(bundle);
313 }
314 }
315
316 /**
317 * Remove a bundle from this <code>BundleTracker</code>.
318 *
319 * The specified bundle will be removed from this <code>BundleTracker</code>
320 * . If the specified bundle was being tracked then the
321 * <code>BundleTrackerCustomizer.removedBundle</code> method will be called
322 * for that bundle.
323 *
324 * @param bundle The <code>Bundle</code> to be removed.
325 */
326 public void remove(Bundle bundle) {
327 final Tracked t = tracked();
328 if (t == null) { /* if BundleTracker is not open */
329 return;
330 }
331 t.untrack(bundle, null);
332 }
333
334 /**
335 * Return the number of bundles being tracked by this
336 * <code>BundleTracker</code>.
337 *
338 * @return The number of bundles being tracked.
339 */
340 public int size() {
341 final Tracked t = tracked();
342 if (t == null) { /* if BundleTracker is not open */
343 return 0;
344 }
345 synchronized (t) {
346 return t.size();
347 }
348 }
349
350 /**
351 * Returns the tracking count for this <code>BundleTracker</code>.
352 *
353 * The tracking count is initialized to 0 when this
354 * <code>BundleTracker</code> is opened. Every time a bundle is added,
355 * modified or removed from this <code>BundleTracker</code> the tracking
356 * count is incremented.
357 *
358 * <p>
359 * The tracking count can be used to determine if this
360 * <code>BundleTracker</code> has added, modified or removed a bundle by
361 * comparing a tracking count value previously collected with the current
362 * tracking count value. If the value has not changed, then no bundle has
363 * been added, modified or removed from this <code>BundleTracker</code>
364 * since the previous tracking count was collected.
365 *
366 * @return The tracking count for this <code>BundleTracker</code> or -1 if
367 * this <code>BundleTracker</code> is not open.
368 */
369 public int getTrackingCount() {
370 final Tracked t = tracked();
371 if (t == null) { /* if BundleTracker is not open */
372 return -1;
373 }
374 synchronized (t) {
375 return t.getTrackingCount();
376 }
377 }
378
379 /**
380 * Inner class which subclasses AbstractTracked. This class is the
381 * <code>SynchronousBundleListener</code> object for the tracker.
382 *
383 * @ThreadSafe
384 * @since 1.4
385 */
386 class Tracked extends AbstractTracked implements SynchronousBundleListener {
387 /**
388 * Tracked constructor.
389 */
390 Tracked() {
391 super();
392 }
393
394 /**
395 * <code>BundleListener</code> method for the <code>BundleTracker</code>
396 * class. This method must NOT be synchronized to avoid deadlock
397 * potential.
398 *
399 * @param event <code>BundleEvent</code> object from the framework.
400 */
401 public void bundleChanged(final BundleEvent event) {
402 /*
403 * Check if we had a delayed call (which could happen when we
404 * close).
405 */
406 if (closed) {
407 return;
408 }
409 final Bundle bundle = event.getBundle();
410 final int state = bundle.getState();
411 if (DEBUG) {
412 System.out
413 .println("BundleTracker.Tracked.bundleChanged[" + state + "]: " + bundle); //$NON-NLS-1$ //$NON-NLS-2$
414 }
415
416 if ((state & mask) != 0) {
417 track(bundle, event);
418 /*
419 * If the customizer throws an unchecked exception, it is safe
420 * to let it propagate
421 */
422 }
423 else {
424 untrack(bundle, event);
425 /*
426 * If the customizer throws an unchecked exception, it is safe
427 * to let it propagate
428 */
429 }
430 }
431
432 /**
433 * Call the specific customizer adding method. This method must not be
434 * called while synchronized on this object.
435 *
436 * @param item Item to be tracked.
437 * @param related Action related object.
438 * @return Customized object for the tracked item or <code>null</code>
439 * if the item is not to be tracked.
440 */
441 Object customizerAdding(final Object item,
442 final Object related) {
443 return customizer
444 .addingBundle((Bundle) item, (BundleEvent) related);
445 }
446
447 void customizerAdded(final Object item, final Object related, final Object object) {
448 customizer.addedBundle((Bundle) item, (BundleEvent) related, object);
449 }
450
451 /**
452 * Call the specific customizer modified method. This method must not be
453 * called while synchronized on this object.
454 *
455 * @param item Tracked item.
456 * @param related Action related object.
457 * @param object Customized object for the tracked item.
458 */
459 void customizerModified(final Object item,
460 final Object related, final Object object) {
461 customizer.modifiedBundle((Bundle) item, (BundleEvent) related,
462 object);
463 }
464
465 /**
466 * Call the specific customizer removed method. This method must not be
467 * called while synchronized on this object.
468 *
469 * @param item Tracked item.
470 * @param related Action related object.
471 * @param object Customized object for the tracked item.
472 */
473 void customizerRemoved(final Object item,
474 final Object related, final Object object) {
475 customizer.removedBundle((Bundle) item, (BundleEvent) related,
476 object);
477 }
478 }
479}