aries-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From gno...@apache.org
Subject svn commit: r1421115 - /aries/trunk/util/util/src/main/java/org/apache/aries/util/tracker/hook/BundleHookBundleTracker.java
Date Thu, 13 Dec 2012 08:12:05 GMT
Author: gnodet
Date: Thu Dec 13 08:12:05 2012
New Revision: 1421115

URL: http://svn.apache.org/viewvc?rev=1421115&view=rev
Log:
[ARIES-983] Invalid logic in BundleHookBundleTracker which cause bundle events to be missed.
Also override methods because the base class isn't really used and would lead to invalid results if called.

Conflicts:
	util/util/src/main/java/org/apache/aries/util/tracker/hook/BundleHookBundleTracker.java

Modified:
    aries/trunk/util/util/src/main/java/org/apache/aries/util/tracker/hook/BundleHookBundleTracker.java

Modified: aries/trunk/util/util/src/main/java/org/apache/aries/util/tracker/hook/BundleHookBundleTracker.java
URL: http://svn.apache.org/viewvc/aries/trunk/util/util/src/main/java/org/apache/aries/util/tracker/hook/BundleHookBundleTracker.java?rev=1421115&r1=1421114&r2=1421115&view=diff
==============================================================================
--- aries/trunk/util/util/src/main/java/org/apache/aries/util/tracker/hook/BundleHookBundleTracker.java (original)
+++ aries/trunk/util/util/src/main/java/org/apache/aries/util/tracker/hook/BundleHookBundleTracker.java Thu Dec 13 08:12:05 2012
@@ -47,660 +47,914 @@ public class BundleHookBundleTracker<T> 
         Class c = EventHook.class;
     }
 
-  private final BundleContext context;
-  private final int mask;
-  private final BundleTrackerCustomizer customizer;
-  private Tracked tracked;
-  private ServiceRegistration sr;
-
-  public BundleHookBundleTracker(BundleContext context, int stateMask, BundleTrackerCustomizer customizer) {
-    super(context, stateMask, customizer);
-    this.context = context;
-    this.mask = stateMask;
-    this.customizer = customizer == null ? this : customizer;
-  }
-
-  @Override
-  public void open() {
-    synchronized (this) {
-      tracked = new Tracked();
-      Bundle[] bundles = context.getBundles();
-      if (bundles != null) {
-        int length = bundles.length;
-        for (int i = 0; i < length; i++) {
-          int state = bundles[i].getState();
-          if ((state & mask) == 0) {
-            /* null out bundles whose states are not interesting */
-            bundles[i] = null;
-          }
-        }
-        /* set tracked with the initial bundles */
-        tracked.setInitial(bundles);
-      }
-    }
-    tracked.trackInitial();
-    EventHook hook = new BundleEventHook(tracked);
-    sr = context.registerService(EventHook.class.getName(), hook, null);
-  }
-
-  @Override
-  public void close() {
-    sr.unregister();
-    tracked.close();
-    tracked = null;
-  }
-
-    @Override
-    public Object addingBundle(Bundle bundle, BundleEvent bundleEvent) {
-        if (bundleEvent == null) {
-            tracked.trackInitial(bundle);
-        }
-        return null;
-    }
-
-    @Override
-    public void modifiedBundle(Bundle bundle, BundleEvent bundleEvent, Object o) {
-    }
-
-    @Override
-    public void removedBundle(Bundle bundle, BundleEvent bundleEvent, Object o) {
-    }
-
-    private class BundleEventHook implements EventHook {
-    private Tracked tracked;
-
-    private BundleEventHook(Tracked tracked) {
-      this.tracked = tracked;
-    }
-
-    public void event(BundleEvent bundleEvent, Collection<BundleContext> bundleContexts) {
-      tracked.bundleChanged(bundleEvent);
-    }
-  }
-
-  /**
-   * Inner class which subclasses AbstractTracked. This class is the
-   * {@code SynchronousBundleListener} object for the tracker.
-   *
-   * @ThreadSafe
-   * @since 1.4
-   */
-  private final class Tracked extends AbstractTracked<Bundle, T, BundleEvent>
-      implements
-      SynchronousBundleListener {
-    /**
-     * Tracked constructor.
-     */
-    Tracked() {
-      super();
-    }
-
-    /**
-     * {@code BundleListener} method for the {@code BundleTracker}
-     * class. This method must NOT be synchronized to avoid deadlock
-     * potential.
-     *
-     * @param event {@code BundleEvent} object from the framework.
-     */
-    public void bundleChanged(final BundleEvent event) {
-      /*
-      * Check if we had a delayed call (which could happen when we
-      * close).
-      */
-      if (closed) {
-        return;
-      }
-      final Bundle bundle = event.getBundle();
-      final int state = bundle.getState();
-      if (DEBUG) {
-        System.out
-            .println("BundleTracker.Tracked.bundleChanged[" + state + "]: " + bundle); //$NON-NLS-1$ //$NON-NLS-2$
-      }
-
-      if ((state & mask) != 0) {
-        track(bundle, event);
-        /*
-        * If the customizer throws an unchecked exception, it is safe
-        * to let it propagate
-        */
-      } else {
-        untrack(bundle, event);
-        /*
-        * If the customizer throws an unchecked exception, it is safe
-        * to let it propagate
-        */
-      }
-    }
-
-    /**
-     * Call the specific customizer adding method. This method must not be
-     * called while synchronized on this object.
-     *
-     * @param item    Item to be tracked.
-     * @param related Action related object.
-     * @return Customized object for the tracked item or {@code null}
-     *         if the item is not to be tracked.
-     */
-    T customizerAdding(final Bundle item, final BundleEvent related) {
-      return (T)customizer.addingBundle(item, related);
-    }
-
-    /**
-     * Call the specific customizer modified method. This method must not be
-     * called while synchronized on this object.
-     *
-     * @param item    Tracked item.
-     * @param related Action related object.
-     * @param object  Customized object for the tracked item.
-     */
-    void customizerModified(final Bundle item, final BundleEvent related,
-                            final T object) {
-      customizer.modifiedBundle(item, related, object);
-    }
-
-    /**
-     * Call the specific customizer removed method. This method must not be
-     * called while synchronized on this object.
-     *
-     * @param item    Tracked item.
-     * @param related Action related object.
-     * @param object  Customized object for the tracked item.
-     */
-    void customizerRemoved(final Bundle item, final BundleEvent related,
-                           final T object) {
-      customizer.removedBundle(item, related, object);
-    }
-  }
-
-  /**
-   * Abstract class to track items. If a Tracker is reused (closed then reopened),
-   * then a new AbstractTracked object is used. This class acts a map of tracked
-   * item -> customized object. Subclasses of this class will act as the listener
-   * object for the tracker. This class is used to synchronize access to the
-   * tracked items. This is not a public class. It is only for use by the
-   * implementation of the Tracker class.
-   *
-   * @param <S> The tracked item. It is the key.
-   * @param <T> The value mapped to the tracked item.
-   * @param <R> The reason the tracked item is being tracked or untracked.
-   * @version $Id: 79452e6c28683021f2bcf11d3689ec75c6b5642f $
-   * @ThreadSafe
-   * @since 1.4
-   */
-  private static abstract class AbstractTracked<S, T, R> {
     /* set this to true to compile in debug messages */
-    static final boolean DEBUG = false;
+    static final boolean				DEBUG	= false;
 
     /**
-     * Map of tracked items to customized objects.
-     *
-     * @GuardedBy this
+     * The Bundle Context used by this {@code BundleTracker}.
      */
-    private final Map<S, T> tracked;
+    protected final BundleContext context;
 
     /**
-     * Modification count. This field is initialized to zero and incremented by
-     * modified.
-     *
-     * @GuardedBy this
+     * The {@code BundleTrackerCustomizer} object for this tracker.
      */
-    private int trackingCount;
-
+    final BundleTrackerCustomizer customizer;
     /**
-     * List of items in the process of being added. This is used to deal with
-     * nesting of events. Since events may be synchronously delivered, events
-     * can be nested. For example, when processing the adding of a service and
-     * the customizer causes the service to be unregistered, notification to the
-     * nested call to untrack that the service was unregistered can be made to
-     * the track method.
-     * <p/>
-     * Since the ArrayList implementation is not synchronized, all access to
-     * this list must be protected by the same synchronized object for
-     * thread-safety.
-     *
-     * @GuardedBy this
+     * Tracked bundles: {@code Bundle} object -> customized Object and
+     * {@code BundleListener} object
      */
-    private final List<S> adding;
+    private volatile Tracked tracked;
 
     /**
-     * true if the tracked object is closed.
-     * <p/>
-     * This field is volatile because it is set by one thread and read by
-     * another.
+     * Accessor method for the current Tracked object. This method is only
+     * intended to be used by the unsynchronized methods which do not modify the
+     * tracked field.
+     *
+     * @return The current Tracked object.
      */
-    volatile boolean closed;
+    private Tracked tracked() {
+        return tracked;
+    }
 
     /**
-     * Initial list of items for the tracker. This is used to correctly process
-     * the initial items which could be modified before they are tracked. This
-     * is necessary since the initial set of tracked items are not "announced"
-     * by events and therefore the event which makes the item untracked could be
-     * delivered before we track the item.
-     * <p/>
-     * An item must not be in both the initial and adding lists at the same
-     * time. An item must be moved from the initial list to the adding list
-     * "atomically" before we begin tracking it.
-     * <p/>
-     * Since the LinkedList implementation is not synchronized, all access to
-     * this list must be protected by the same synchronized object for
-     * thread-safety.
-     *
-     * @GuardedBy this
+     * State mask for bundles being tracked. This field contains the ORed values
+     * of the bundle states being tracked.
      */
-    private final LinkedList<S> initial;
+    private final int mask;
 
     /**
-     * AbstractTracked constructor.
+     * BundleHook service registration
      */
-    AbstractTracked() {
-      tracked = new HashMap<S, T>();
-      trackingCount = 0;
-      adding = new ArrayList<S>(6);
-      initial = new LinkedList<S>();
-      closed = false;
-    }
+    private ServiceRegistration sr;
 
     /**
-     * Set initial list of items into tracker before events begin to be
-     * received.
-     * <p/>
-     * This method must be called from Tracker's open method while synchronized
-     * on this object in the same synchronized block as the add listener call.
+     * Create a {@code BundleTracker} for bundles whose state is present in the
+     * specified state mask.
      *
-     * @param list The initial list of items to be tracked. {@code null}
-     *             entries in the list are ignored.
-     * @GuardedBy this
+     * <p>
+     * Bundles whose state is present on the specified state mask will be
+     * tracked by this {@code BundleTracker}.
+     *
+     * @param context The {@code BundleContext} against which the tracking is
+     *        done.
+     * @param stateMask The bit mask of the {@code OR}ing of the bundle states
+     *        to be tracked.
+     * @param customizer The customizer object to call when bundles are added,
+     *        modified, or removed in this {@code BundleTracker}. If customizer
+     *        is {@code null}, then this {@code BundleTracker} will be used as
+     *        the {@code BundleTrackerCustomizer} and this {@code BundleTracker}
+     *        will call the {@code BundleTrackerCustomizer} methods on itself.
+     * @see Bundle#getState()
      */
-    void setInitial(S[] list) {
-      if (list == null) {
-        return;
-      }
-      for (S item : list) {
-        if (item == null) {
-          continue;
-        }
-        if (DEBUG) {
-          System.out.println("AbstractTracked.setInitial: " + item); //$NON-NLS-1$
-        }
-        initial.add(item);
-      }
+    public BundleHookBundleTracker(BundleContext context, int stateMask, BundleTrackerCustomizer customizer) {
+        super(context, stateMask, customizer);
+        this.context = context;
+        this.mask = stateMask;
+        this.customizer = customizer == null ? this : customizer;
     }
 
     /**
-     * Track the initial list of items. This is called after events can begin to
-     * be received.
-     * <p/>
-     * This method must be called from Tracker's open method while not
-     * synchronized on this object after the add listener call.
+     * Open this {@code BundleTracker} and begin tracking bundles.
+     *
+     * <p>
+     * Bundle which match the state criteria specified when this
+     * {@code BundleTracker} was created are now tracked by this
+     * {@code BundleTracker}.
+     *
+     * @throws java.lang.IllegalStateException If the {@code BundleContext} with
+     *         which this {@code BundleTracker} was created is no longer valid.
+     * @throws java.lang.SecurityException If the caller and this class do not
+     *         have the appropriate
+     *         {@code AdminPermission[context bundle,LISTENER]}, and the Java
+     *         Runtime Environment supports permissions.
      */
-    void trackInitial() {
-      while (true) {
-        S item;
+    @Override
+    public void open() {
+        final Tracked t;
         synchronized (this) {
-          if (closed || (initial.size() == 0)) {
-            /*
-                        * if there are no more initial items
-                        */
-            return; /* we are done */
-          }
-          /*
-                   * move the first item from the initial list to the adding list
-                   * within this synchronized block.
-                   */
-          item = initial.removeFirst();
-          if (tracked.get(item) != null) {
-            /* if we are already tracking this item */
-            if (DEBUG) {
-              System.out
-                  .println("AbstractTracked.trackInitial[already tracked]: " + item); //$NON-NLS-1$
+            if (tracked != null) {
+                return;
             }
-            continue; /* skip this item */
-          }
-          if (adding.contains(item)) {
-            /*
-                        * if this item is already in the process of being added.
-                        */
-            if (DEBUG) {
-              System.out
-                  .println("AbstractTracked.trackInitial[already adding]: " + item); //$NON-NLS-1$
+            t = new Tracked();
+            synchronized (t) {
+                EventHook hook = new BundleEventHook(t);
+                sr = context.registerService(EventHook.class.getName(), hook, null);
+                Bundle[] bundles = context.getBundles();
+                if (bundles != null) {
+                    int length = bundles.length;
+                    for (int i = 0; i < length; i++) {
+                        int state = bundles[i].getState();
+                        if ((state & mask) == 0) {
+                            /* null out bundles whose states are not interesting */
+                            bundles[i] = null;
+                        }
+                    }
+                    /* set tracked with the initial bundles */
+                    t.setInitial(bundles);
+                }
             }
-            continue; /* skip this item */
-          }
-          adding.add(item);
+            tracked = t;
         }
-        if (DEBUG) {
-          System.out.println("AbstractTracked.trackInitial: " + item); //$NON-NLS-1$
-        }
-        trackAdding(item, null); /*
-									 * Begin tracking it. We call trackAdding
-									 * since we have already put the item in the
-									 * adding list.
-									 */
-      }
+        /* Call tracked outside of synchronized region */
+        tracked.trackInitial(); /* process the initial references */
     }
 
-      void trackInitial(S item) {
+    /**
+     * Close this {@code BundleTracker}.
+     *
+     * <p>
+     * This method should be called when this {@code BundleTracker} should end
+     * the tracking of bundles.
+     *
+     * <p>
+     * This implementation calls {@link #getBundles()} to get the list of
+     * tracked bundles to remove.
+     */
+    @Override
+    public void close() {
+        final Bundle[] bundles;
+        final Tracked outgoing;
         synchronized (this) {
-          if (closed) {
-            /*
-                        * if there are no more initial items
-                        */
-            return; /* we are done */
-          }
-          /*
-                   * move the first item from the initial list to the adding list
-                   * within this synchronized block.
-                   */
-          item = initial.removeFirst();
-          if (tracked.get(item) != null) {
-            /* if we are already tracking this item */
-            if (DEBUG) {
-              System.out
-                  .println("AbstractTracked.trackInitial[already tracked]: " + item); //$NON-NLS-1$
+            outgoing = tracked;
+            if (outgoing == null) {
+                return;
             }
-            return; /* skip this item */
-          }
-          if (adding.contains(item)) {
-            /*
-                        * if this item is already in the process of being added.
-                        */
             if (DEBUG) {
-              System.out
-                  .println("AbstractTracked.trackInitial[already adding]: " + item); //$NON-NLS-1$
+                System.out.println("BundleTracker.close"); //$NON-NLS-1$
+            }
+            tracked.close();
+            bundles = getBundles();
+            tracked = null;
+            try {
+                sr.unregister();
+            } catch (IllegalStateException e) {
+				/* In case the context was stopped. */
             }
-            return; /* skip this item */
-          }
-          adding.add(item);
         }
-        if (DEBUG) {
-          System.out.println("AbstractTracked.trackInitial: " + item); //$NON-NLS-1$
+        if (bundles != null) {
+            for (int i = 0; i < bundles.length; i++) {
+                outgoing.untrack(bundles[i], null);
+            }
         }
-        trackAdding(item, null); /*
-									 * Begin tracking it. We call trackAdding
-									 * since we have already put the item in the
-									 * adding list.
-									 */
     }
 
     /**
-     * Called by the owning Tracker object when it is closed.
+     * Default implementation of the
+     * {@code BundleTrackerCustomizer.addingBundle} method.
+     *
+     * <p>
+     * This method is only called when this {@code BundleTracker} has been
+     * constructed with a {@code null BundleTrackerCustomizer} argument.
+     *
+     * <p>
+     * This implementation simply returns the specified {@code Bundle}.
+     *
+     * <p>
+     * This method can be overridden in a subclass to customize the object to be
+     * tracked for the bundle being added.
+     *
+     * @param bundle The {@code Bundle} being added to this
+     *        {@code BundleTracker} object.
+     * @param event The bundle event which caused this customizer method to be
+     *        called or {@code null} if there is no bundle event associated with
+     *        the call to this method.
+     * @return The specified bundle.
+     * @see BundleTrackerCustomizer#addingBundle(Bundle, BundleEvent)
      */
-    void close() {
-      closed = true;
+    @Override
+    public Object addingBundle(Bundle bundle, BundleEvent event) {
+        T result = (T) bundle;
+        return result;
     }
 
     /**
-     * Begin to track an item.
+     * Default implementation of the
+     * {@code BundleTrackerCustomizer.modifiedBundle} method.
+     *
+     * <p>
+     * This method is only called when this {@code BundleTracker} has been
+     * constructed with a {@code null BundleTrackerCustomizer} argument.
      *
-     * @param item    Item to be tracked.
-     * @param related Action related object.
+     * <p>
+     * This implementation does nothing.
+     *
+     * @param bundle The {@code Bundle} whose state has been modified.
+     * @param event The bundle event which caused this customizer method to be
+     *        called or {@code null} if there is no bundle event associated with
+     *        the call to this method.
+     * @param object The customized object for the specified Bundle.
+     * @see BundleTrackerCustomizer#modifiedBundle(Bundle, BundleEvent, Object)
      */
-    void track(final S item, final R related) {
-      final T object;
-      synchronized (this) {
-        if (closed) {
-          return;
-        }
-        object = tracked.get(item);
-        if (object == null) { /* we are not tracking the item */
-          if (adding.contains(item)) {
-            /* if this item is already in the process of being added. */
-            if (DEBUG) {
-              System.out
-                  .println("AbstractTracked.track[already adding]: " + item); //$NON-NLS-1$
-            }
-            return;
-          }
-          adding.add(item); /* mark this item is being added */
-        } else { /* we are currently tracking this item */
-          if (DEBUG) {
-            System.out
-                .println("AbstractTracked.track[modified]: " + item); //$NON-NLS-1$
-          }
-          modified(); /* increment modification count */
-        }
-      }
-
-      if (object == null) { /* we are not tracking the item */
-        trackAdding(item, related);
-      } else {
-        /* Call customizer outside of synchronized region */
-        customizerModified(item, related, object);
-        /*
-               * If the customizer throws an unchecked exception, it is safe to
-               * let it propagate
-               */
-      }
+    @Override
+    public void modifiedBundle(Bundle bundle, BundleEvent event, Object object) {
+		/* do nothing */
     }
 
     /**
-     * Common logic to add an item to the tracker used by track and
-     * trackInitial. The specified item must have been placed in the adding list
-     * before calling this method.
-     *
-     * @param item    Item to be tracked.
-     * @param related Action related object.
-     */
-    private void trackAdding(final S item, final R related) {
-      if (DEBUG) {
-        System.out.println("AbstractTracked.trackAdding: " + item); //$NON-NLS-1$
-      }
-      T object = null;
-      boolean becameUntracked = false;
-      /* Call customizer outside of synchronized region */
-      try {
-        object = customizerAdding(item, related);
-        /*
-               * If the customizer throws an unchecked exception, it will
-               * propagate after the finally
-               */
-      } finally {
-        synchronized (this) {
-          if (adding.remove(item) && !closed) {
-            /*
-                        * if the item was not untracked during the customizer
-                        * callback
-                        */
-            if (object != null) {
-              tracked.put(item, object);
-              modified(); /* increment modification count */
-              notifyAll(); /* notify any waiters */
-            }
-          } else {
-            becameUntracked = true;
-          }
-        }
-      }
-      /*
-          * The item became untracked during the customizer callback.
-          */
-      if (becameUntracked && (object != null)) {
-        if (DEBUG) {
-          System.out
-              .println("AbstractTracked.trackAdding[removed]: " + item); //$NON-NLS-1$
-        }
-        /* Call customizer outside of synchronized region */
-        customizerRemoved(item, related, object);
-        /*
-               * If the customizer throws an unchecked exception, it is safe to
-               * let it propagate
-               */
-      }
-    }
-
-    /**
-     * Discontinue tracking the item.
-     *
-     * @param item    Item to be untracked.
-     * @param related Action related object.
-     */
-    void untrack(final S item, final R related) {
-      final T object;
-      synchronized (this) {
-        if (initial.remove(item)) { /*
-										 * if this item is already in the list
-										 * of initial references to process
-										 */
-          if (DEBUG) {
-            System.out
-                .println("AbstractTracked.untrack[removed from initial]: " + item); //$NON-NLS-1$
-          }
-          return; /*
-						 * we have removed it from the list and it will not be
-						 * processed
-						 */
-        }
-
-        if (adding.remove(item)) { /*
-										 * if the item is in the process of
-										 * being added
-										 */
-          if (DEBUG) {
-            System.out
-                .println("AbstractTracked.untrack[being added]: " + item); //$NON-NLS-1$
-          }
-          return; /*
-						 * in case the item is untracked while in the process of
-						 * adding
-						 */
-        }
-        object = tracked.remove(item); /*
-											 * must remove from tracker before
-											 * calling customizer callback
-											 */
-        if (object == null) { /* are we actually tracking the item */
-          return;
-        }
-        modified(); /* increment modification count */
-      }
-      if (DEBUG) {
-        System.out.println("AbstractTracked.untrack[removed]: " + item); //$NON-NLS-1$
-      }
-      /* Call customizer outside of synchronized region */
-      customizerRemoved(item, related, object);
-      /*
-          * If the customizer throws an unchecked exception, it is safe to let it
-          * propagate
-          */
-    }
-
-    /**
-     * Returns the number of tracked items.
-     *
-     * @return The number of tracked items.
-     * @GuardedBy this
-     */
-    int size() {
-      return tracked.size();
+     * Default implementation of the
+     * {@code BundleTrackerCustomizer.removedBundle} method.
+     *
+     * <p>
+     * This method is only called when this {@code BundleTracker} has been
+     * constructed with a {@code null BundleTrackerCustomizer} argument.
+     *
+     * <p>
+     * This implementation does nothing.
+     *
+     * @param bundle The {@code Bundle} being removed.
+     * @param event The bundle event which caused this customizer method to be
+     *        called or {@code null} if there is no bundle event associated with
+     *        the call to this method.
+     * @param object The customized object for the specified bundle.
+     * @see BundleTrackerCustomizer#removedBundle(Bundle, BundleEvent, Object)
+     */
+    @Override
+    public void removedBundle(Bundle bundle, BundleEvent event, Object object) {
+        /* do nothing */
     }
 
     /**
-     * Returns if the tracker is empty.
+     * Return an array of {@code Bundle}s for all bundles being tracked by this
+     * {@code BundleTracker}.
      *
-     * @return Whether the tracker is empty.
-     * @GuardedBy this
-     * @since 1.5
+     * @return An array of {@code Bundle}s or {@code null} if no bundles are
+     *         being tracked.
      */
-    boolean isEmpty() {
-      return tracked.isEmpty();
+    public Bundle[] getBundles() {
+        final Tracked t = tracked();
+        if (t == null) { /* if BundleTracker is not open */
+            return null;
+        }
+        synchronized (t) {
+            int length = t.size();
+            if (length == 0) {
+                return null;
+            }
+            return t.copyKeys(new Bundle[length]);
+        }
     }
 
     /**
-     * Return the customized object for the specified item
+     * Returns the customized object for the specified {@code Bundle} if the
+     * specified bundle is being tracked by this {@code BundleTracker}.
      *
-     * @param item The item to lookup in the map
-     * @return The customized object for the specified item.
-     * @GuardedBy this
+     * @param bundle The {@code Bundle} being tracked.
+     * @return The customized object for the specified {@code Bundle} or
+     *         {@code null} if the specified {@code Bundle} is not being
+     *         tracked.
      */
-    T getCustomizedObject(final S item) {
-      return tracked.get(item);
+    public T getObject(Bundle bundle) {
+        final Tracked t = tracked();
+        if (t == null) { /* if BundleTracker is not open */
+            return null;
+        }
+        synchronized (t) {
+            return t.getCustomizedObject(bundle);
+        }
     }
 
     /**
-     * Copy the tracked items into an array.
+     * Remove a bundle from this {@code BundleTracker}.
      *
-     * @param list An array to contain the tracked items.
-     * @return The specified list if it is large enough to hold the tracked
-     *         items or a new array large enough to hold the tracked items.
-     * @GuardedBy this
+     * The specified bundle will be removed from this {@code BundleTracker} . If
+     * the specified bundle was being tracked then the
+     * {@code BundleTrackerCustomizer.removedBundle} method will be called for
+     * that bundle.
+     *
+     * @param bundle The {@code Bundle} to be removed.
      */
-    S[] copyKeys(final S[] list) {
-      return tracked.keySet().toArray(list);
+    public void remove(Bundle bundle) {
+        final Tracked t = tracked();
+        if (t == null) { /* if BundleTracker is not open */
+            return;
+        }
+        t.untrack(bundle, null);
     }
 
     /**
-     * Increment the modification count. If this method is overridden, the
-     * overriding method MUST call this method to increment the tracking count.
+     * Return the number of bundles being tracked by this {@code BundleTracker}.
      *
-     * @GuardedBy this
+     * @return The number of bundles being tracked.
      */
-    void modified() {
-      trackingCount++;
+    public int size() {
+        final Tracked t = tracked();
+        if (t == null) { /* if BundleTracker is not open */
+            return 0;
+        }
+        synchronized (t) {
+            return t.size();
+        }
     }
 
     /**
-     * Returns the tracking count for this {@code ServiceTracker} object.
-     * <p/>
-     * The tracking count is initialized to 0 when this object is opened. Every
-     * time an item is added, modified or removed from this object the tracking
-     * count is incremented.
+     * Returns the tracking count for this {@code BundleTracker}.
+     *
+     * The tracking count is initialized to 0 when this {@code BundleTracker} is
+     * opened. Every time a bundle is added, modified or removed from this
+     * {@code BundleTracker} the tracking count is incremented.
      *
-     * @return The tracking count for this object.
-     * @GuardedBy this
+     * <p>
+     * The tracking count can be used to determine if this {@code BundleTracker}
+     * has added, modified or removed a bundle by comparing a tracking count
+     * value previously collected with the current tracking count value. If the
+     * value has not changed, then no bundle has been added, modified or removed
+     * from this {@code BundleTracker} since the previous tracking count was
+     * collected.
+     *
+     * @return The tracking count for this {@code BundleTracker} or -1 if this
+     *         {@code BundleTracker} is not open.
      */
-    int getTrackingCount() {
-      return trackingCount;
+    public int getTrackingCount() {
+        final Tracked t = tracked();
+        if (t == null) { /* if BundleTracker is not open */
+            return -1;
+        }
+        synchronized (t) {
+            return t.getTrackingCount();
+        }
     }
 
     /**
-     * Copy the tracked items and associated values into the specified map.
+     * Return a {@code Map} with the {@code Bundle}s and customized objects for
+     * all bundles being tracked by this {@code BundleTracker}.
      *
-     * @param <M> Type of {@code Map} to hold the tracked items and
-     *            associated values.
-     * @param map The map into which to copy the tracked items and associated
-     *            values. This map must not be a user provided map so that user code
-     *            is not executed while synchronized on this.
-     * @return The specified map.
-     * @GuardedBy this
+     * @return A {@code Map} with the {@code Bundle}s and customized objects for
+     *         all services being tracked by this {@code BundleTracker}. If no
+     *         bundles are being tracked, then the returned map is empty.
      * @since 1.5
      */
-    <M extends Map<? super S, ? super T>> M copyEntries(final M map) {
-      map.putAll(tracked);
-      return map;
+    public Map<Bundle, T> getTracked() {
+        Map<Bundle, T> map = new HashMap<Bundle, T>();
+        final Tracked t = tracked();
+        if (t == null) { /* if BundleTracker is not open */
+            return map;
+        }
+        synchronized (t) {
+            return t.copyEntries(map);
+        }
     }
 
     /**
-     * Call the specific customizer adding method. This method must not be
-     * called while synchronized on this object.
+     * Return if this {@code BundleTracker} is empty.
      *
-     * @param item    Item to be tracked.
-     * @param related Action related object.
-     * @return Customized object for the tracked item or {@code null} if
-     *         the item is not to be tracked.
+     * @return {@code true} if this {@code BundleTracker} is not tracking any
+     *         bundles.
+     * @since 1.5
      */
-    abstract T customizerAdding(final S item, final R related);
+    public boolean isEmpty() {
+        final Tracked t = tracked();
+        if (t == null) { /* if BundleTracker is not open */
+            return true;
+        }
+        synchronized (t) {
+            return t.isEmpty();
+        }
+    }
 
-    /**
-     * Call the specific customizer modified method. This method must not be
-     * called while synchronized on this object.
-     *
-     * @param item    Tracked item.
-     * @param related Action related object.
-     * @param object  Customized object for the tracked item.
-     */
-    abstract void customizerModified(final S item, final R related,
-                                     final T object);
+    private class BundleEventHook implements EventHook {
+        private final Tracked tracked;
+
+        private BundleEventHook(Tracked tracked) {
+            this.tracked = tracked;
+        }
+
+        public void event(BundleEvent bundleEvent, Collection bundleContexts) {
+            tracked.bundleChanged(bundleEvent);
+        }
+    }
 
     /**
-     * Call the specific customizer removed method. This method must not be
-     * called while synchronized on this object.
+     * Inner class which subclasses AbstractTracked. This class is the
+     * {@code SynchronousBundleListener} object for the tracker.
      *
-     * @param item    Tracked item.
-     * @param related Action related object.
-     * @param object  Customized object for the tracked item.
+     * @ThreadSafe
+     * @since 1.4
      */
-    abstract void customizerRemoved(final S item, final R related,
-                                    final T object);
-  }
+    private final class Tracked extends AbstractTracked<Bundle, T, BundleEvent> implements SynchronousBundleListener {
+        /**
+         * Tracked constructor.
+         */
+        Tracked() {
+            super();
+        }
+
+        /**
+         * {@code BundleListener} method for the {@code BundleTracker}
+         * class. This method must NOT be synchronized to avoid deadlock
+         * potential.
+         *
+         * @param event {@code BundleEvent} object from the framework.
+         */
+        public void bundleChanged(final BundleEvent event) {
+            /*
+             * Check if we had a delayed call (which could happen when we
+             * close).
+             */
+            if (closed) {
+                return;
+            }
+            final Bundle bundle = event.getBundle();
+            final int state = bundle.getState();
+            if (DEBUG) {
+                System.out.println("BundleTracker.Tracked.bundleChanged[" + state + "]: " + bundle); //$NON-NLS-1$ //$NON-NLS-2$
+            }
+
+            if ((state & mask) != 0) {
+                track(bundle, event);
+                /*
+                 * If the customizer throws an unchecked exception, it is safe
+                 * to let it propagate
+                 */
+            } else {
+                untrack(bundle, event);
+                /*
+                 * If the customizer throws an unchecked exception, it is safe
+                 * to let it propagate
+                 */
+            }
+        }
+
+        /**
+         * Call the specific customizer adding method. This method must not be
+         * called while synchronized on this object.
+         *
+         * @param item    Item to be tracked.
+         * @param related Action related object.
+         * @return Customized object for the tracked item or {@code null}
+         *         if the item is not to be tracked.
+         */
+        T customizerAdding(final Bundle item, final BundleEvent related) {
+            return (T) customizer.addingBundle(item, related);
+        }
+
+        /**
+         * Call the specific customizer modified method. This method must not be
+         * called while synchronized on this object.
+         *
+         * @param item    Tracked item.
+         * @param related Action related object.
+         * @param object  Customized object for the tracked item.
+         */
+        void customizerModified(final Bundle item, final BundleEvent related,
+                                final T object) {
+            customizer.modifiedBundle(item, related, object);
+        }
+
+        /**
+         * Call the specific customizer removed method. This method must not be
+         * called while synchronized on this object.
+         *
+         * @param item    Tracked item.
+         * @param related Action related object.
+         * @param object  Customized object for the tracked item.
+         */
+        void customizerRemoved(final Bundle item, final BundleEvent related,
+                               final T object) {
+            customizer.removedBundle(item, related, object);
+        }
+    }
+
+    /**
+     * Abstract class to track items. If a Tracker is reused (closed then reopened),
+     * then a new AbstractTracked object is used. This class acts a map of tracked
+     * item -> customized object. Subclasses of this class will act as the listener
+     * object for the tracker. This class is used to synchronize access to the
+     * tracked items. This is not a public class. It is only for use by the
+     * implementation of the Tracker class.
+     *
+     * @param <S> The tracked item. It is the key.
+     * @param <T> The value mapped to the tracked item.
+     * @param <R> The reason the tracked item is being tracked or untracked.
+     * @version $Id: 79452e6c28683021f2bcf11d3689ec75c6b5642f $
+     * @ThreadSafe
+     * @since 1.4
+     */
+    private static abstract class AbstractTracked<S, T, R> {
+        /* set this to true to compile in debug messages */
+        static final boolean DEBUG = false;
+
+        /**
+         * Map of tracked items to customized objects.
+         *
+         * @GuardedBy this
+         */
+        private final Map<S, T> tracked;
+
+        /**
+         * Modification count. This field is initialized to zero and incremented by
+         * modified.
+         *
+         * @GuardedBy this
+         */
+        private int trackingCount;
+
+        /**
+         * List of items in the process of being added. This is used to deal with
+         * nesting of events. Since events may be synchronously delivered, events
+         * can be nested. For example, when processing the adding of a service and
+         * the customizer causes the service to be unregistered, notification to the
+         * nested call to untrack that the service was unregistered can be made to
+         * the track method.
+         * <p/>
+         * Since the ArrayList implementation is not synchronized, all access to
+         * this list must be protected by the same synchronized object for
+         * thread-safety.
+         *
+         * @GuardedBy this
+         */
+        private final List<S> adding;
+
+        /**
+         * true if the tracked object is closed.
+         * <p/>
+         * This field is volatile because it is set by one thread and read by
+         * another.
+         */
+        volatile boolean closed;
+
+        /**
+         * Initial list of items for the tracker. This is used to correctly process
+         * the initial items which could be modified before they are tracked. This
+         * is necessary since the initial set of tracked items are not "announced"
+         * by events and therefore the event which makes the item untracked could be
+         * delivered before we track the item.
+         * <p/>
+         * An item must not be in both the initial and adding lists at the same
+         * time. An item must be moved from the initial list to the adding list
+         * "atomically" before we begin tracking it.
+         * <p/>
+         * Since the LinkedList implementation is not synchronized, all access to
+         * this list must be protected by the same synchronized object for
+         * thread-safety.
+         *
+         * @GuardedBy this
+         */
+        private final LinkedList<S> initial;
+
+        /**
+         * AbstractTracked constructor.
+         */
+        AbstractTracked() {
+            tracked = new HashMap<S, T>();
+            trackingCount = 0;
+            adding = new ArrayList<S>(6);
+            initial = new LinkedList<S>();
+            closed = false;
+        }
+
+        /**
+         * Set initial list of items into tracker before events begin to be
+         * received.
+         * <p/>
+         * This method must be called from Tracker's open method while synchronized
+         * on this object in the same synchronized block as the add listener call.
+         *
+         * @param list The initial list of items to be tracked. {@code null}
+         *             entries in the list are ignored.
+         * @GuardedBy this
+         */
+        void setInitial(S[] list) {
+            if (list == null) {
+                return;
+            }
+            for (S item : list) {
+                if (item == null) {
+                    continue;
+                }
+                if (DEBUG) {
+                    System.out.println("AbstractTracked.setInitial: " + item); //$NON-NLS-1$
+                }
+                initial.add(item);
+            }
+        }
+
+        /**
+         * Track the initial list of items. This is called after events can begin to
+         * be received.
+         * <p/>
+         * This method must be called from Tracker's open method while not
+         * synchronized on this object after the add listener call.
+         */
+        void trackInitial() {
+            while (true) {
+                S item;
+                synchronized (this) {
+                    if (closed || (initial.size() == 0)) {
+                        /*
+                         * if there are no more initial items
+                         */
+                        return; /* we are done */
+                    }
+                    /*
+                    * move the first item from the initial list to the adding list
+                    * within this synchronized block.
+                    */
+                    item = initial.removeFirst();
+                    if (tracked.get(item) != null) {
+                        /* if we are already tracking this item */
+                        if (DEBUG) {
+                            System.out.println("AbstractTracked.trackInitial[already tracked]: " + item); //$NON-NLS-1$
+                        }
+                        continue; /* skip this item */
+                    }
+                    if (adding.contains(item)) {
+                        /*
+                         * if this item is already in the process of being added.
+                         */
+                        if (DEBUG) {
+                            System.out.println("AbstractTracked.trackInitial[already adding]: " + item); //$NON-NLS-1$
+                        }
+                        continue; /* skip this item */
+                    }
+                    adding.add(item);
+                }
+                if (DEBUG) {
+                    System.out.println("AbstractTracked.trackInitial: " + item); //$NON-NLS-1$
+                }
+                trackAdding(item, null); /*
+                                          * Begin tracking it. We call trackAdding
+									      * since we have already put the item in the
+									      * adding list.
+									      */
+            }
+        }
+
+        /**
+         * Called by the owning Tracker object when it is closed.
+         */
+        void close() {
+            closed = true;
+        }
+
+        /**
+         * Begin to track an item.
+         *
+         * @param item    Item to be tracked.
+         * @param related Action related object.
+         */
+        void track(final S item, final R related) {
+            final T object;
+            synchronized (this) {
+                if (closed) {
+                    return;
+                }
+                object = tracked.get(item);
+                if (object == null) { /* we are not tracking the item */
+                    if (adding.contains(item)) {
+                        /* if this item is already in the process of being added. */
+                        if (DEBUG) {
+                            System.out
+                                    .println("AbstractTracked.track[already adding]: " + item); //$NON-NLS-1$
+                        }
+                        return;
+                    }
+                    adding.add(item); /* mark this item is being added */
+                } else { /* we are currently tracking this item */
+                    if (DEBUG) {
+                        System.out
+                                .println("AbstractTracked.track[modified]: " + item); //$NON-NLS-1$
+                    }
+                    modified(); /* increment modification count */
+                }
+            }
+
+            if (object == null) { /* we are not tracking the item */
+                trackAdding(item, related);
+            } else {
+                /* Call customizer outside of synchronized region */
+                customizerModified(item, related, object);
+                /*
+                 * If the customizer throws an unchecked exception, it is safe to
+                 * let it propagate
+                 */
+            }
+        }
+
+        /**
+         * Common logic to add an item to the tracker used by track and
+         * trackInitial. The specified item must have been placed in the adding list
+         * before calling this method.
+         *
+         * @param item    Item to be tracked.
+         * @param related Action related object.
+         */
+        private void trackAdding(final S item, final R related) {
+            if (DEBUG) {
+                System.out.println("AbstractTracked.trackAdding: " + item); //$NON-NLS-1$
+            }
+            T object = null;
+            boolean becameUntracked = false;
+            /* Call customizer outside of synchronized region */
+            try {
+                object = customizerAdding(item, related);
+                /*
+                 * If the customizer throws an unchecked exception, it will
+                 * propagate after the finally
+                 */
+            } finally {
+                synchronized (this) {
+                    if (adding.remove(item) && !closed) {
+                        /*
+                         * if the item was not untracked during the customizer
+                         * callback
+                         */
+                        if (object != null) {
+                            tracked.put(item, object);
+                            modified(); /* increment modification count */
+                            notifyAll(); /* notify any waiters */
+                        }
+                    } else {
+                        becameUntracked = true;
+                    }
+                }
+            }
+            /*
+             * The item became untracked during the customizer callback.
+             */
+            if (becameUntracked && (object != null)) {
+                if (DEBUG) {
+                    System.out.println("AbstractTracked.trackAdding[removed]: " + item); //$NON-NLS-1$
+                }
+                /* Call customizer outside of synchronized region */
+                customizerRemoved(item, related, object);
+                /*
+                 * If the customizer throws an unchecked exception, it is safe to
+                 * let it propagate
+                 */
+            }
+        }
+
+        /**
+         * Discontinue tracking the item.
+         *
+         * @param item    Item to be untracked.
+         * @param related Action related object.
+         */
+        void untrack(final S item, final R related) {
+            final T object;
+            synchronized (this) {
+                if (initial.remove(item)) { /*
+										     * if this item is already in the list
+										     * of initial references to process
+										     */
+                    if (DEBUG) {
+                        System.out.println("AbstractTracked.untrack[removed from initial]: " + item); //$NON-NLS-1$
+                    }
+                    return; /*
+						     * we have removed it from the list and it will not be
+						     * processed
+						     */
+                }
+
+                if (adding.remove(item)) { /*
+										    * if the item is in the process of
+										    * being added
+										    */
+                    if (DEBUG) {
+                        System.out.println("AbstractTracked.untrack[being added]: " + item); //$NON-NLS-1$
+                    }
+                    return; /*
+						     * in case the item is untracked while in the process of
+						     * adding
+						     */
+                }
+                object = tracked.remove(item); /*
+											    * must remove from tracker before
+											    * calling customizer callback
+											    */
+                if (object == null) { /* are we actually tracking the item */
+                    return;
+                }
+                modified(); /* increment modification count */
+            }
+            if (DEBUG) {
+                System.out.println("AbstractTracked.untrack[removed]: " + item); //$NON-NLS-1$
+            }
+            /* Call customizer outside of synchronized region */
+            customizerRemoved(item, related, object);
+            /*
+             * If the customizer throws an unchecked exception, it is safe to let it
+             * propagate
+             */
+        }
+
+        /**
+         * Returns the number of tracked items.
+         *
+         * @return The number of tracked items.
+         * @GuardedBy this
+         */
+        int size() {
+            return tracked.size();
+        }
+
+        /**
+         * Returns if the tracker is empty.
+         *
+         * @return Whether the tracker is empty.
+         * @GuardedBy this
+         * @since 1.5
+         */
+        boolean isEmpty() {
+            return tracked.isEmpty();
+        }
+
+        /**
+         * Return the customized object for the specified item
+         *
+         * @param item The item to lookup in the map
+         * @return The customized object for the specified item.
+         * @GuardedBy this
+         */
+        T getCustomizedObject(final S item) {
+            return tracked.get(item);
+        }
+
+        /**
+         * Copy the tracked items into an array.
+         *
+         * @param list An array to contain the tracked items.
+         * @return The specified list if it is large enough to hold the tracked
+         *         items or a new array large enough to hold the tracked items.
+         * @GuardedBy this
+         */
+        S[] copyKeys(final S[] list) {
+            return tracked.keySet().toArray(list);
+        }
+
+        /**
+         * Increment the modification count. If this method is overridden, the
+         * overriding method MUST call this method to increment the tracking count.
+         *
+         * @GuardedBy this
+         */
+        void modified() {
+            trackingCount++;
+        }
+
+        /**
+         * Returns the tracking count for this {@code ServiceTracker} object.
+         * <p/>
+         * The tracking count is initialized to 0 when this object is opened. Every
+         * time an item is added, modified or removed from this object the tracking
+         * count is incremented.
+         *
+         * @return The tracking count for this object.
+         * @GuardedBy this
+         */
+        int getTrackingCount() {
+            return trackingCount;
+        }
+
+        /**
+         * Copy the tracked items and associated values into the specified map.
+         *
+         * @param <M> Type of {@code Map} to hold the tracked items and
+         *            associated values.
+         * @param map The map into which to copy the tracked items and associated
+         *            values. This map must not be a user provided map so that user code
+         *            is not executed while synchronized on this.
+         * @return The specified map.
+         * @GuardedBy this
+         * @since 1.5
+         */
+        <M extends Map<? super S, ? super T>> M copyEntries(final M map) {
+            map.putAll(tracked);
+            return map;
+        }
+
+        /**
+         * Call the specific customizer adding method. This method must not be
+         * called while synchronized on this object.
+         *
+         * @param item    Item to be tracked.
+         * @param related Action related object.
+         * @return Customized object for the tracked item or {@code null} if
+         *         the item is not to be tracked.
+         */
+        abstract T customizerAdding(final S item, final R related);
+
+        /**
+         * Call the specific customizer modified method. This method must not be
+         * called while synchronized on this object.
+         *
+         * @param item    Tracked item.
+         * @param related Action related object.
+         * @param object  Customized object for the tracked item.
+         */
+        abstract void customizerModified(final S item, final R related,
+                                         final T object);
+
+        /**
+         * Call the specific customizer removed method. This method must not be
+         * called while synchronized on this object.
+         *
+         * @param item    Tracked item.
+         * @param related Action related object.
+         * @param object  Customized object for the tracked item.
+         */
+        abstract void customizerRemoved(final S item, final R related,
+                                        final T object);
+    }
 
 }



Mime
View raw message