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 034package org.apache.camel.impl.osgi.tracker; 035 036import org.osgi.framework.Bundle; 037import org.osgi.framework.BundleContext; 038import org.osgi.framework.BundleEvent; 039import 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 */ 066public 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}