You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by pd...@apache.org on 2014/09/22 23:56:32 UTC

svn commit: r1626904 - in /felix/sandbox/pderop/dependencymanager-prototype/org.apache.felix.dependencymanager: ./ src/org/apache/felix/dm/ src/org/apache/felix/dm/impl/

Author: pderop
Date: Mon Sep 22 21:56:31 2014
New Revision: 1626904

URL: http://svn.apache.org/r1626904
Log:
Fixed problems and finalized implementation for parallel DM.

Modified:
    felix/sandbox/pderop/dependencymanager-prototype/org.apache.felix.dependencymanager/design.txt
    felix/sandbox/pderop/dependencymanager-prototype/org.apache.felix.dependencymanager/src/org/apache/felix/dm/DependencyManager.java
    felix/sandbox/pderop/dependencymanager-prototype/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/Activator.java
    felix/sandbox/pderop/dependencymanager-prototype/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ComponentScheduler.java

Modified: felix/sandbox/pderop/dependencymanager-prototype/org.apache.felix.dependencymanager/design.txt
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/org.apache.felix.dependencymanager/design.txt?rev=1626904&r1=1626903&r2=1626904&view=diff
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/org.apache.felix.dependencymanager/design.txt (original)
+++ felix/sandbox/pderop/dependencymanager-prototype/org.apache.felix.dependencymanager/design.txt Mon Sep 22 21:56:31 2014
@@ -21,29 +21,55 @@ This prototype demonstrates the new conc
 
  * Added support for concurrent mode: To allow components to be handled in parallel, you can now
    register in the OSGi service registry a threadpool (java.util.concurrent.Executor) service with
-   a "target=org.apache.felix.dependencymanager" service property, and you also have to set the
-   "org.apache.felix.dependencymanager.parallel" system property to either "*", or to a list of
-   component classnames prefixes. This will allow to handle all component dependencies and all
-   component lifecycle callbacks concurrently. Notice that component events are still handled
-   serially. The only difference is that multiple components can be handled in parallel. See the
-   org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/DispatchExecutor.java for for
-   informations.
+   a "target=org.apache.felix.dependencymanager" service property. Then, the dependency manager will
+   use that threadpool for managing components. If you want to ensure that the threadpool is
+   registered in the OSGi registry before all components are started, you have two choices:
+
+- you can first use the start level service and manage to start the management agent bundle which
+  provides the threadpool before all other bundles.
+
+- If the start level service is not an option, then you can use the
+  "org.apache.felix.dependencymanager.parallel" OSGi system property. This property allows
+  to specify all component implementation class name prefixes which have to wait for the thread pool
+  before being started by Dependency Manager. This property can simply take "*", or a list of
+  package prefixes (comma separated). This property can also be used to specify some components
+  which should not be handled using a threadpool, even if one is available from the OSGi
+  registry. To do so, just use a "!" in front of a given classname prefix.
 
-For example: 
+For example: if you want to enable parallelism for all components, just set the system property:
 
-- if you want to enable parallelism for all components, just set the system property:
+     org.apache.felix.dependencymanager.parallel=*
 
-org.apache.felix.dependencymanager.parallel=*
-
-- if you want to enable parallelism for some components whose class names is starting with foo.bar
+if you want to enable parallelism for some components whose class names is starting with foo.bar
   and foo.zoo:
 
-org.apache.felix.dependencymanager.parallel=foo.bar, foo.zoo
+      org.apache.felix.dependencymanager.parallel=foo.bar, foo.zoo
+
+if yo want to enable parallelism for all components, except for components whose class names
+  start with "foo.notparallel", then you can use a negation prefix ("!"), like this:
+
+      org.apache.felix.dependencymanager.parallel=!foo.notparallel, *
+
+Using the above property, all components will wait for the threadpool, except components which
+implementation class names which are starting with "foo.notparallel" prefix.
+
+- Notice that if the Dependency Manager API is used for declaring a threadpool and all its related
+  dependent services, then you have to use the "org.apache.felix.dependencymanager.parallel" system
+  property in order to exclude the threadpool DM components from the list of components using the
+  threadpool. As an example, you can take a look at the org.apache.felix.dependencymanager.samples
+  project, which is declaring a threadpool using the Dependency Manager API. The sample is then
+  using the following property in the org.apache.felix.dependencymanager.samples/bnd.bnd file:
+
+	org.apache.felix.dependencymanager.parallel=\
+		'!org.apache.felix.dependencymanager.samples.tpool, *',\
+
+Here, all components except those having a package starting with
+org.apache.felix.dependencymanager.samples.tpool will wait for the threadpool before being initialized 
+by Dependency Manager. But the org.apache.felix.dependencymanager.samples.tpool.ThreadPool will not
+wait for a threadpool, since *it is* the threadpool !
+
 
-- if yo uwant to enable parallelism for all components, except for components whose class names
-  start with "foo.notparallel":
 
-org.apache.felix.dependencymanager.parallel=!foo.parallel, *
 
 
 

Modified: felix/sandbox/pderop/dependencymanager-prototype/org.apache.felix.dependencymanager/src/org/apache/felix/dm/DependencyManager.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/org.apache.felix.dependencymanager/src/org/apache/felix/dm/DependencyManager.java?rev=1626904&r1=1626903&r2=1626904&view=diff
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/org.apache.felix.dependencymanager/src/org/apache/felix/dm/DependencyManager.java (original)
+++ felix/sandbox/pderop/dependencymanager-prototype/org.apache.felix.dependencymanager/src/org/apache/felix/dm/DependencyManager.java Mon Sep 22 21:56:31 2014
@@ -80,7 +80,6 @@ public class DependencyManager {
     private final BundleContext m_context;
     private final Logger m_logger;
     private final List<Component> m_components = new CopyOnWriteArrayList<>();
-    private volatile Executor m_threadPool;
 
     // service registry cache
     private static ServiceRegistryCache m_serviceRegistryCache;
@@ -138,15 +137,6 @@ public class DependencyManager {
     }
     
     /**
-     * Sets a threadpool to this dependency manager. All added/removed components will then be handled
-     * in parallel, using the provided threadpool.
-     */
-    public DependencyManager setThreadPool(Executor threadPool) {
-        m_threadPool = threadPool;
-        return this;
-    }
-
-    /**
      * Returns the list of currently created dependency managers.
      * @return the list of currently created dependency managers
      */
@@ -188,14 +178,7 @@ public class DependencyManager {
      */
     public void add(Component c) {
         m_components.add(c);
-        if (useComponentSchedulerFor(c)) {
-            ComponentScheduler.instance().add(c);
-        } else {
-            if (m_threadPool != null) {
-                ((ComponentContext) c).setThreadPool(m_threadPool);
-            }
-            ((ComponentContext) c).start(); 
-        }
+        ComponentScheduler.instance().add(c);
     }
 
     /**
@@ -205,11 +188,7 @@ public class DependencyManager {
      * @param service the service to remove
      */
     public void remove(Component c) {
-        if (useComponentSchedulerFor(c)) {
-            ComponentScheduler.instance().remove(c);
-        } else {
-            ((ComponentContext) c).stop();
-        }
+        ComponentScheduler.instance().remove(c);
         m_components.remove(c);
     }
 
@@ -701,44 +680,4 @@ public class DependencyManager {
             return context;
         }
     }
-    
-    /**
-     * Determine if the component scheduler should be used for a given component. The scheduler is used if all the 
-     * following conditions are true:
-     *   - The user has not set a threadpool using {@link #setThreadPool(Executor)}.
-     *   - the {@link #PARALLEL} system property is set to a comma separated list of prefix of component classnames
-     *   which have to be activated using the threadpool. Notice that prefixes can be negated using "!".
-     * 
-     * When used, the scheduler will bufferize all activated DM components until a threadpool with a
-     * {@link #THREADPOOL} service property is registered in the OSGi registry. And at the point where the 
-     * threadpool comes in, then all bufferized components will be activated using that threadpool.
-     * This simple mechanism allows to avoid to use a start level service in order to wait for the threadpool before
-     * activating any DM components.
-     * 
-     * @return true if the component scheduler should be used, false if not.
-     */
-    private boolean useComponentSchedulerFor(Component c) {
-        if (m_threadPool != null) {
-            return false;
-        }
-        
-        String parallel = m_context.getProperty(DependencyManager.PARALLEL);
-        if (parallel != null) {
-            for (String prefix : parallel.trim().split(",")) {
-                boolean not = prefix.startsWith("!");
-                if (not) {
-                    prefix = prefix.substring(1);
-                }
-                if ("*".equals(prefix)) {
-                    return !not;
-                }
-
-                if (c.getComponentDeclaration().getClassName().startsWith(prefix)) {
-                    return !not;
-                }
-            }
-        }
-        
-        return false;              
-    }
 }

Modified: felix/sandbox/pderop/dependencymanager-prototype/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/Activator.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/Activator.java?rev=1626904&r1=1626903&r2=1626904&view=diff
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/Activator.java (original)
+++ felix/sandbox/pderop/dependencymanager-prototype/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/Activator.java Mon Sep 22 21:56:31 2014
@@ -28,23 +28,18 @@ import org.osgi.framework.BundleContext;
  * DependencyManager Activator. We are using this activator in order to track and use a threadpool, which can be 
  * optionally registered by any management agent bundle.
  * The management agent can just register a <code>java.util.Executor</code> service in the service registry
- * using the "target=org.apache.felix.dependencymanager" property, and the "org.apache.felix.dependencymanager.parallel"
- * OSGi system property must also be configured to "true".
+ * using the "target=org.apache.felix.dependencymanager" property. 
  *    
  * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
  */
 public class Activator extends DependencyActivatorBase {
     @Override
     public void init(BundleContext ctx, DependencyManager mgr) throws Exception {
-        boolean parallelModeEnabled = ctx.getProperty(DependencyManager.PARALLEL) != null;
-        
-        if (parallelModeEnabled) {
-            mgr.add(createComponent()
-                    .setImplementation(ComponentScheduler.instance())
-                    .add(createTemporalServiceDependency(10000)
-                        .setService(Executor.class, "(target=" + DependencyManager.THREADPOOL + ")")
-                        .setRequired(true)
-                        .setAutoConfig("m_threadPool")));
-        }
+        mgr.add(createComponent()
+               .setImplementation(ComponentScheduler.instance())
+               .add(createServiceDependency()
+                   .setService(Executor.class, "(target=" + DependencyManager.THREADPOOL + ")")
+                   .setRequired(true)
+                   .setCallbacks("bind", "unbind")));
     }
 }

Modified: felix/sandbox/pderop/dependencymanager-prototype/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ComponentScheduler.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ComponentScheduler.java?rev=1626904&r1=1626903&r2=1626904&view=diff
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ComponentScheduler.java (original)
+++ felix/sandbox/pderop/dependencymanager-prototype/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ComponentScheduler.java Mon Sep 22 21:56:31 2014
@@ -18,68 +18,97 @@
  */
 package org.apache.felix.dm.impl;
 
-import java.util.Dictionary;
 import java.util.LinkedHashSet;
 import java.util.Set;
 import java.util.concurrent.Executor;
 
 import org.apache.felix.dm.Component;
 import org.apache.felix.dm.ComponentDeclaration;
-import org.apache.felix.dm.DependencyManager;
 import org.apache.felix.dm.context.ComponentContext;
+import org.osgi.framework.BundleContext;
 
 /**
- * When a DependencyManager is not explicitly configured with a threadpool, and when parallel mode is enabled,
- * then added components are delegated to this class, which will cache all added components until one threadpool
- * is registered in the OSGi service registry.
- * 
- * Parallel mode can be enabled by default by doing this:
- * 
- * - register a threadpool (java.util.concurrent.Executor) into the OSGi service registry with the 
- *   "target=org.apache.felix.dependencymanager" property.
- *   
- * - and set the "org.apache.felix.dependencymanager.parallel" system property to either "*" or to some component classname 
- *   prefixes. Note that you can specify several class name prefixes, and that some prefixes can be negated using "!".   
- *  
+ * The Dependency Manager delegates all components addition/removal to this class, which is in charge of tracking
+ * a threadpool from the OSGi registry in order to handle components concurrently.
+ * 
+ * By default, an external management bundle may register a threadpool (java.util.concurrent.Executor) with a
+ * special "target=org.apache.felix.dependencymanager" service property. So, if the threadpool is registered, then 
+ * any components added will use that threadpool. 
+ * 
+ * If you want to ensure that all components must wait for a threadpool, before they are actually added
+ * in a dependency manager, you can simply use the "org.apache.felix.dependencymanager.parallel" OSGi system 
+ * property, which can specify the list of components which  must wait for the threadpool.
+ * This property value can be a wildcard ("*"), or a list of components implementation class prefixes 
+ * (comma seperated). So, all components class names starting with the specified prefixes will be cached until the 
+ * threadpool becomes available.
+ * Some class name prefixes can also be negated (using "!"), in order to exclude some components from the list of 
+ * components using the threadpool.
+ * 
+ * Notice that if the threadpool and all the services it may depends on are also declared using the 
+ * Dependency Manager API, then you have to list the package of such components with a "!" prefix, in order to 
+ * indicat that those components must not wait for a threadpool (since they are part of the threadpool 
+ * implementation !).
+ * 
+ * Examples:
+ * 
+ * org.apache.felix.dependencymanager.parallel=*   
+ *      -> means all components must be cached until a threadpool comes up.
+ * 
+ * org.apache.felix.dependencymanager.parallel=foo.bar, foo.zoo
+ *      -> means only components whose implementation class names is starting with "foo.bar" or "foo.zoo" must wait for and 
+ *      use a threadpool.   
+ * 
+ * org.apache.felix.dependencymanager.parallel=!foo.threadpool, *
+ *      -> means all components must wait for and use a threadpool, except the threadpool components implementations class names
+ *       (starting with foo.threadpool prefix). 
+ *       
  * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
  */
 public class ComponentScheduler {
     private final static ComponentScheduler m_instance = new ComponentScheduler();
+    private final static String PARALLEL = "org.apache.felix.dependencymanager.parallel";
     private volatile Executor m_threadPool;
     private final Executor m_serial = new SerialExecutor(null);
-    private boolean m_started;
     private Set<Component> m_pending = new LinkedHashSet<>();
 
     public static ComponentScheduler instance() {
         return m_instance;
     }
 
-    protected void start() {
+    protected void bind(final Executor threadPool) {
+        m_threadPool = threadPool;
         m_serial.execute(new Runnable() {
             @Override
             public void run() {
-                m_started = true;
                 for (Component c : m_pending) {
-                    addUsingThreadPool(c);
+                    ((ComponentContext) c).setThreadPool(threadPool);
+                    ((ComponentContext) c).start();
                 }
                 m_pending.clear();
             }
         });
     }
 
+    protected void unbind(Executor threadPool) {
+        m_threadPool = null;
+    }
+
     public void add(final Component c) {
-        if (!isParallelComponent(c)) {
+        if (mayStartNow(c)) {
             ((ComponentContext) c).start();
         }
         else {
+            // The component requires a threadpool: delay execution until one is available.
             m_serial.execute(new Runnable() {
                 @Override
                 public void run() {
-                    if (!m_started) {
+                    Executor threadPool = m_threadPool;
+                    if (threadPool == null) {
                         m_pending.add(c);
                     }
                     else {
-                        addUsingThreadPool(c);
+                        ((ComponentContext) c).setThreadPool(threadPool);
+                        ((ComponentContext) c).start();
                     }
                 }
             });
@@ -87,48 +116,60 @@ public class ComponentScheduler {
     }
 
     public void remove(final Component c) {
-        if (!isParallelComponent(c)) {
-            ((ComponentContext) c).stop();
-        }
-        else {
-            m_serial.execute(new Runnable() {
-                @Override
-                public void run() {
-                    if (!m_started) {
-                        m_pending.remove(c);
-                    }
-                    else {
-                        ((ComponentContext) c).stop();
-                    }
+        m_serial.execute(new Runnable() {
+            @Override
+            public void run() {
+                if (!m_pending.remove(c)) {
+                    ((ComponentContext) c).stop();
                 }
-            });
-        }
+            }
+        });
     }
 
-    private void addUsingThreadPool(Component c) {
-        ((ComponentContext) c).setThreadPool(m_threadPool);
-        ((ComponentContext) c).start();
+    private boolean mayStartNow(Component c) {
+        Executor threadPool = m_threadPool;
+        BundleContext ctx = c.getDependencyManager().getBundleContext();
+        String parallel = ctx.getProperty(PARALLEL);
+
+        if (threadPool == null) {
+            // No threadpool available. If a "parallel" OSGi system property is specified, we have to wait for a
+            // threadpool if the component class name is matching one of the prefixes specified in the "parallel"
+            // system property.
+            if (parallel != null && requiresThreadPool(c, parallel)) {
+                return false; // wait for a threadpool
+            } else {
+                return true; // no threadpool required, start the component now, synchronously
+            }
+        }
+        else {
+            // A threadpool is there. If the "parallel" OSGi system property not specified, we can start the component
+            // now and we'll use the threadpool for it.
+            // But if the "parallel" system property is specified, the component will use the threadpool only if it's
+            // classname is starting with one of the prefixes specified in the property.
+            if (parallel == null || requiresThreadPool(c, parallel)) {
+                ((ComponentContext) c).setThreadPool(threadPool);
+            }
+            return true; // start the component now, possibly using the threadpool (see above).
+        }
     }
 
-    private boolean isParallelComponent(Component c) {
+    private boolean requiresThreadPool(Component c, String parallel) {
         // The component declared from our DM Activator can not be parallel.
         ComponentDeclaration decl = c.getComponentDeclaration();
         if (ComponentScheduler.class.getName().equals(decl.getName())) {
             return false;
         }
 
-        // A threadpool component declared by a "management agent" using DM API cannot be itself parallel.
-        String[] services = decl.getServices();
-        if (services != null) {
-            for (String service : services) {
-                if (Executor.class.getName().equals(service)) {
-                    Dictionary<?, ?> props = decl.getServiceProperties();
-                    if (props != null && DependencyManager.THREADPOOL.equals(props.get("target"))) {
-                        return false;
-                    }
-                }
+        for (String prefix : parallel.trim().split(",")) {
+            prefix = prefix.trim();
+            boolean not = prefix.startsWith("!");
+            if (not) {
+                prefix = prefix.substring(1).trim();
+            }
+            if ("*".equals(prefix) || c.getComponentDeclaration().getClassName().startsWith(prefix)) {
+                return !not;
             }
         }
-        return true;
+        return false;
     }
 }