001 /**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018 /*
019 * Copyright (c) OSGi Alliance (2007, 2008). All Rights Reserved.
020 *
021 * Licensed under the Apache License, Version 2.0 (the "License");
022 * you may not use this file except in compliance with the License.
023 * You may obtain a copy of the License at
024 *
025 * http://www.apache.org/licenses/LICENSE-2.0
026 *
027 * Unless required by applicable law or agreed to in writing, software
028 * distributed under the License is distributed on an "AS IS" BASIS,
029 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
030 * See the License for the specific language governing permissions and
031 * limitations under the License.
032 */
033
034 package org.apache.camel.impl.osgi.tracker;
035
036 import org.osgi.framework.Bundle;
037 import org.osgi.framework.BundleContext;
038 import org.osgi.framework.BundleEvent;
039 import org.osgi.framework.SynchronousBundleListener;
040
041 /**
042 * The <code>BundleTracker</code> class simplifies tracking bundles much like
043 * the <code>ServiceTracker</code> simplifies tracking services.
044 * <p>
045 * A <code>BundleTracker</code> is constructed with state criteria and a
046 * <code>BundleTrackerCustomizer</code> object. A <code>BundleTracker</code> can
047 * use the <code>BundleTrackerCustomizer</code> to select which bundles are
048 * tracked and to create a customized object to be tracked with the bundle. The
049 * <code>BundleTracker</code> can then be opened to begin tracking all bundles
050 * whose state matches the specified state criteria.
051 * <p>
052 * The <code>getBundles</code> method can be called to get the
053 * <code>Bundle</code> objects of the bundles being tracked. The
054 * <code>getObject</code> method can be called to get the customized object for
055 * a tracked bundle.
056 * <p>
057 * The <code>BundleTracker</code> class is thread-safe. It does not call a
058 * <code>BundleTrackerCustomizer</code> while holding any locks.
059 * <code>BundleTrackerCustomizer</code> implementations must also be
060 * thread-safe.
061 *
062 * @ThreadSafe
063 * @version
064 * @since 1.4
065 */
066 public class BundleTracker implements BundleTrackerCustomizer {
067 /* set this to true to compile in debug messages */
068 static final boolean DEBUG = false;
069
070 /**
071 * The Bundle Context used by this <code>BundleTracker</code>.
072 */
073 protected final BundleContext context;
074
075 /**
076 * State mask for bundles being tracked. This field contains the ORed values
077 * of the bundle states being tracked.
078 */
079 final int mask;
080
081 /**
082 * The <code>BundleTrackerCustomizer</code> object for this tracker.
083 */
084 final BundleTrackerCustomizer customizer;
085
086 /**
087 * Tracked bundles: <code>Bundle</code> object -> customized Object and
088 * <code>BundleListener</code> object
089 */
090 private volatile Tracked tracked;
091
092 /**
093 * Create a <code>BundleTracker</code> for bundles whose state is present in
094 * the specified state mask.
095 * <p>
096 * Bundles whose state is present on the specified state mask will be
097 * tracked by this <code>BundleTracker</code>.
098 *
099 * @param context The <code>BundleContext</code> against which the tracking
100 * is done.
101 * @param stateMask The bit mask of the <code>OR</code>ing of the bundle
102 * states to be tracked.
103 * @param customizer The customizer object to call when bundles are added,
104 * modified, or removed in this <code>BundleTracker</code>. If
105 * customizer is <code>null</code>, then this
106 * <code>BundleTracker</code> will be used as the
107 * <code>BundleTrackerCustomizer</code> and this
108 * <code>BundleTracker</code> will call the
109 * <code>BundleTrackerCustomizer</code> methods on itself.
110 * @see Bundle#getState()
111 */
112 public BundleTracker(BundleContext context, int stateMask, BundleTrackerCustomizer customizer) {
113 this.context = context;
114 this.mask = stateMask;
115 this.customizer = (customizer == null) ? this : customizer;
116 }
117
118 /**
119 * Accessor method for the current Tracked object. This method is only
120 * intended to be used by the unsynchronized methods which do not modify the
121 * tracked field.
122 *
123 * @return The current Tracked object.
124 */
125 private Tracked tracked() {
126 return tracked;
127 }
128
129
130 /**
131 * Open this <code>BundleTracker</code> and begin tracking bundles.
132 * <p>
133 * Bundle which match the state criteria specified when this
134 * <code>BundleTracker</code> was created are now tracked by this
135 * <code>BundleTracker</code>.
136 *
137 * @throws java.lang.IllegalStateException If the <code>BundleContext</code>
138 * with which this <code>BundleTracker</code> was created is no
139 * longer valid.
140 * @throws java.lang.SecurityException If the caller and this class do not
141 * have the appropriate
142 * <code>AdminPermission[context bundle,LISTENER]</code>, and
143 * the Java Runtime Environment supports permissions.
144 */
145 public void open() {
146 final Tracked t;
147 synchronized (this) {
148 if (tracked != null) {
149 return;
150 }
151 if (DEBUG) {
152 System.out.println("BundleTracker.open");
153 }
154 t = new Tracked();
155 synchronized (t) {
156 context.addBundleListener(t);
157 Bundle[] bundles = context.getBundles();
158 if (bundles != null) {
159 int length = bundles.length;
160 for (int i = 0; i < length; i++) {
161 int state = bundles[i].getState();
162 if ((state & mask) == 0) {
163 /* null out bundles whose states are not interesting */
164 bundles[i] = null;
165 }
166 }
167 /* set tracked with the initial bundles */
168 t.setInitial(bundles);
169 }
170 }
171 tracked = t;
172 }
173 /* Call tracked outside of synchronized region */
174 t.trackInitial(); /* process the initial references */
175 }
176
177 /**
178 * Close this <code>BundleTracker</code>.
179 * <p>
180 * This method should be called when this <code>BundleTracker</code> should
181 * end the tracking of bundles.
182 * <p>
183 * This implementation calls {@link #getBundles()} to get the list of
184 * tracked bundles to remove.
185 */
186 public void close() {
187 final Bundle[] bundles;
188 final Tracked outgoing;
189 synchronized (this) {
190 outgoing = tracked;
191 if (outgoing == null) {
192 return;
193 }
194 if (DEBUG) {
195 System.out.println("BundleTracker.close");
196 }
197 outgoing.close();
198 bundles = getBundles();
199 tracked = null;
200 try {
201 context.removeBundleListener(outgoing);
202 } catch (IllegalStateException e) {
203 /* In case the context was stopped. */
204 }
205 }
206 if (bundles != null) {
207 for (Bundle bundle : bundles) {
208 outgoing.untrack(bundle, null);
209 }
210 }
211 }
212
213 /**
214 * Default implementation of the
215 * <code>BundleTrackerCustomizer.addingBundle</code> method.
216 * <p>
217 * This method is only called when this <code>BundleTracker</code> has been
218 * constructed with a <code>null BundleTrackerCustomizer</code> argument.
219 * <p>
220 * This implementation simply returns the specified <code>Bundle</code>.
221 * <p>
222 * This method can be overridden in a subclass to customize the object to be
223 * tracked for the bundle being added.
224 *
225 * @param bundle The <code>Bundle</code> being added to this
226 * <code>BundleTracker</code> object.
227 * @param event The bundle event which caused this customizer method to be
228 * called or <code>null</code> if there is no bundle event
229 * associated with the call to this method.
230 * @return The specified bundle.
231 * @see BundleTrackerCustomizer#addingBundle(Bundle, BundleEvent)
232 */
233 public Object addingBundle(Bundle bundle, BundleEvent event) {
234 return bundle;
235 }
236
237 /**
238 * Default implementation of the
239 * <code>BundleTrackerCustomizer.modifiedBundle</code> method.
240 * <p>
241 * This method is only called when this <code>BundleTracker</code> has been
242 * constructed with a <code>null BundleTrackerCustomizer</code> argument.
243 * <p>
244 * This implementation does nothing.
245 *
246 * @param bundle The <code>Bundle</code> whose state has been modified.
247 * @param event The bundle event which caused this customizer method to be
248 * called or <code>null</code> if there is no bundle event
249 * associated with the call to this method.
250 * @param object The customized object for the specified Bundle.
251 * @see BundleTrackerCustomizer#modifiedBundle(Bundle, BundleEvent, Object)
252 */
253 public void modifiedBundle(Bundle bundle, BundleEvent event, Object object) {
254 /* do nothing */
255 }
256
257 /**
258 * Default implementation of the
259 * <code>BundleTrackerCustomizer.removedBundle</code> method.
260 * <p>
261 * This method is only called when this <code>BundleTracker</code> has been
262 * constructed with a <code>null BundleTrackerCustomizer</code> argument.
263 * <p>
264 * This implementation does nothing.
265 *
266 * @param bundle The <code>Bundle</code> being removed.
267 * @param event The bundle event which caused this customizer method to be
268 * called or <code>null</code> if there is no bundle event
269 * associated with the call to this method.
270 * @param object The customized object for the specified bundle.
271 * @see BundleTrackerCustomizer#removedBundle(Bundle, BundleEvent, Object)
272 */
273 public void removedBundle(Bundle bundle, BundleEvent event, Object object) {
274 /* do nothing */
275 }
276
277 /**
278 * Return an array of <code>Bundle</code>s for all bundles being tracked by
279 * this <code>BundleTracker</code>.
280 *
281 * @return An array of <code>Bundle</code>s or <code>null</code> if no
282 * bundles are being tracked.
283 */
284 public Bundle[] getBundles() {
285 final Tracked t = tracked();
286 if (t == null) { /* if BundleTracker is not open */
287 return null;
288 }
289 synchronized (t) {
290 int length = t.size();
291 if (length == 0) {
292 return null;
293 }
294 return (Bundle[])t.getTracked(new Bundle[length]);
295 }
296 }
297
298 /**
299 * Returns the customized object for the specified <code>Bundle</code> if
300 * the specified bundle is being tracked by this <code>BundleTracker</code>.
301 *
302 * @param bundle The <code>Bundle</code> being tracked.
303 * @return The customized object for the specified <code>Bundle</code> or
304 * <code>null</code> if the specified <code>Bundle</code> is not
305 * being tracked.
306 */
307 public Object getObject(Bundle bundle) {
308 final Tracked t = tracked();
309 if (t == null) { /* if BundleTracker is not open */
310 return null;
311 }
312 synchronized (t) {
313 return t.getCustomizedObject(bundle);
314 }
315 }
316
317 /**
318 * Remove a bundle from this <code>BundleTracker</code>. The specified
319 * bundle will be removed from this <code>BundleTracker</code> . If the
320 * 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>. The
352 * tracking count is initialized to 0 when this <code>BundleTracker</code>
353 * is opened. Every time a bundle is added, modified or removed from this
354 * <code>BundleTracker</code> the tracking count is incremented.
355 * <p>
356 * The tracking count can be used to determine if this
357 * <code>BundleTracker</code> has added, modified or removed a bundle by
358 * comparing a tracking count value previously collected with the current
359 * tracking count value. If the value has not changed, then no bundle has
360 * been added, modified or removed from this <code>BundleTracker</code>
361 * since the previous tracking count was collected.
362 *
363 * @return The tracking count for this <code>BundleTracker</code> or -1 if
364 * this <code>BundleTracker</code> is not open.
365 */
366 public int getTrackingCount() {
367 final Tracked t = tracked();
368 if (t == null) { /* if BundleTracker is not open */
369 return -1;
370 }
371 synchronized (t) {
372 return t.getTrackingCount();
373 }
374 }
375
376 /**
377 * Inner class which subclasses AbstractTracked. This class is the
378 * <code>SynchronousBundleListener</code> object for the tracker.
379 *
380 * @ThreadSafe
381 * @since 1.4
382 */
383 class Tracked extends AbstractTracked implements SynchronousBundleListener {
384
385 /**
386 * <code>BundleListener</code> method for the <code>BundleTracker</code>
387 * class. This method must NOT be synchronized to avoid deadlock
388 * potential.
389 *
390 * @param event <code>BundleEvent</code> object from the framework.
391 */
392 public void bundleChanged(final BundleEvent event) {
393 /*
394 * Check if we had a delayed call (which could happen when we
395 * close).
396 */
397 if (closed) {
398 return;
399 }
400 final Bundle bundle = event.getBundle();
401 final int state = bundle.getState();
402 if (DEBUG) {
403 System.out.println("BundleTracker.Tracked.bundleChanged[" + state + "]: " + bundle);
404 }
405
406 if ((state & mask) != 0) {
407 track(bundle, event);
408 /*
409 * If the customizer throws an unchecked exception, it is safe
410 * to let it propagate
411 */
412 } else {
413 untrack(bundle, event);
414 /*
415 * If the customizer throws an unchecked exception, it is safe
416 * to let it propagate
417 */
418 }
419 }
420
421 /**
422 * Call the specific customizer adding method. This method must not be
423 * called while synchronized on this object.
424 *
425 * @param item Item to be tracked.
426 * @param related Action related object.
427 * @return Customized object for the tracked item or <code>null</code>
428 * if the item is not to be tracked.
429 */
430 Object customizerAdding(final Object item, final Object related) {
431 return customizer.addingBundle((Bundle)item, (BundleEvent)related);
432 }
433
434 /**
435 * Call the specific customizer modified method. This method must not be
436 * called while synchronized on this object.
437 *
438 * @param item Tracked item.
439 * @param related Action related object.
440 * @param object Customized object for the tracked item.
441 */
442 void customizerModified(final Object item, final Object related, final Object object) {
443 customizer.modifiedBundle((Bundle)item, (BundleEvent)related, object);
444 }
445
446 /**
447 * Call the specific customizer removed method. This method must not be
448 * called while synchronized on this object.
449 *
450 * @param item Tracked item.
451 * @param related Action related object.
452 * @param object Customized object for the tracked item.
453 */
454 void customizerRemoved(final Object item, final Object related, final Object object) {
455 customizer.removedBundle((Bundle)item, (BundleEvent)related, object);
456 }
457 }
458 }