You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@aries.apache.org by dj...@apache.org on 2011/11/10 02:58:27 UTC
svn commit: r1200094 - in
/aries/trunk/util/src/main/java/org/apache/aries/util/tracker:
RecursiveBundleTracker.java hook/ hook/BundleHookBundleTracker.java
Author: djencks
Date: Thu Nov 10 01:58:27 2011
New Revision: 1200094
URL: http://svn.apache.org/viewvc?rev=1200094&view=rev
Log:
ARIES-778 Make RecursiveBundleTracker work with (at least) Region based isolation
Added:
aries/trunk/util/src/main/java/org/apache/aries/util/tracker/hook/
aries/trunk/util/src/main/java/org/apache/aries/util/tracker/hook/BundleHookBundleTracker.java (with props)
Modified:
aries/trunk/util/src/main/java/org/apache/aries/util/tracker/RecursiveBundleTracker.java
Modified: aries/trunk/util/src/main/java/org/apache/aries/util/tracker/RecursiveBundleTracker.java
URL: http://svn.apache.org/viewvc/aries/trunk/util/src/main/java/org/apache/aries/util/tracker/RecursiveBundleTracker.java?rev=1200094&r1=1200093&r2=1200094&view=diff
==============================================================================
--- aries/trunk/util/src/main/java/org/apache/aries/util/tracker/RecursiveBundleTracker.java (original)
+++ aries/trunk/util/src/main/java/org/apache/aries/util/tracker/RecursiveBundleTracker.java Thu Nov 10 01:58:27 2011
@@ -18,6 +18,7 @@
*/
package org.apache.aries.util.tracker;
+import org.apache.aries.util.tracker.hook.BundleHookBundleTracker;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
@@ -29,61 +30,68 @@ import org.osgi.util.tracker.BundleTrack
* events related to framework bundles, as it will automatically handle these events. In
* order to use this class clients must create a subclass and implement the methods of the
* <code>BundleTrackerCustomizer</code> interface. In spite of this, instances of this class
- * MUST NOT be passed as a parameter to any <code>BundleTracker</code>.</p>
- *
+ * MUST NOT be passed as a parameter to any <code>BundleTracker</code>.</p>
+ * <p/>
* The model for using this is that classes should instantiate it
* and pass it a 'vanilla' bundle tracker.
- * @author pradine
*
+ * @author pradine
*/
-public final class RecursiveBundleTracker {
- private static final int COMPOSITE_BUNDLE_MASK =
+public final class RecursiveBundleTracker {
+ private static final int COMPOSITE_BUNDLE_MASK =
Bundle.INSTALLED | Bundle.RESOLVED | Bundle.STARTING | Bundle.ACTIVE | Bundle.STOPPING;
-
- private final BundleTracker tracker;
-
- /**
- * Constructor
- *
- * @param context - The <code>BundleContext</code> against which the tracking is done.
- * @param stateMask - The bit mask of the ORing of the bundle states to be tracked. The
- * mask must contain the flags <code>Bundle.INSTALLED | Bundle.STARTING | Bundle.ACTIVE | Bundle.STOPPING</code>
- * as a minimum.
- * @throws IllegalArgumentException - If the provided bit mask does not contain required
- * flags
- */
- public RecursiveBundleTracker(BundleContext context, int stateMask, BundleTrackerCustomizer customizer) {
- // We always need INSTALLED events so we can recursively listen to the frameworks
- if ((stateMask & COMPOSITE_BUNDLE_MASK) != COMPOSITE_BUNDLE_MASK)
- throw new IllegalArgumentException();
- if (areMultipleFrameworksAvailable(context)) {
- tracker = new InternalRecursiveBundleTracker(context, stateMask, customizer);
- } else {
- tracker = new BundleTracker(context, stateMask, customizer);
- }
- }
-
- private static boolean areMultipleFrameworksAvailable(BundleContext context) {
- ServiceReference sr = context.getServiceReference("org.osgi.service.framework.CompositeBundleFactory");
- return sr != null;
- }
-
- /**
- * Start tracking bundles that match the bit mask provided at creation time.
- *
- * @see BundleTracker#open()
- */
- public void open() {
- tracker.open();
+
+ private final BundleTracker tracker;
+
+ /**
+ * Constructor
+ *
+ * @param context - The <code>BundleContext</code> against which the tracking is done.
+ * @param stateMask - The bit mask of the ORing of the bundle states to be tracked. The
+ * mask must contain the flags <code>Bundle.INSTALLED | Bundle.STARTING | Bundle.ACTIVE | Bundle.STOPPING</code>
+ * as a minimum.
+ * @throws IllegalArgumentException - If the provided bit mask does not contain required
+ * flags
+ */
+ public RecursiveBundleTracker(BundleContext context, int stateMask, BundleTrackerCustomizer customizer) {
+ // We always need INSTALLED events so we can recursively listen to the frameworks
+ if ((stateMask & COMPOSITE_BUNDLE_MASK) != COMPOSITE_BUNDLE_MASK)
+ throw new IllegalArgumentException();
+
+ BundleTracker tracker = null;
+ try {
+ tracker = new BundleHookBundleTracker(context, stateMask, customizer);
+ } catch (Throwable e) {
}
-
- /**
- * Stop the tracking of bundles
- *
- * @see BundleTracker#close()
- */
- public void close() {
- tracker.close();
+ if (tracker == null && areMultipleFrameworksAvailable(context)) {
+ tracker = new InternalRecursiveBundleTracker(context, stateMask, customizer);
+ } else {
+ tracker = new BundleTracker(context, stateMask, customizer);
}
+ this.tracker = tracker;
+ }
+
+ private static boolean areMultipleFrameworksAvailable(BundleContext context) {
+ ServiceReference sr = context.getServiceReference("org.osgi.service.framework.CompositeBundleFactory");
+ return sr != null;
+ }
+
+ /**
+ * Start tracking bundles that match the bit mask provided at creation time.
+ *
+ * @see BundleTracker#open()
+ */
+ public void open() {
+ tracker.open();
+ }
+
+ /**
+ * Stop the tracking of bundles
+ *
+ * @see BundleTracker#close()
+ */
+ public void close() {
+ tracker.close();
+ }
}
Added: aries/trunk/util/src/main/java/org/apache/aries/util/tracker/hook/BundleHookBundleTracker.java
URL: http://svn.apache.org/viewvc/aries/trunk/util/src/main/java/org/apache/aries/util/tracker/hook/BundleHookBundleTracker.java?rev=1200094&view=auto
==============================================================================
--- aries/trunk/util/src/main/java/org/apache/aries/util/tracker/hook/BundleHookBundleTracker.java (added)
+++ aries/trunk/util/src/main/java/org/apache/aries/util/tracker/hook/BundleHookBundleTracker.java Thu Nov 10 01:58:27 2011
@@ -0,0 +1,625 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+
+package org.apache.aries.util.tracker.hook;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.framework.SynchronousBundleListener;
+import org.osgi.framework.hooks.bundle.EventHook;
+import org.osgi.util.tracker.BundleTracker;
+import org.osgi.util.tracker.BundleTrackerCustomizer;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class BundleHookBundleTracker<T> extends BundleTracker {
+
+ 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() {
+ tracked = new Tracked();
+ EventHook hook = new BundleEventHook(tracked);
+ sr = context.registerService(EventHook.class.getName(), hook, null);
+ }
+
+ @Override
+ public void close() {
+ sr.unregister();
+ tracked.close();
+ tracked = null;
+ }
+
+ 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;
+
+ /**
+ * 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);
+ }
+
+}
Propchange: aries/trunk/util/src/main/java/org/apache/aries/util/tracker/hook/BundleHookBundleTracker.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: aries/trunk/util/src/main/java/org/apache/aries/util/tracker/hook/BundleHookBundleTracker.java
------------------------------------------------------------------------------
svn:keywords = Date Revision
Propchange: aries/trunk/util/src/main/java/org/apache/aries/util/tracker/hook/BundleHookBundleTracker.java
------------------------------------------------------------------------------
svn:mime-type = text/plain