Add a bundle tracker to externalize the tracking code from the extension manager.

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@654647 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/ipojo/extender.pattern.handler/src/main/java/org/apache/felix/ipojo/handler/extender/BundleTracker.java b/ipojo/extender.pattern.handler/src/main/java/org/apache/felix/ipojo/handler/extender/BundleTracker.java
new file mode 100644
index 0000000..a693023
--- /dev/null
+++ b/ipojo/extender.pattern.handler/src/main/java/org/apache/felix/ipojo/handler/extender/BundleTracker.java
@@ -0,0 +1,154 @@
+/* 

+ * 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.felix.ipojo.handler.extender;

+

+import java.util.HashSet;

+import java.util.Set;

+import org.osgi.framework.Bundle;

+import org.osgi.framework.BundleContext;

+import org.osgi.framework.BundleEvent;

+import org.osgi.framework.SynchronousBundleListener;

+

+/**

+ * This is a very simple bundle tracker utility class that tracks active

+ * bundles. The tracker must be given a bundle context upon creation,

+ * which it uses to listen for bundle events. The bundle tracker must be

+ * opened to track objects and closed when it is no longer needed. This

+ * class is abstract, which means in order to use it you must create a

+ * subclass of it. Subclasses must implement the <tt>addedBundle()</tt>

+ * and <tt>removedBundle()</tt> methods, which can be used to perform some

+ * custom action upon the activation or deactivation of bundles. Since this

+ * tracker is quite simple, its concurrency control approach is also

+ * simplistic. This means that subclasses should take great care to ensure

+ * that their <tt>addedBundle()</tt> and <tt>removedBundle()</tt> methods

+ * are very simple and do not do anything to change the state of any bundles.

+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>

+ **/

+public abstract class BundleTracker {

+    /**

+     * Set of tracked bundles.

+     */

+    final Set m_bundleSet = new HashSet();

+

+    /**

+     * Bundle context.

+     */

+    final BundleContext m_context;

+

+    /**

+     * Synchronous bundle listener.

+     */

+    final SynchronousBundleListener m_listener;

+

+    /**

+     * Flag indicating if the tracking is open.

+     */

+    boolean m_open;

+

+    /**

+     * Constructs a bundle tracker object that will use the specified

+     * bundle context.

+     * @param context The bundle context to use to track bundles.

+     **/

+    public BundleTracker(BundleContext context) {

+        m_context = context;

+        m_listener = new SynchronousBundleListener() {

+            public void bundleChanged(BundleEvent evt) {

+                synchronized (BundleTracker.this) {

+                    if (!m_open) { return; }

+

+                    if (evt.getType() == BundleEvent.STARTED) {

+                        if (!m_bundleSet.contains(evt.getBundle())) {

+                            m_bundleSet.add(evt.getBundle());

+                            addedBundle(evt.getBundle());

+                        }

+                    } else if (evt.getType() == BundleEvent.STOPPED) {

+                        if (m_bundleSet.contains(evt.getBundle())) {

+                            m_bundleSet.remove(evt.getBundle());

+                            removedBundle(evt.getBundle());

+                        }

+                    }

+                }

+            }

+        };

+    }

+

+    /**

+     * Returns the current set of active bundles.

+     * @return The current set of active bundles.

+     **/

+    public synchronized Bundle[] getBundles() {

+        return (Bundle[]) m_bundleSet.toArray(new Bundle[m_bundleSet.size()]);

+    }

+

+    /**

+     * Call this method to start the tracking of active bundles.

+     **/

+    public synchronized void open() {

+        if (!m_open) {

+            m_open = true;

+

+            m_context.addBundleListener(m_listener);

+

+            Bundle[] bundles = m_context.getBundles();

+            for (int i = 0; i < bundles.length; i++) {

+                if (bundles[i].getState() == Bundle.ACTIVE) {

+                    m_bundleSet.add(bundles[i]);

+                    addedBundle(bundles[i]);

+                }

+            }

+        }

+    }

+

+    /**

+     * Call this method to stop the tracking of active bundles.

+     **/

+    public synchronized void close() {

+        if (m_open) {

+            m_open = false;

+

+            m_context.removeBundleListener(m_listener);

+

+            Bundle[] bundles = (Bundle[]) m_bundleSet.toArray(new Bundle[m_bundleSet.size()]);

+            for (int i = 0; i < bundles.length; i++) {

+                if (m_bundleSet.remove(bundles[i])) {

+                    removedBundle(bundles[i]);

+                }

+            }

+        }

+    }

+

+    /**

+     * Subclasses must implement this method; it can be used to perform

+     * actions upon the activation of a bundle. Subclasses should keep

+     * this method implementation as simple as possible and should not

+     * cause the change in any bundle state to avoid concurrency issues.

+     * @param bundle The bundle being added to the active set.

+     **/

+    protected abstract void addedBundle(Bundle bundle);

+

+    /**

+     * Subclasses must implement this method; it can be used to perform

+     * actions upon the deactivation of a bundle. Subclasses should keep

+     * this method implementation as simple as possible and should not

+     * cause the change in any bundle state to avoid concurrency issues.

+     * @param bundle The bundle being removed from the active set.

+     **/

+    protected abstract void removedBundle(Bundle bundle);

+}

diff --git a/ipojo/extender.pattern.handler/src/main/java/org/apache/felix/ipojo/handler/extender/ExtenderManager.java b/ipojo/extender.pattern.handler/src/main/java/org/apache/felix/ipojo/handler/extender/ExtenderManager.java
index aa8341a..69fdea3 100644
--- a/ipojo/extender.pattern.handler/src/main/java/org/apache/felix/ipojo/handler/extender/ExtenderManager.java
+++ b/ipojo/extender.pattern.handler/src/main/java/org/apache/felix/ipojo/handler/extender/ExtenderManager.java
@@ -19,22 +19,19 @@
 package org.apache.felix.ipojo.handler.extender;

 

 import java.lang.reflect.InvocationTargetException;

-import java.util.ArrayList;

 import java.util.Dictionary;

-import java.util.List;

+import java.util.HashSet;

+import java.util.Set;

 

 import org.apache.felix.ipojo.PrimitiveHandler;

 import org.apache.felix.ipojo.util.Callback;

 import org.osgi.framework.Bundle;

-import org.osgi.framework.BundleContext;

-import org.osgi.framework.BundleEvent;

-import org.osgi.framework.SynchronousBundleListener;

 

 /**

  * Track and manage extensions.

  * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>

  */

-public class ExtenderManager implements SynchronousBundleListener {

+public class ExtenderManager extends BundleTracker {

     

     /**

      * Looked extension.

@@ -57,14 +54,9 @@
     private PrimitiveHandler m_handler;

     

     /**

-     * Bundle context. 

+     * Set of managed bundles.

      */

-    private BundleContext m_context;

-    

-    /**

-     * List of managed bundles. 

-     */

-    private List m_bundles = new ArrayList();

+    private Set m_bundles = new HashSet();

     

     /**

      * Constructor.

@@ -74,40 +66,27 @@
      * @param unbind : onDeparture method.

      */

     public ExtenderManager(ExtenderModelHandler handler, String extension, String bind, String unbind) {

+        super(handler.getInstanceManager().getContext());

         m_handler = handler;

         m_onArrival = new Callback(bind, new Class[] {Bundle.class, String.class}, false, m_handler.getInstanceManager());

         m_onDeparture = new Callback(unbind, new Class[] {Bundle.class}, false, m_handler.getInstanceManager());

         m_extension = extension;

-        m_context = handler.getInstanceManager().getContext();

     }

-    

+

+

     /**

-     * Start method.

-     * Look for already presents bundle and register a (synchronous) bundle listener.

+     * A bundle arrives.

+     * Checks if the bundle match with the looked extension, if so call the arrival callback.

+     * @param bundle : arriving bundle.

+     * @see org.apache.felix.ipojo.handler.extender.BundleTracker#addedBundle(org.osgi.framework.Bundle)

      */

-    public void start() {

-        synchronized (this) {

-            // listen to any changes in bundles.

-            m_context.addBundleListener(this);

-            // compute already started bundles.

-            for (int i = 0; i < m_context.getBundles().length; i++) {

-                if (m_context.getBundles()[i].getState() == Bundle.ACTIVE) {

-                    onArrival(m_context.getBundles()[i]);

-                }

-            }

-        }

-    }

-    

-    /**

-     * Manage a bundle arrival:

-     * Check the extension and manage it if present.

-     * @param bundle : bundle.

-     */

-    private void onArrival(Bundle bundle) {

+    protected void addedBundle(Bundle bundle) {

         Dictionary headers = bundle.getHeaders();

         String header = (String) headers.get(m_extension);

         if (header != null) {

-            m_bundles.add(bundle);

+            synchronized (this) {

+                m_bundleSet.add(bundle);

+            }

             try {

                 m_onArrival.call(new Object[] {bundle, header});

             } catch (NoSuchMethodException e) {

@@ -124,40 +103,18 @@
     }

 

     /**

-     * Stop method.

-     * Remove the bundle listener. 

+     * A bundle is stopping.

+     * Check if the bundle was managed, if so call the remove departure callback.

+     * @param bundle : leaving bundle.

+     * @see org.apache.felix.ipojo.handler.extender.BundleTracker#removedBundle(org.osgi.framework.Bundle)

      */

-    public void stop() {

-        m_context.removeBundleListener(this);

-        m_bundles.clear();

-    }

-

-    /**

-     * Bundle listener.

-     * @param event : event.

-     * @see org.osgi.framework.BundleListener#bundleChanged(org.osgi.framework.BundleEvent)

-     */

-    public void bundleChanged(BundleEvent event) {

-        switch (event.getType()) {

-            case BundleEvent.STARTED:

-                onArrival(event.getBundle());

-                break;

-            case BundleEvent.STOPPING:

-                onDeparture(event.getBundle());

-                break;

-            default: 

-                break;

+    protected void removedBundle(Bundle bundle) {

+        boolean contained;

+        synchronized (this) {

+            contained = m_bundles.remove(bundle); // Stack confinement

         }

         

-    }

-

-    /**

-     * Manage a bundle departure.

-     * If the bundle was managed, invoke the OnDeparture callback, and remove the bundle from the list.

-     * @param bundle : bundle.

-     */

-    private void onDeparture(Bundle bundle) {

-        if (m_bundles.contains(bundle)) {

+        if (contained) {

             try {

                 m_onDeparture.call(new Object[] {bundle});

             } catch (NoSuchMethodException e) {

diff --git a/ipojo/extender.pattern.handler/src/main/java/org/apache/felix/ipojo/handler/extender/ExtenderModelHandler.java b/ipojo/extender.pattern.handler/src/main/java/org/apache/felix/ipojo/handler/extender/ExtenderModelHandler.java
index 8fc50b9..0f51828 100644
--- a/ipojo/extender.pattern.handler/src/main/java/org/apache/felix/ipojo/handler/extender/ExtenderModelHandler.java
+++ b/ipojo/extender.pattern.handler/src/main/java/org/apache/felix/ipojo/handler/extender/ExtenderModelHandler.java
@@ -41,6 +41,7 @@
     

     /**

      * Extension manager list.

+     * Immutable once set.

      */

     private List m_managers = new ArrayList(1);

 

@@ -77,7 +78,7 @@
      */

     public void start() {

         for (int i = 0; i < m_managers.size(); i++) {

-            ((ExtenderManager) m_managers.get(i)).start();

+            ((ExtenderManager) m_managers.get(i)).open();

         }

     }

 

@@ -87,7 +88,7 @@
      */

     public void stop() {

         for (int i = 0; i < m_managers.size(); i++) {

-            ((ExtenderManager) m_managers.get(i)).stop();

+            ((ExtenderManager) m_managers.get(i)).close();

         } 

     }