FELIX-3869: Fragment support for DependencyManager-Component.


git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1441505 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/dependencymanager/runtime/doc/changelog.txt b/dependencymanager/runtime/doc/changelog.txt
index b4aa109..2bb4add 100644
--- a/dependencymanager/runtime/doc/changelog.txt
+++ b/dependencymanager/runtime/doc/changelog.txt
@@ -1,11 +1,19 @@
-Changes from 3.0.0 to 3.0.1
----------------------------
+Trunk
+------
+
+** Bug
+
+** Improvement
+    * [FELIX-3869] - Fragment support for DependencyManager-Component
+
+Release 3.1.0
+-------------
 
 ** Bug
 
 ** Improvement
     * Removed root changelog.txt
-    * [FELIX-2954 ] - annotated component factory does not allow to provide a component instance explicitly
+    * [FELIX-2954] - annotated component factory does not allow to provide a component instance explicitly
     * [FELIX-2966] - Annotations should automatically generate Import-Service/Export-Service headers
     * [FELIX-2965] - Annotations should allow to enable or disable auto-configuration mode.
     * [FELIX-3866] - Added support for swap attribute in AspectService and AdpaterService annotations.
diff --git a/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/Activator.java b/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/Activator.java
index b551302..18a3499 100644
--- a/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/Activator.java
+++ b/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/Activator.java
@@ -18,12 +18,17 @@
  */
 package org.apache.felix.dm.runtime;
 
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.felix.dm.Component;
 import org.apache.felix.dm.DependencyActivatorBase;
 import org.apache.felix.dm.DependencyManager;
 import org.apache.felix.dm.ServiceDependency;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
 import org.osgi.service.log.LogService;
+import org.osgi.service.packageadmin.PackageAdmin;
 
 /*
  * This is the Activator for our DependencyManager Component Runtime.
@@ -42,6 +47,12 @@
     final static String CONF_LOG = "dm.runtime.log";
     
     /**
+     * Name of bundle context property telling if PackageAdmin service is required or not.
+     * (default = false)
+     */
+    private final static String CONF_PACKAGE_ADMIN = "dm.runtime.packageAdmin";
+    
+    /**
      * Name of bundle context property telling if Components must be auto configured
      * with BundleContext/ServiceRegistration etc .. (default = false) 
      */
@@ -52,32 +63,42 @@
      * 
      * We depend on the OSGi LogService, and we track all started bundles which do have a 
      * "DependencyManager-Component" Manifest header.
-     * If the "dm.runtime.log=true" parameter is configured in the Felix config.properties
-     * then we'll use a required/temporal service dependency over the log service.
-     * This temporal dependency allows to not being restarted if the log service is temporarily 
-     * unavailable (that is: when the log service is updated).
-     * if the "dm.runtime.log" is not configured or it it is set to false, then we'll use 
-     * an optional dependency over the log service, in order to use a NullObject in case
-     * the log service is not available.
+     * If the "dm.runtime.log=true" or "dm.runtime.packageAdmin=true" parameter is configured in the Felix config.properties
+     * then we'll use a required/temporal service dependency over the respective service.
+     * These temporal dependencies avoid us to be restarted if the respective service is temporarily
+     * unavailable (that is: when the service is updating).
+     * if the "dm.runtime.log" or "dm.runtime.packageAdmin" is not configured or it it is set to false, then we'll use
+     * an optional dependency over the respective service, in order to use a NullObject in case
+     * the service is not available.  
      */
     @Override
     public void init(BundleContext context, DependencyManager dm) throws Exception
     {
-        boolean logActive = "true".equalsIgnoreCase(context.getProperty(CONF_LOG));
-        ServiceDependency logDep = logActive ?
-            createTemporalServiceDependency() : createServiceDependency().setRequired(false);        
-        logDep.setService(LogService.class).setAutoConfig(true);              
-        
-        dm.add(createComponent()
-               .setImplementation(DependencyManagerRuntime.class)
-               .setComposition("getComposition")
-               .add(logDep)
-               .add(createBundleDependency()
-                    .setRequired(false)
-                    .setAutoConfig(false)
-                    .setStateMask(Bundle.ACTIVE)
-                    .setFilter("(DependencyManager-Component=*)")
-                    .setCallbacks("bundleStarted", "bundleStopped")));
+        Component component = createComponent()
+            .setImplementation( DependencyManagerRuntime.class )
+            .setComposition( "getComposition" )
+            .add(createBundleDependency()
+                .setRequired( false )
+                .setAutoConfig( false )
+                .setStateMask( Bundle.ACTIVE )
+                .setFilter( "(DependencyManager-Component=*)" )
+                .setCallbacks( "bundleStarted", "bundleStopped" ) );
+                
+        Map<String, Class<?>> services = new HashMap<String, Class<?>>( 2 );
+        services.put( CONF_LOG, LogService.class );
+        services.put( CONF_PACKAGE_ADMIN, PackageAdmin.class );
+        for (Map.Entry<String, Class<?>> entry : services.entrySet())
+        {
+            String serviceProperty = entry.getKey();
+            Class<?> service = entry.getValue();
+            boolean serviceActive = "true".equalsIgnoreCase(context.getProperty(serviceProperty));
+            ServiceDependency serviceDep =
+                    serviceActive ? createTemporalServiceDependency()
+                                 : createServiceDependency().setRequired(false);
+            serviceDep.setService(service).setAutoConfig(true);
+            component.add(serviceDep);
+        }
+        dm.add( component );
     }
 
     /**
diff --git a/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/DependencyManagerRuntime.java b/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/DependencyManagerRuntime.java
index 9022064..2bd6aa7 100644
--- a/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/DependencyManagerRuntime.java
+++ b/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/DependencyManagerRuntime.java
@@ -29,6 +29,7 @@
 import org.apache.felix.dm.Component;
 import org.apache.felix.dm.DependencyManager;
 import org.osgi.framework.Bundle;
+import org.osgi.service.packageadmin.PackageAdmin;
 
 /**
  * This class parses service descriptors generated by the annotation bnd processor.
@@ -42,6 +43,7 @@
     private ConcurrentHashMap<Bundle, DependencyManager> m_managers =
             new ConcurrentHashMap<Bundle, DependencyManager>();
     private DescriptorParser m_parser;
+    private PackageAdmin m_packageAdmin; // Possibly a NullObject
 
     /**
      * Our constructor. We'll initialize here our DM component builders.
@@ -96,31 +98,26 @@
     }
 
     /**
-     * Checks if a started bundle have some DependencyManager descriptors 
-     * referenced in the "DependencyManager-Component" OSGi header.
-     * @param b the started bundle.
+     * Load the DM descriptors from the started bundle. We also check possible fragments 
+     * attached to the bundle, which might also contain some DM descriptors. 
+     * @param bundle the started bundle which contains a DependencyManager-Component header
      */
-    protected void bundleStarted(Bundle b)
+    protected void bundleStarted(Bundle bundle)
     {
-        String descriptorPaths = (String) b.getHeaders().get("DependencyManager-Component");
-        if (descriptorPaths == null)
-        {
-            return;
-        }
-
-        for (String descriptorPath : descriptorPaths.split(","))
-        {
-            URL descriptorURL = b.getEntry(descriptorPath);
-            if (descriptorURL == null)
-            {
-                Log.instance().error("Runtime: " + "DependencyManager component descriptor not found: %s",
-                                     descriptorPath);
-                continue;
+        Log.instance().info("Scanning started bundle %s", bundle.getSymbolicName());        
+        List<URL> descriptorURLs = new ArrayList<URL>();
+        collectDescriptors(bundle, descriptorURLs);                
+        Bundle[] fragments = m_packageAdmin.getFragments(bundle);
+        if (fragments != null) {
+            for (Bundle fragment : fragments) {
+                collectDescriptors(fragment, descriptorURLs);
             }
-            loadDescriptor(b, descriptorURL);
+        }
+        for (URL descriptorURL: descriptorURLs) {
+            loadDescriptor(bundle, descriptorURL);
         }
     }
-
+    
     /**
      * Unregisters all services for a stopping bundle.
      * @param b
@@ -132,7 +129,7 @@
         DependencyManager dm = m_managers.remove(b);
         if (dm != null)
         {
-            List<Component> services = new ArrayList(dm.getComponents());
+            List<Component> services = new ArrayList<Component>(dm.getComponents());
             for (Component service : services)
             {
                 Log.instance().info("Runtime: Removing service: %s", service);
@@ -142,15 +139,40 @@
     }
 
     /**
+     * Collect all descriptors found from a given bundle, including its possible attached fragments.
+     * @param bundle a started bundle containing some DM descriptors
+     * @param out the list of descriptors' URLS found from the started bundle, as well as from possibly
+     *        attached fragments.
+     */
+    private void collectDescriptors(Bundle bundle, List<URL> out) {
+        String descriptorPaths = (String) bundle.getHeaders().get("DependencyManager-Component");
+        if (descriptorPaths == null)
+        {
+            return;
+        }
+
+        for (String descriptorPath : descriptorPaths.split(","))
+        {
+            URL descriptorURL = bundle.getEntry(descriptorPath);
+            if (descriptorURL == null)
+            {
+                Log.instance()
+                        .error("Runtime: " + "DependencyManager component descriptor not found: %s",
+                               descriptorPath);
+                continue;
+            }
+            out.add(descriptorURL);
+        }        
+    }
+    
+    /**
      * Load a DependencyManager component descriptor from a given bundle.
      * @param b
      * @param descriptorURL
      */
     private void loadDescriptor(Bundle b, URL descriptorURL)
     {
-        Log.instance().debug("Runtime: ++++ Parsing descriptor %s from bundle %s",
-                             descriptorURL,
-                             b.getSymbolicName());
+        Log.instance().debug("Parsing descriptor %s from bundle %s", descriptorURL, b.getSymbolicName());
 
         BufferedReader in = null;
         try
@@ -164,7 +186,6 @@
             }
 
             m_parser.parse(in, b, dm);
-            m_managers.put(b, dm);
         }
 
         catch (Throwable t)