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"));
+ * }
+ *
+ * @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.