Author: pderop
Date: Sun Feb 28 20:03:05 2016
New Revision: 1732786
URL: http://svn.apache.org/viewvc?rev=1732786&view=rev
Log:
updated dm-lambda doc.
Modified:
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/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=1732786&r1=1732785&r2=1732786&view=diff
==============================================================================
---
felix/site/trunk/content/documentation/subprojects/apache-felix-dependency-manager/guides/dm-lambda.mdtext
(original)
+++
felix/site/trunk/content/documentation/subprojects/apache-felix-dependency-manager/guides/dm-lambda.mdtext
Sun Feb 28 20:03:05 2016
@@ -4,8 +4,6 @@ Title: Dependency Manager Lambda
**Welcome to Felix Dependency Manager Lambda.**
-(TODO: update this document in order to explain that dependencies are optional
by default, and not required).
-
## Introduction
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
@@ -74,18 +72,16 @@ Now, let's rework the above example, usi
// Declare our Consumer component
component(comp -> comp.impl(ServiceConsumer.class)
- .withSvc(ServiceProvider.class, svc ->
svc.filter("(p1=v1)").add(ServiceConsumer::setProvider))
+ .withSvc(ServiceProvider.class, svc ->
svc.required().filter("(p1=v1)").add(ServiceConsumer::setProvider))
.withCnf(ServiceConsumer.class.getName()));
// Declare our ServiceProvider service component:
component(comp -> comp.impl(ServiceProviderImpl.class)
.provides(ServiceProvider.class, p1 -> "v1", p2 -> 123)
- .withSvc(LogService.class, EventAdmin.class));
+ .withSvc(true, LogService.class, EventAdmin.class));
}
-(all dependencies are required by default, if they are not explicitly declared
as optional or required).
-
# Principle
The new API is provided by the `org.apache.felix.dependencymanager.lambda.jar`
bundle. The following builders are currently supported:
@@ -119,7 +115,7 @@ for dm-lambda activators:
}
}
-The `component()` method returns a `ComponentBuilder` and the call to `build`
at the end of the call chain returns the actual DM Component object.
+The `component()` method returns a `ComponentBuilder` and the call to `build`
at the end of the method calls chain returns the actual DM Component object.
Here is a shorter version:
@@ -167,10 +163,20 @@ Here is a more concise version with less
}
}
+## Dependency default mode (required or optional ?)
+
+When you declare a dependency without explicitly invoking `optional()`,
`required()`, or `required(boolean)`, then by default,
+the dependency is assumed to be optional. This is in line with the behavior of
the Dependency Manager API.
+
+Now, you can change this default behavior by configuring the
"`org.apache.felix.dependencymanager.lambda.defaultRequiredDependency`"
+system property. This property can be set with a list of java package prefixes
(comma separated).
+When a component implementation class starts with one of the package prefixes
specified in the above property, then dependencies will be
+assumed to be required by default.
+
## Adding service dependencies injected in class fields.
You can add a dependency using the "`withSvc`" 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 ...):
+Such methods accept a `Consumer<ServiceDependencyBuilder>` lambda expression,
which may then configure the dependency using a chain of method calls
(filter/callbacks,autoconfig, etc ...):
When you don't specify callbacks, services are injected in class fields with
compatible service dependency type, but you can specify a field name.
Unavailable optional dependencies are injected as "`Null Objects`".
@@ -200,33 +206,35 @@ Here is a more concise version where the
}
}
-If you depend on multiple services (without filters), you can declare the
services in one shot like this
-(the dependencies will be injected in compatible class fields, by reflection):
+When injecting services in class fields (auto config mode), there are shotcuts
that avoid using a lambda when defining a service dependency.
+These shortcuts are available from the ComponentBuilder interface.
+
+Examples:
+
+#### Declaring multiple auto config dependencies in one shot (using varargs of
interfaces):
:::java
- import org.apache.felix.dm.lambda.DependencyManagerActivator;
+ component(comp -> comp.impl(Hello.class).withSvc(ConfigurationAdmin.class,
EventAdmin.class, MetatypeService.class));
- public class Activator extends DependencyManagerActivator {
- @Override
- public void init(BundleContext ctx, DependencyManager dm) throws
Exception {
- // using a varargs of service dependencies ...
- component(comp -> comp.impl(Hello.class).withSvc(LogService.class,
EventAdmin.class));
- }
- }
+#### Declaring multiple auto config dependencies in one shot with a `required`
flag:
-When you want to inject a service in a class field, but using a filter, you
can also define a one-liner
-dependency that is not needing a ServiceDependencyBuilder lambda:
+ :::java
+ component(comp -> comp.impl(Hello.class).withSvc(true,
ConfigurationAdmin.class, EventAdmin.class, MetatypeService.class));
+
+#### Declaring an autoconfig dependency with a `required` flag:
:::java
- import org.apache.felix.dm.lambda.DependencyManagerActivator;
+ component(comp -> comp.impl(Hello.class).withSvc(ConfigurationAdmin.class,
true));
- public class Activator extends DependencyManagerActivator {
- @Override
- public void init(BundleContext ctx, DependencyManager dm) throws
Exception {
- // using a varargs of service dependencies ...
- component(comp -> comp.impl(Hello.class).withSvc(LogService.class,
"(vendor=apache)"));
- }
- }
+#### Declaring an autoconfig dependency with a `filter ` and `required` flag:
+
+ :::java
+ component(comp -> comp.impl(Hello.class).withSvc(ConfigurationAdmin.class,
"(vendor=apache)", true));
+
+#### Declaring a autoconfig dependency with a `filter `, an explicit class
field, and `required` flag:
+
+ :::java
+ component(comp -> comp.impl(Hello.class).withSvc(ConfigurationAdmin.class,
"(vendor=apache)", "configadmin", true));
Dependency services can be injected in the following kind of fields:
@@ -413,28 +421,24 @@ In case a lookup does not yield a value
### multiple ways to define a configuration dependency
-You can first pass a configuration pid to the `withCnf` method:
+You can first pass a configuration pid to the `withCnf` method. In this
example, the Hello component has an "`updated(Dictionary properties)`" method
called when configuration is available or updated.
:::java
component(comp -> comp.impl(Hello.class).withCnf("my.pid"))
-The above example assumes that your Hello component has an
"`updated(Dictionary properties)`" method and will call it when configuration
is available or updated.
-
-You can pass a "`configuration type`" to the `withCnf` method:
+You can pass a "`configuration type`" to the `withCnf` method. The pid is
assumed to be the fqdn of the type passed to the `withCnf` method, and the
callback is assumed to be "`updated`"
+and to accept as argument an implementation of the specified configuration
type:
:::java
component(comp -> comp.impl(Hello.class).withCnf(MyConfiguration.class))
-In the above example, the pid is assumed to be the fqdn of the type passed to
the `withCnf` method, and the callback is assumed to be "`updated`"
-and to accept as argument an implementation of the specified configuration
type.
-
You can define the updated callback method explicitly using a
ConfigurationDependencyBuilder lambda that you can pass to the "`withCnf`"
method:
:::java
component(comp ->
comp.impl(Hello.class).withCnf((ConfigurationDependencyBuilder cnf) ->
cnf.pid("my.pid").update("modified")));
-shorter version which does not declare the type of the lambda passed to the
`withCnf` method:
+Here is shorter version which does not declare the type of the lambda passed
to the `withCnf` method:
:::java
component(comp -> comp.impl(Hello.class).withCnf(cnf ->
cnf.pid("my.pid").update("modified")));
@@ -444,7 +448,7 @@ You can also define the callback using a
:::java
component(comp -> comp.impl(Hello.class).withCnf(cnf ->
cnf.pid("my.pid").update(Hello::modified)));
-And finally, you can define a configuration type, and callback using a method
reference. Here, the updated callback has to take
+And finally, you can define a configuration type, and a callback using a
method reference. Here, the updated callback has to take
in argument the configuration type parameter (the pid is assumed to be the
fqdn of the configuration type):
:::java
@@ -595,6 +599,48 @@ And an example where you create a new DM
}
}
+## Component Lifecycle Callbacks
+
+Like with DM API, default lifecycle callbacks are the following:
+
+- "init": the method is called on the component implementation class(es) once
all required dependencies declared in the Activator
+have been injected. This method can then be used to possibly add more
dependencies dynamically.
+- "start": the method is called on the component implementation class(es) once
all required dependencies (including the ones added
+from the "init" callback) have been injected. Then the optional dependency
callbacks are invoked (after the start callback).
+- "stop": the method is called on the component implementation class(es) when
some required dependencies are being lost
+or when the component's bundle is stopping.
+- "destroy": the component is destroyed and may be re-created and
re-initialized in case some required dependencies comes up again.
+
+You can change the callback names using the "init"/"start"/"stop"/"destroy"
methods from the ComponentBuilder interface. For example:
+
+ :::java
+ component(comp -> comp.impl(Pojo.class)
+ .init("initialize")
+ .start("activate")
+ .stop("deactivate")
+ .destroy("shutdown"));
+
+Same example, but with some specific callback instance on which the callback
should be invoked:
+
+ CallbackHandler handler = new CallbackHandler();
+ component(comp -> comp.impl(Pojo.class)
+ .init(handler, "initialize")
+ .start(handler, "activate")
+ .stop(handler, "deactivate")
+ .destroy(handler, "shutdown"));
+
+When using callback instances, you can also use method references using the
callback instance object:
+
+ CallbackHandler handler = new CallbackHandler();
+ component(comp -> comp.impl(Pojo.class)
+ .init(handler::initialize)
+ .start(handler::activate)
+ .stop(handler::deactivate)
+ .destroy(handler::shutdown));
+
+Callbacks are empty-args, or may take a DM Component in argument.
+
+Method Reference for Component implementations class are not supported.
## Creating Aspect Components
Like with the original DM API, you can create a chain of aspects (service
interceptors) ordered by a ranking attribute, using the "`aspect`" factory
method.
@@ -635,7 +681,7 @@ Same example, but using callbacks for in
## Creating Service Adapter Components
-DM service adapters allows to create adapter services when a given type of
adapted service is found in the OSGI registry.
+DM service adapters allow to create adapter services when a given type of
adapted service is found in the OSGI registry.
Using the "`adapter`" factory method, you can pass to it consumer of an
`ServiceAdapterBuilder` that
can be used to construct a DM adapter component.
@@ -668,6 +714,17 @@ Example that defines a factory configura
:::java
public class Activator extends DependencyManagerActivator {
public void init(BundleContext ctx, DependencyManager dm) throws
Exception {
+ factoryPidAdapter((FactoryPidAdapterBuilder adapter) -> adapter
+
.impl(DictionaryImpl.class).factoryPid("foo.bar").propagate().update(ServiceImpl::updated)
+ .withSvc(LogService.class, log -> log.optional()));
+ }
+ }
+
+Same more concise example that is not declaring the type of the lambda type:
+
+ :::java
+ public class Activator extends DependencyManagerActivator {
+ public void init(BundleContext ctx, DependencyManager dm) throws
Exception {
factoryPidAdapter(adapter -> adapter
.impl(DictionaryImpl.class).factoryPid("foo.bar").propagate().update(ServiceImpl::updated)
.withSvc(LogService.class, log -> log.optional()));
@@ -719,8 +776,10 @@ track any "Tracked" services registered
download a web page at initialization, before you component is started. The
downloaded webpage is required to be able to
handle Tracked services. Now, you don't want to block the initialization of
your component because in a reactive word,
it is forbidden to block on the current thread.
-So, you use an HttpClient which allows to asynchronously download a web page,
and when you schedule doGET() on the
-client, the method returns to you a `CompletableFuture<String>`.
+
+So, you use an `HttpClient` which allows to asynchronously download a web
page: this service is assumed to provide a doGET() method
+which does not block the current thread, but instead returns
`CompletableFuture<String>`
+which represents the future result of the asynchronously downloaded page.
So, from your component init() method, you can just declare a FutureDependency
on the result of the `CompletableFuture<String>`
@@ -736,7 +795,9 @@ So, the Activator looks like this:
@Override
public void init(BundleContext ctx, DependencyManager dm) throws
Exception {
component(comp -> comp.impl(Pojo.class).provides(PojoService)
-
.withCnf("foo.pid").withSvc(HttpClient.class).withSvc(Tracked.class, svc ->
svc.optional().add(Pojo::bindTracked));
+ .withCnf(cnf -> cnf.pid("foo.pid"))
+ .withSvc(HttpClient.class, svc -> svc.required())
+ .withSvc(Tracked.class, svc ->
svc.optional().add(Pojo::bindTracked));
}
}