Author: pderop
Date: Tue Feb 2 19:32:17 2016
New Revision: 1728179
URL: http://svn.apache.org/viewvc?rev=1728179&view=rev
Log:
Started documentation on upcomming dm-lambda.
Added:
felix/site/trunk/content/documentation/subprojects/apache-felix-dependency-manager/guides/dm-lambda.mdtext
Modified:
felix/site/trunk/content/documentation/subprojects/apache-felix-dependency-manager.mdtext
Modified:
felix/site/trunk/content/documentation/subprojects/apache-felix-dependency-manager.mdtext
URL:
http://svn.apache.org/viewvc/felix/site/trunk/content/documentation/subprojects/apache-felix-dependency-manager.mdtext?rev=1728179&r1=1728178&r2=1728179&view=diff
==============================================================================
---
felix/site/trunk/content/documentation/subprojects/apache-felix-dependency-manager.mdtext
(original)
+++
felix/site/trunk/content/documentation/subprojects/apache-felix-dependency-manager.mdtext
Tue Feb 2 19:32:17 2016
@@ -37,6 +37,7 @@ Below is the full table of contents.
* [Performance
Tuning](apache-felix-dependency-manager/guides/performance-tuning.html)
* [Development](apache-felix-dependency-manager/guides/development.html)
* [Design
Patterns](apache-felix-dependency-manager/guides/design-patterns.html)
+* [Dependency Manager
Lambda]((apache-felix-dependency-manager/guides/dm-lambda.html)
* [Resource Adapters](apache-felix-dependency-manager/guides/resources.html)
* [Javadocs](apache-felix-dependency-manager/guides/javadocs.html)
@@ -56,4 +57,4 @@ Below is the full table of contents.
*
[Resource](apache-felix-dependency-manager/reference/dependency-resource.html)
* [Thread Model](apache-felix-dependency-manager/reference/thread-model.html)
* [External Links and
Articles](apache-felix-dependency-manager/reference/external-links.html)
-
+
Added:
felix/site/trunk/content/documentation/subprojects/apache-felix-dependency-manager/guides/dm-lambda.mdtext
URL:
http://svn.apache.org/viewvc/felix/site/trunk/content/documentation/subprojects/apache-felix-dependency-manager/guides/dm-lambda.mdtext?rev=1728179&view=auto
==============================================================================
---
felix/site/trunk/content/documentation/subprojects/apache-felix-dependency-manager/guides/dm-lambda.mdtext
(added)
+++
felix/site/trunk/content/documentation/subprojects/apache-felix-dependency-manager/guides/dm-lambda.mdtext
Tue Feb 2 19:32:17 2016
@@ -0,0 +1,450 @@
+Title: Dependency Manager Lambda
+
+Since the R7 version, a new dm-lambda library has been introduced in the DM
distribution. This new library allows to programmatically declare OSGi
components
+using a bit more fluent, concise and type-safe API that is based on Java8
Lambda expressions and other goodies like method references.
+
+## Principle
+
+The new library is based on the `builder` design pattern applied to java8
lambdas. Basically, you call a chain of methods from a fluent `builder`, and at
the end of the chain, you call
+`build()` which returns the actual DM objects that you already know from the
original DM API. We'll see later that using lambdas you can then build the
objects and add them to
+the DependencyManager class automatically.
+
+The new API is provided by the `org.apache.felix.dependencymanager.lambda.jar`
bundle. the following builders are currently supported:
+
+- ComponentBuilder: it is used to build some instances of the
org.apache.felix.dm.Component interface.
+- ServiceDependencyBuilder: builds some instances of
org.apache.felix.dm.ServiceDependency.
+- ConfigurationDependencyBuiler: builds some instances of
org.apache.felix.dm.ConfigurationDependency.
+- BundleAdapterBuilder: builds some DM bundle adapters.
+- ServiceAdapterBuilder.java: builds some instances of DM service adapters.
+- FactoryPidAdapterBuilder: builds some instances of DM factory pid adapters.
+- FutureDependencyBuilder: it's a new feature, allowing to "wait for" an
asynchronous event represented by a standard jdk8 `CompletableFuture` object.
+
+(There is currently no builders for DM ResourceDependency and ResourceAdapter
objects, but they will be supported soon).
+
+There are two ways to use these builders:
+
+You can first instantiate builders using some of the convenient factory
methods available from the DependencyManagerActivator class, which is the new
base class
+for dm-lambda activators:
+
+ :::java
+ import static org.apache.felix.dm.lambda.DependencyManagerActivator.*;
+
+ public class Activator extends DependencyActivatorBase {
+ @Override
+ public void init(BundleContext ctx, DependencyManager dm) throws
Exception {
+ Component comp = component().impl(Hello.class).build();
+ m_dm.add(comp);
+ }
+ }
+
+The `component()` method returns a `ComponentBuilder` and the call to `build`
at the end of the call chain returns the actual DM Component object.
+
+Now, most of the time, in an Activator, you usually almost always create and
immediately add the component in the `dm` object.
+So, in order to reduce the "`code ceremony`", you can also use a special
overloaded factory method that accepts a lambda which takes as argument a
+`Consumer<ComponentBuilder>` parameter.
+So, the lambda has just to invoke the chain of necessary methods from the
builder, without having to call `build` and add the returned Component to the
`dm` object.
+
+The following is the same as above, using a consumer<ComponentBuilder> lambda
expression:
+
+ :::java
+ public class Activator extends DependencyActivatorBase {
+ @Override
+ public void init(BundleContext ctx, DependencyManager dm) throws
Exception {
+ component((ComponentBuilder comp) -> comp.impl(Hello.class));
+ }
+ }
+
+And here is a more concise version where the type of the lambda parameter is
not declared:
+
+ :::java
+ public class Activator extends DependencyActivatorBase {
+ @Override
+ public void init(BundleContext ctx, DependencyManager dm) throws
Exception {
+ component(comp -> comp.impl(Hello.class));
+ }
+ }
+
+## Adding service dependencies
+
+Service Dependencies, unlike in the original DM API, are required by default,
and you can add a dependency using the `withSrv` methods available from the
ComponentBuilder interface.
+Such method accepts a Consumer<ServiceDependencyBuilder> lambda expression,
which may then configure the dependency using a chain of method calls
(filter/callbacks,autoconfig, etc ...):
+
+ :::java
+ public class Activator extends DependencyActivatorBase {
+ @Override
+ public void init(BundleContext ctx, DependencyManager dm) throws
Exception {
+ component(comp -> comp.impl(Hello.class).withSrv(LogService.class,
(ServiceDependencyBuilder srv) -> srv.filter("(vendor=apache)")));
+ }
+ }
+
+The above example adds a service dependency on a LogService with a service
filter. Here is a more concise version where the type of the `srv` lambda
parameter is not declared:
+
+ :::java
+ public class Activator extends DependencyActivatorBase {
+ @Override
+ public void init(BundleContext ctx, DependencyManager dm) throws
Exception {
+ component(comp -> comp.impl(Hello.class).withSrv(LogService.class,
srv -> srv.filter("(vendor=apache)")));
+ }
+ }
+
+If you depend on multiple required services (with no filters), you can declare
the services in one shot like this:
+
+ :::java
+ public class Activator extends DependencyActivatorBase {
+ @Override
+ public void init(BundleContext ctx, DependencyManager dm) throws
Exception {
+ // using a varargs of service dependencies ...
+ component(comp -> comp.impl(Hello.class).withSrv(LogService.class,
EventAdmin.class));
+ }
+ }
+
+## Defining Service Dependency Component's callbacks
+
+By default, service dependencies are auto injected in class fields (you can
configure the name of the class field where the dependency should be injected).
+But like in the current DM API, you can specify callbacks on the component
implementation class using the "`cb`" `ServiceDependencyBuilder` method:
+
+ :::java
+ public class Activator extends DependencyActivatorBase {
+ @Override
+ public void init(BundleContext ctx, DependencyManager dm) throws
Exception {
+ component(comp -> comp.impl(Hello.class).withSrv(LogService.class,
srv -> srv.cb("setLog")));
+ }
+ }
+
+The `cb` method accepts a varargs of strings (up to 4 method names):
+
+1. when using one argument, the first argument is used as the `add` callback.
+1. when using two argument, the first argument is used as the `add` callback,
and the second one as the `remove` callback.
+1. when using three arguments, the first argument is used as the `add`
callback, the second one as the "change" callback, and the third one as the
`remove` callback.
+1. when using four arguments, the given argument is used as the `add`
callback, the second one as the "change" callback, the third one as the
`remove` callback, and the last one as the `swap` callback.
+
+The add/change/remove callbacks accepts the following kind of method
signatures ("S" represents the type of the service dependency):
+
+ method(S service)
+ method(S service, Map<String, Object> serviceProperties)
+ method(S service, Dictionary<String, Object> serviceProperties)
+ method(ServiceReference<S> serviceRef, S service),
+ method(ServiceReference<S> serviceRef)
+ method(Component serviceComponent)
+ method(Component serviceComponent, ServiceReference<S> serviceRef)
+ method(Component serviceComponent, S service)
+ method(Component serviceComponent, ServiceReference<S> serviceRef, S
service)
+
+And the "swap" callbacks accepts the following method signatures:
+
+ swapMethod(S oldService, S newService)
+ swapMethod(ServiceReference<S> oldRef, S old, ServiceReference<S> newRef,
S newService)
+ swapMethod(Component component, S oldService, S newService)
+ swapMethod(Component component, ServiceReference<S> oldRef, S old,
ServiceReference<S> newRef, S newService)
+
+Now you can also use a more type-safe callback using a Java 8 method reference:
+
+ :::java
+ public class Activator extends DependencyActivatorBase {
+ @Override
+ public void init(BundleContext ctx, DependencyManager dm) throws
Exception {
+ component(comp -> comp.impl(Hello.class).withSrv(LogService.class,
srv -> srv.cb(Hello::setLog)));
+ }
+ }
+
+or:
+
+ :::java
+ public class Activator extends DependencyActivatorBase {
+ @Override
+ public void init(BundleContext ctx, DependencyManager dm) throws
Exception {
+ component(comp -> comp.impl(Hello.class).withSrv(LogService.class,
srv -> srv.cb(Hello::setLog, Hello::unsetLog)));
+ }
+ }
+
+## Defining Service Dependency Object instance callback
+
+Sometimes, you want to inject the dependency to a seperate object that is not
part of the component implementation classes.
+In this case, you can use the "`cbi`" method (which stands for "`callback
instance`").
+
+For example, the following example injects a dependency in a DependencyHandler
instance:
+
+ :::java
+ public class Activator extends DependencyActivatorBase {
+ @Override
+ public void init(BundleContext ctx, DependencyManager dm) throws
Exception {
+ DependencyHandler depHandler = new DependencyHandler();
+ component(comp -> comp.impl(Hello.class).withSrv(LogService.class,
srv -> srv.cbi(depHandler, "setLog")));
+ }
+ }
+
+or using method reference:
+
+ :::java
+ public class Activator extends DependencyActivatorBase {
+ @Override
+ public void init(BundleContext ctx, DependencyManager dm) throws
Exception {
+ DependencyHandler depHandler = new DependencyHandler();
+ component(comp -> comp.impl(Hello.class).withSrv(LogService.class,
srv -> srv.cbi(depHandler::setLog)));
+ }
+ }
+
+You can chain multiple callbacks:
+
+ :::java
+ public class Activator extends DependencyActivatorBase {
+ @Override
+ public void init(BundleContext ctx, DependencyManager dm) throws
Exception {
+ DependencyHandler depHandler = new DependencyHandler();
+ component(comp -> comp.impl(Hello.class).withSrv(LogService.class,
srv -> srv.cb(Hello::setLog).cbi(depHandler::setLog)));
+ }
+ }
+
+## Providing a service
+
+When a component provides a service with some properties, so far it was
necessary to create a Dictionary and pass it to the `Component.setInterface()`
method.
+
+Now you can now pass properties as varargs of properties (a suite of key-value
properties):
+
+ :::java
+ public class Activator extends DependencyActivatorBase {
+ @Override
+ public void init(BundleContext ctx, DependencyManager dm) throws
Exception {
+ component(comp ->
comp.impl(Hello.class).provides(HelloService.class, "p1", "v1", "p2", 123));
+ }
+ }
+
+or if you build your program using the `-parameter` option, you can also use
the "FluentProperty" lambda that allows to declare
+service properties as a suite of FlientProperty lambdas:
+
+ :::java
+ public class Activator extends DependencyActivatorBase {
+ @Override
+ public void init(BundleContext ctx, DependencyManager dm) throws
Exception {
+ component(comp ->
comp.impl(Hello.class).provides(HelloService.class, p1 -> "v1", p2 -> 123));
+ }
+ }
+
+## Managing components outside of Activators.
+
+You can create Components outside of the Activator by using some static
factory methods from the `DependencyManagerActivator` class.
+
+For example, considere a use case where you want to retrieve some informations
from some already injected services, and you then want to dynamically add more
dependencies from your
+`init` component callback. First let's look at the Activator:
+
+ :::java
+ public class Activator extends DependencyActivatorBase {
+ @Override
+ public void init(BundleContext ctx, DependencyManager dm) throws
Exception {
+ component(comp -> comp.impl(Pojo.class).withCnf("pojo.pid"));
+ }
+ }
+
+Here, we define a Configuration dependency with a "pojo.pid" configuration
pid. So, now, the Pojo will then for example be able to parse an xml from the
configuration, and depending on
+what it has parsed, it will possibly add more dependencies, like this:
+
+ import static org.apache.felix.dm.lambda.DependencyManagerActivator.*;
+
+ :::java
+ public class Pojo {
+ void updated(Dictionary conf) throws Exception {
+ parseXml(conf.get("some.xml.configuration"));
+ }
+
+ void init(Component c) { // lifecycle dm callback that allow you to add
more dependencies
+ if (xmlConfigurationRequiresEventAdmin) {
+ component(c, comp -> comp.withSrv(EventAdmin.class));
+ }
+ }
+ }
+
+The available variety of factory methods allows you to also create some DM
objects and add them manually, like:
+
+ :::java
+ public class Pojo {
+ void updated(Dictionary conf) throws Exception {
+ parseXml(conf.get("some.xml.configuration"));
+ }
+
+ void init(Component c) { // lifecycle dm callback that allow you to add
more dependencies
+ if (xmlConfigurationRequiresEventAdmin) {
+ DependencyManager dm = c.getDependencyManager();
+ ServiceDependency dep = serviceDependency(c,
EventAdmin.class).filter("(vendor=felix)").build();
+ dm.add(dep);
+ }
+ }
+ }
+
+## CompletableFuture dependency.
+
+The new library provides a new feature; you can now make your component depend
on the result of a jdk8 `CompletableFuture`.
+CompletableFuture java8 class provides an asynchronous event-driven model and
you can now define dependencies on any asynchronous events,
+like if they were service dependencies.
+
+Assume you develop an Http Service and you want to register it in the OSGi
service registry only once the service is listening on port 80.
+Now, you want to use for example Vertx.io which allows to build reactive
applications on the JVM. This library wraps asynchronous events behind a
CompletableFuture.
+
+So, naturally, you can write from your init() method something like this:
+
+ :::java
+ public class HttpServiceImpl implements HttpService {
+
+ // lifecycle dm callback that allow you to add more dependencies
+ void init(Component c) {
+ CompletableFuture<HttpServer> futureServer =
createServer().listenFuture();
+ component(c, comp -> comp.withFuture(futureService, future ->
future.cbi(this::serverReady)));
+ }
+
+ // Inject our HttpServer that is listening
+ void serverReady(HttpServer server) { ... }
+
+ void start() {
+ // at this point we are fully started
+ }
+
+ }
+
+and your HttpService will be registered only once the server is listening.
+
+## Comparing two activators using old and new API:
+
+Assume we have a `ServiceConsumer` which depends on the following services:
+
+- a required `ServiceProvider`: with `(p1=v1)` service filter and using a
"setProvider" callback.
+- a Configuration with
pid=`org.apache.felix.dm.lambda.samples.hello.ServiceConsumer`.
+
+Now assume we have `ServiceProvider` provided with p1="v1" and p2=123 service
properties; and the provider also depends on:
+
+- a required `LogService` service (injected in class fields).
+- a required `EventAdmin` service (injected in class fields).
+
+Then we have the following typical Activator (we define both components in the
same Activator for simplicity):
+
+ import org.apache.felix.dm.DependencyActivatorBase;
+ ...
+
+ :::java
+ public class Activator extends DependencyActivatorBase {
+ @Override
+ public void init(BundleContext ctx, DependencyManager dm) throws
Exception {
+ // Declare our Consumer component
+
+ Component consumer = dm.createComponent()
+ .setImplementation(ServiceConsumer.class)
+
.add(createServiceDependency().setService(ServiceProvider.class,
"(p1=v1)").setRequired(true).setCallbacks("setProvider", null))
+
.add(createConfigurationDependency().setPid("org.apache.felix.dm.lambda.samples.hello.ServiceConsumer"));
+ dm.add(consumer);
+
+ // Declare our ServiceProvider service component
+
+ Properties properties = new Properties();
+ Properties.put("p1", "v1");
+ properties.put("p2", 123);
+ Component provider = dm.createComponent()
+ .setImplementation(ServiceProviderImpl.class)
+ .setInterface(ServiceProvider.class.getName(), properties)
+
.add(createServiceDependency().setService(LogService.class).setRequired(true))
+ .add(createServiceDependency().setService(EventAdmin.class,
null).setRequired(true));
+ dm.add(provider);
+ }
+ }
+
+Now, let's rework the above example, using the new dm-lambda API:
+
+ :::java
+ import org.apache.felix.dm.lambda.DependencyManagerActivator;
+ ...
+
+ public class Activator extends DependencyManagerActivator {
+ @Override
+ public void activate() throws Exception {
+ // Declare our Consumer component
+
+ component(comp -> comp.impl(ServiceConsumer.class)
+ .withSrv(ServiceProvider.class, srv ->
srv.filter("(p1=v1)").cb(ServiceConsumer::setProvider))
+ .withCnf(ServiceConsumer.class));
+
+ // Declare our ServiceProvider service component:
+
+ component(comp -> comp.impl(ServiceProviderImpl.class)
+ .provides(ServiceProvider.class, p1 -> "v1", p2 -> 123)
+ .withSrv(LogService.class, EventAdmin.class));
+ }
+
+## Sample codes
+
+many samples codes are available from the distribution source release: Please
take a look at the following:
+
+###
org.apache.felix.dependencymanager.lambda.samples/src/org/apache/felix/dm/lambda/samples/hello/
+
+This sample provides a DM Activator declaring one service consumer and a
service provider. The
+ServiceConsumer is also depending on a configuration pid (see
org.apache.felix.dependencymanager.samples.hello.Configurator).
+
+###
org.apache.felix.dependencymanager.lambda.samples/src/org/apache/felix/dm/lambda/samples/compositefactory/
+
+This Activator is an example usage of DM composite components. A composite
component is implemented
+using a composition of multiple object instances, which are used to implement
a given service.
+
+The sample also uses a Factory approach in order to instantiate the
composition of objects: A
+"CompositionManager" is first injected with a Configuration that can possibly
be used to create
+and configure all the composites.
+
+Dependencies are injected is some of the component implementation instances,
using java8 method references. For instance,
+the LogService is only injected in the ProviderImpl and the ProviderComposite1
class and not in the ProviderComposite2 class.
+
+###
org.apache.felix.dependencymanager.lambda.samples/src/org/apache/felix/dm/lambda/samples/device/
+
+This is an example showing a Dependency Manager "Adapter" in action. Two kinds
of services are
+registered in the registry: some Device, and some DeviceParameter services.
For each Device (having
+a given id), there is also a corresponding "DeviceParameter" service, having
the same id.
+
+Then a "DeviceAccessImpl" adapter service is defined: it is used to "adapt"
the "Device" service to
+a "DeviceAccess" service, which provides the union of each pair of
Device/DeviceParameter having the
+same device.id . The adapter also dynamically propagate the service properties
of the adapted Device
+service.
+
+###
org.apache.felix.dependencymanager.lambda.samples/src/org/apache/felix/dm/lambda/samples/dictionary/
+
+This sample shows a "SpellChecker" application which provides a
+"dictionary:spellcheck" GOGO shell command. The GOGO "dictionary:spellcheck"
command accepts a
+string as parameter, which is checked for proper exactness. The SpellChecker
class has a
+required/multiple (1..N) dependency over every available "DictionaryService"
services, which are
+internally used by the SpellChecker command, when checking word exactness.
+
+A DictionaryService is defined using a FactoryConfigurationAdapterService ,
allowing to instantiate
+many "DictionaryService" instances for each configuration that are added to the
+factory pid "Spell Checker Configuration (api)" from web console.
+The factory pid configuration metatypes are defined using the bnd "metatype"
annotations
+(see DictionaryConfiguration.java).
+
+The DictionaryService is decorated with a DictionaryAspect, which you can
instantiate by adding a
+configuration to the "Spell Checker Aspect Dictionary (api)" pid from web
console. The
+aspect configuration metatype is also declared using the bnd metatype
annotations (see
+DictionaryAspectConfiguration.java).
+
+Before running this sample, go to webconsole, and add some words in the Spell
Checker Configuration (api) factory PID, and
+in the Spell Checker Aspect Dictionary (api) PID.
+
+Then go to gogo shell, and type dm help. You will normally see the
dictionary:spellcheck command.
+Type dictionary:spellcheck with some words configured either in the spell
checker configuration, or in the spell checker aspect configuration,
+and the dictionary will check for proper word exactness in the configuration.
+
+###
org.apache.felix.dependencymanager.lambda.samples/src/org/apache/felix/dm/lambda/samples/factory/
+
+This sample is an example usage of DM components that are created using a
Factory object.
+The Factory is defined using java8 method references.
+
+###
org.apache.felix.dependencymanager.lambda.samples/src/org/apache/felix/dm/lambda/samples/future/
+
+The purpose of this sample is to show an example usage of the new
"CompletableFuture" dependency that has been
+added in the dm-lambda library. CompletableFuture java8 class provides
functional operations and promotes an asynchronous event-driven model.
+
+In such model, you can use the new dm-lambda library to add dependencies on
asynchronous events using the standard JDK CompletableFuture class.
+
+In this example, the Activator first defines a PageLink component that is used
to download a given page from the web. The service then parses
+the content of the page and returns all available hrefs (links) found from the
web page.
+
+The PageLink is initialized with the Felix web site URL, which is
asynchronously downloaded from the PageLink::init method, using a
CompletableFuture.
+The CF is then added as a "FutureDependency" in the PageLinkImpl.init()
method, and when the CF completes, the PageLinkImpl.start() callback is invoked
+and the service is registered.
+
+The Activator is then getting injected with the PageLink service, and displays
the links (hrefs) found from the Felix web site.
+
+Caution: if you are using a corporate http proxy, you have to fix the
Activator in order to configure the ip addr and port number of your
+http proxy.
+