Applied patch (FELIX-132) to have SCR listen for bundle events for
creating/disposing of components as well as to use reflection to get
the bundle context.
git-svn-id: https://svn.apache.org/repos/asf/incubator/felix/trunk@440833 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/scr/src/main/java/org/apache/felix/scr/Activator.java b/scr/src/main/java/org/apache/felix/scr/Activator.java
index 112f376..71b6181 100644
--- a/scr/src/main/java/org/apache/felix/scr/Activator.java
+++ b/scr/src/main/java/org/apache/felix/scr/Activator.java
@@ -16,25 +16,216 @@
*/
package org.apache.felix.scr;
-import org.osgi.framework.BundleActivator;
-import org.osgi.framework.BundleContext;
+import java.lang.reflect.Method;
+import java.util.*;
+
+import org.osgi.framework.*;
/**
- * This activator is used to cover requirement described in section 112.8.1
- * When SCR is implemented as a bundle, any component configurations
- * activated by SCR must be deactivated when the SCR bundle is stopped. When
- * the SCR bundle is started, it must process any components that are declared
- * in active bundles.
- *
+ * This activator is used to cover requirement described in section 112.8.1 @@ -27,14
+ * 37,202 @@ in active bundles.
+ *
*/
-public class Activator implements BundleActivator {
+public class Activator implements BundleActivator, SynchronousBundleListener
+{
- public void start(BundleContext arg0) throws Exception {
- //TODO: pending
- }
+ // map of GenericActivator instances per Bundle indexed by Bundle symbolic
+ // name
+ private Map m_componentBundles;
- public void stop(BundleContext arg0) throws Exception {
- // TODO: pending
- }
+ /**
+ * Registers this instance as a (synchronous) bundle listener and loads the
+ * components of already registered bundles.
+ *
+ * @param context The <code>BundleContext</code> of the SCR implementation
+ * bundle.
+ */
+ public void start(BundleContext context) throws Exception
+ {
+ m_componentBundles = new HashMap();
-}
+ // register for bundle updates
+ context.addBundleListener(this);
+
+ // 112.8.2 load all components of active bundles
+ loadAllComponents(context);
+ }
+
+ /**
+ * Unregisters this instance as a bundle listener and unloads all components
+ * which have been registered during the active life time of the SCR
+ * implementation bundle.
+ *
+ * @param context The <code>BundleContext</code> of the SCR implementation
+ * bundle.
+ */
+ public void stop(BundleContext context) throws Exception
+ {
+ // unregister as bundle listener
+ context.removeBundleListener(this);
+
+ // 112.8.2 dispose off all active components
+ disposeAllComponents();
+ }
+
+ // ---------- BundleListener Interface -------------------------------------
+
+ /**
+ * Loads and unloads any components provided by the bundle whose state
+ * changed. If the bundle has been started, the components are loaded. If
+ * the bundle is about to stop, the components are unloaded.
+ *
+ * @param event The <code>BundleEvent</code> representing the bundle state
+ * change.
+ */
+ public void bundleChanged(BundleEvent event)
+ {
+ if (event.getType() == BundleEvent.STARTED)
+ {
+ loadComponents(event.getBundle());
+ }
+ else if (event.getType() == BundleEvent.STOPPING)
+ {
+ disposeComponents(event.getBundle());
+ }
+ }
+
+ // ---------- Component Management -----------------------------------------
+
+ // Loads the components of all bundles currently active.
+ private void loadAllComponents(BundleContext context)
+ {
+ Bundle[] bundles = context.getBundles();
+ for (int i = 0; i < bundles.length; i++)
+ {
+ Bundle bundle = bundles[i];
+ if (bundle.getState() == Bundle.ACTIVE)
+ {
+ loadComponents(bundle);
+ }
+ }
+ }
+
+ /**
+ * Loads the components of the given bundle. If the bundle has no
+ * <i>Service-Component</i> header, this method has no effect. The
+ * fragments of a bundle are not checked for the header (112.4.1).
+ * <p>
+ * This method calls the {@link #getBundleContext(Bundle)} method to find
+ * the <code>BundleContext</code> of the bundle. If the context cannot be
+ * found, this method does not load components for the bundle.
+ */
+ private void loadComponents(Bundle bundle)
+ {
+ if (bundle.getHeaders().get("Service-Component") == null)
+ {
+ // no components in the bundle, abandon
+ return;
+ }
+
+ // there should be components, load them with a bundle context
+ BundleContext context = getBundleContext(bundle);
+ if (context == null)
+ {
+ GenericActivator.error("Cannot get BundleContext of bundle "
+ + bundle.getSymbolicName());
+ return;
+ }
+
+ GenericActivator ga = new GenericActivator();
+ try
+ {
+ ga.start(context);
+ m_componentBundles.put(bundle.getSymbolicName(), ga);
+ }
+ catch (Exception e)
+ {
+ GenericActivator.exception("Error while loading components "
+ + "of bundle " + bundle.getSymbolicName(), null, e);
+ }
+ }
+
+ /**
+ * Unloads components of the given bundle. If no components have been loaded
+ * for the bundle, this method has no effect.
+ */
+ private void disposeComponents(Bundle bundle)
+ {
+ String name = bundle.getSymbolicName();
+ GenericActivator ga = (GenericActivator) m_componentBundles.remove(name);
+ if (ga != null)
+ {
+ try
+ {
+ ga.dispose();
+ }
+ catch (Exception e)
+ {
+ GenericActivator.exception("Error while disposing components "
+ + "of bundle " + name, null, e);
+ }
+ }
+ }
+
+ // Unloads all components registered with the SCR
+ private void disposeAllComponents()
+ {
+ for (Iterator it = m_componentBundles.values().iterator(); it.hasNext();)
+ {
+ GenericActivator ga = (GenericActivator) it.next();
+ try
+ {
+ ga.dispose();
+ }
+ catch (Exception e)
+ {
+ GenericActivator.exception(
+ "Error while disposing components of bundle "
+ + ga.getBundleContext().getBundle().getSymbolicName(),
+ null, e);
+ }
+ it.remove();
+ }
+ }
+
+ /**
+ * Returns the <code>BundleContext</code> of the bundle.
+ * <p>
+ * This method assumes a <code>getContext</code> method returning a
+ * <code>BundleContext</code> instance to be present in the class of the
+ * bundle or any of its parent classes.
+ *
+ * @param bundle The <code>Bundle</code> whose context is to be returned.
+ *
+ * @return The <code>BundleContext</code> of the bundle or
+ * <code>null</code> if no <code>getContext</code> method
+ * returning a <code>BundleContext</code> can be found.
+ */
+ private BundleContext getBundleContext(Bundle bundle)
+ {
+ for (Class clazz = bundle.getClass(); clazz != null; clazz = clazz.getSuperclass())
+ {
+ try
+ {
+ Method m = clazz.getDeclaredMethod("getContext", null);
+ if (m.getReturnType().equals(BundleContext.class))
+ {
+ m.setAccessible(true);
+ return (BundleContext) m.invoke(bundle, null);
+ }
+ }
+ catch (NoSuchMethodException nsme)
+ {
+ // don't actually care, just try super class
+ }
+ catch (Throwable t)
+ {
+ GenericActivator.exception("Cannot get BundleContext for "
+ + bundle.getSymbolicName(), null, t);
+ }
+ }
+
+ // fall back to nothing
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/scr/src/main/java/org/apache/felix/scr/GenericActivator.java b/scr/src/main/java/org/apache/felix/scr/GenericActivator.java
index 165c4b7..c1f7e3b 100644
--- a/scr/src/main/java/org/apache/felix/scr/GenericActivator.java
+++ b/scr/src/main/java/org/apache/felix/scr/GenericActivator.java
@@ -217,7 +217,13 @@
//GenericActivator.trace("GenericActivator : Bundle ["+context.getBundle().getBundleId()+"] STOPPED");
}
- /**
+ void dispose() throws Exception {
+ if (m_context != null) {
+ stop(m_context);
+ }
+ }
+
+ /**
* Returns the list of instance references currently associated to this activator
*
* @return the list of instance references