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/11/08 02:13:49 UTC

svn commit: r1637498 - /felix/sandbox/pderop/dependencymanager-prototype/org.apache.felix.dependencymanager/src/org/apache/felix/dm/ComponentExecutorFactory.java

Author: pderop
Date: Sat Nov  8 01:13:49 2014
New Revision: 1637498

URL: http://svn.apache.org/r1637498
Log:
Clarified the javadoc about the new concurrency principles of DM4

Modified:
    felix/sandbox/pderop/dependencymanager-prototype/org.apache.felix.dependencymanager/src/org/apache/felix/dm/ComponentExecutorFactory.java

Modified: felix/sandbox/pderop/dependencymanager-prototype/org.apache.felix.dependencymanager/src/org/apache/felix/dm/ComponentExecutorFactory.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/org.apache.felix.dependencymanager/src/org/apache/felix/dm/ComponentExecutorFactory.java?rev=1637498&r1=1637497&r2=1637498&view=diff
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/org.apache.felix.dependencymanager/src/org/apache/felix/dm/ComponentExecutorFactory.java (original)
+++ felix/sandbox/pderop/dependencymanager-prototype/org.apache.felix.dependencymanager/src/org/apache/felix/dm/ComponentExecutorFactory.java Sat Nov  8 01:13:49 2014
@@ -3,59 +3,147 @@ package org.apache.felix.dm;
 import java.util.concurrent.Executor;
 
 /**
- * A ComponentExecutorFactory service can be registered by a management agent bundle in order to
- * supply custom Executors used to manage DependencyManager components.<p>
- * When a Component is added to a DependencyManager, the ComponentExecutorFactory is then 
- * used to create an Executor for that component and the Executor will be used to execute 
- * all the DM internal code related to the component's dependency management. The Executor 
- * will also be used to invoke all the component's lifecycle callbacks.<p>
- * 
- * All the component dependency management/lifecycle callbacks will be handled in the Executor, 
- * but serially, in FIFO order (it's actually a kind of actor thread model). This means that you then 
- * don't have to deal with synchronizations anymore in your component dependencies/lifecycle callbacks; 
- * and multiple components will be managed and started concurrently, in parallel.<p>
- * 
- * If you want to ensure that the ComponentExecutorFactory is registered in the OSGI registry 
- * before all other DM components are added to their respective DependencyManagers, then 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 ComponentExecutorFactory service.
- * This property value can be set to a wildcard ("*"), or a list of components implementation class prefixes 
- * (comma separated). So, all components class names starting with the specified prefixes will be cached 
+ * A <code>ComponentExecutorFactory</code> service can be registered by any management agent bundle 
+ * in order to enable parallel activation of Components.<p>
+ * 
+ * A <code>ComponentExecutorFactory</code> is part of the new concurrency model that forms the basis 
+ * of Dependency Manager 4.0. Let's first give a brief overview of the default thread model used when 
+ * no ComponentExecutorFactory is used. Then we'll explain the rationale and the usage of a 
+ * <code>ComponentExecutorFactory</code> service.
+ * <p> 
+ * 
+ * <h3>Default Thread Model</h3>
+ * 
+ * By default, Dependency Manager uses a <b>lock-free/single thread</b> model:
+ * <p><ul>
+ * 
+ * <li> When an external event that influence the state of a Component is taking place (for example, 
+ * when a service dependency on which the Component is depending on is registered in the registry by 
+ * a given thread), then DependencyManager does not perform any locking for the handling of the event. 
+ * Instead of that, a job that will handle the event is inserted in an internal lock-free 
+ * <b><code>Serial Queue</code></b> which is internally maintained in each Component.
+ * 
+ * <li> all jobs scheduled in the <code>Serial Queue</code> are then executed in FIFO order. This avoid 
+ * to use complex synchronization tricky code in DM internals, and also it dramatically simplifies the 
+ * development of DM components, because all lifecycle callbacks (init/start/stop/destroy) and dependency 
+ * injections are scheduled through the <code>Serial Queue</code>. This means that your component is not 
+ * concurrently called in lifecycle callbacks and in dependency injection methods.
+ * 
+ * <li> Now let's describe which thread is executing the jobs scheduled in a Component <code>Serial Queue</code>: 
+ * When a job (J1) is scheduled in the queue while it is empty, then the current thread becomes the "master"
+ * and will immediately execute the </code>Serial Queue</code> tasks (synchronously). And if another thread 
+ * triggers another event concurrently while the "master" thread is executing the job J1, then a job (J2) 
+ * for this new event is just enqueued in the <code>Serial Queue</code>, but the other thread returns 
+ * immediately to the caller, and the job J2 will then be executed by the "master" thread (after J1).
+ * </ul>
+ * 
+ * <p>
+ * This mechanism allows to serially handle all Component events (service dependencies) in FIFO order 
+ * without maintaining any locks.
+ * 
+ * <h3>Enabling parallelism with a <code>ComponentExecutorFactory</code></h3>
+ *  
+ * As described above, all the external events that influence the state of a given component are handed by 
+ * jobs scheduled in the <code>Serial Queue</code> of the Component, and the jobs are getting executed serially 
+ * by a single "master" thread. So usually, bundles are started from a single thread, meaning that all Components
+ * are then activated synchronously.
+ * <p>
+ * 
+ * But when you register in the OSGi service registry a <code>ComponentExecutorFactory</code>, that factory 
+ * will be used by DependencyManager to create an Executor of your choice for each Component, typically a shared 
+ * threadpool configured by yourself. And all the Component <code>Serial Queues</code> will be executed using 
+ * the Executor returned by the {@link #getExecutorFor(Component)} method.
+ * However, jobs scheduled in the <code>Serial Queue</code> of a given Component are still executed one at a 
+ * time, in FIFO order and the Component remains single threaded, and <b>independent Components 
+ * may then each be managed and activated concurrently with respect to each other</b>.
+ * <p>
+ * If you want to ensure that all Components are initialized <b>after</b> the ComponentExecutorFactory is 
+ * registered in the OSGI registry, you can use the "org.apache.felix.dependencymanager.parallel" OSGi 
+ * system property which specifies the list of components which must wait for the ComponentExecutorFactory 
+ * service. This property value can be set to a wildcard ("*"), or a list of components implementation class 
+ * prefixes (comma separated). So, all components class names starting with the specified prefixes will be cached 
  * until the ComponentExecutorFactory service is registered (In this way, it is not necessary to use
  * the StartLevel service if you want to ensure that all components are started concurrently).
+ * <p>
  * 
  * Some class name prefixes can also be negated (using "!"), in order to exclude some components from the 
- * list of components using the ComponentExecutorFactory service.<p>
+ * list of components using the ComponentExecutorFactory service.
+ * <p>
  * 
  * Notice that if the ComponentExecutorFactory itself and all its dependent services are defined using 
  * the Dependency Manager API, then you have to list the package of such components with a "!" 
  * prefix, in order to indicate that those components must not wait for a ComponentExecutorFactory service
- * (since they are part of the ComponentExecutorFactory implementation !).<p>
- * 
- * Examples:
+ * (since they are part of the ComponentExecutorFactory implementation !).
+ * <p>
  * 
- * <blockquote>
+ * <h3>Examples for the usage of the "org.apache.felix.dependencymanager.parallel" property:</h3>
  * 
- * <pre>
+ * <blockquote><pre>
  * org.apache.felix.dependencymanager.parallel=*   
  *      -> means all components must be cached until a ComponentExecutorFactory comes up.
  * 
  * org.apache.felix.dependencymanager.parallel=foo.bar, foo.zoo
  *      -> means only components whose implementation class names are starting with "foo.bar" or "foo.zoo" 
- *      must be handled using an Executor returned by the ComponentExecutorFactory service.
+ *      must be handled using an Executor returned by the ComponentExecutorFactory service. Other Components
+ *      will be handled normally, as when there is no ComponentExecutorFactory available.
  * 
  * org.apache.felix.dependencymanager.parallel=!foo.threadpool, *
  *      -> means all components must be delayed until the ComponentExecutorFactory comes up, except the 
  *      components whose implementations class names are starting with "foo.threadpool" prefix). 
- * </pre>
- * </blockquote>
+ * </pre></blockquote>
+ * 
+ * <h3>Examples of a ComponentExecutorFactory that provides a shared threadpool:</h3>
+ * 
+ * First, we define the OSGi bundle context system property to enable parallelism for all DM Components
+ * excepts the one which declares the ComponentExecutorFactory:
  * 
- * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a> 
+ * <blockquote> <pre>
+ *   org.apache.felix.dependencymanager.parallel=!com.acme.management.threadpool, *
+ * </pre></blockquote>
+ * 
+ * Next, here is the Activator which declares the ComponentExecutorFactory:
+ * 
+ * <blockquote> <pre>
+ *   package com.acme.management.threadpool;
+ *   import org.apache.felix.dm.*;
+ *   
+ *   public class Activator extends DependencyActivatorBase {      
+ *      public void init(BundleContext context, DependencyManager mgr) throws Exception {
+ *         mgr.add(createComponent()
+ *            .setInterface(ComponentExecutorFactory.class.getName(), null)
+ *            .setImplementation(ComponentExecutorFactoryImpl.class)
+ *            .add(createConfigurationDependency()
+ *                 .setPid("com.acme.management.threadpool.ComponentExecutorFactoryImpl")));
+ *      }
+ *   }
+ * </pre></blockquote>
+ * 
+ * And here is the implementation for our ComponentExecutorFactory:
+ * 
+ * <blockquote> <pre>
+ *   package com.acme.management.threadpool;
+ *   import org.apache.felix.dm.*;
+ *
+ *  public class ComponentExecutorFactoryImpl implements ComponentExecutorFactory {
+ *      volatile Executor m_threadPool;
+ *      
+ *      void updated(Dictionary conf) {
+ *          m_sharedThreadPool = Executors.newFixedThreadPool(Integer.parseInt("threadpool.size"));
+ *      }
+ *
+ *      &#64;Override
+ *      public Executor getExecutorFor(Component component) {
+ *          return m_sharedThreadPool; // Use a shared threadpool for all Components
+ *      }
+ *  }
+ * </pre></blockquote>
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ * @since 4.0.0
  */
 public interface ComponentExecutorFactory {
     /**
-     * Returns an Executor (typically or thread pool) used to handle the component's dependencies 
-     * and invoke all component lifecycle callbacks.
+     * Returns an Executor (typically a shared thread pool) used to manage a given DependencyManager Component.
      * 
      * @param component the Component to be managed by the returned Executor
      * @return an Executor used to manage the given component, or null if the component must not be managed using any executor.