Author: pderop
Date: Sat Feb 20 21:53:18 2016
New Revision: 1731482
URL: http://svn.apache.org/viewvc?rev=1731482&view=rev
Log:
Updated documentation.
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=1731482&r1=1731481&r2=1731482&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
Sat Feb 20 21:53:18 2016
@@ -2,29 +2,99 @@ Title: Dependency Manager Lambda
----------
+**Welcome to Felix 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.
-
-(Please notice that using the dm-lambda library requires the usage of a recent
Java8 jvm (the library has been tested with java version "1.8.0_71").
+## Introduction
-## Principle
+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 new style based on java8 lambda expressions and other goodies like
method references.
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 avoids the call to the last "`build`"
method and adds the constructed DM Component into the
-DependencyManager object automatically.
+We'll see later that using lambdas avoids to call the last "`build`" method
and allows to automatically add the constructed DM Component into the
+DependencyManager object.
+
+Please notice that using the dm-lambda library requires the usage of a recent
Java8 jvm (the library has been tested with java version "1.8.0_71").
+
+## Comparing two activators using old and new API:
+
+Before presenting the new API, let's get a jump start and dive into a
comparison between the old and new API: assume we have a `ServiceConsumer`
which depends on the following services:
+
+- a required dependency on `ServiceProvider` service with a "`(p1=v1)`"
filter. The dependency is injected in the "`ServiceConsumer.setProvider()`"
method.
+- 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(ServiceConsumer.class.getName()));
+ 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 init(BundleContext ctx, DependencyManager dm) throws
Exception {
+ // Declare our Consumer component
+
+ component(comp -> comp.impl(ServiceConsumer.class)
+ .withSvc(ServiceProvider.class, srv ->
srv.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));
+ }
+
+(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:
+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.
+- ComponentBuilder: it is used to build org.apache.felix.dm.Component from
original DM API.
+- ServiceDependencyBuilder: builds org.apache.felix.dm.ServiceDependency from
original DM API.
+- ConfigurationDependencyBuiler: builds
org.apache.felix.dm.ConfigurationDependency from original DM API.
+- BundleAdapterBuilder: builds a bundle adapter component from the original DM
API.
+- ServiceAdapterBuilder.java: builds a DM service adapter from the original DM
API.
+- FactoryPidAdapterBuilder: builds a DM factory pid adapter from the original
DM API.
+- 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).
@@ -40,10 +110,9 @@ for dm-lambda activators:
public class Activator extends DependencyManagerActivator {
@Override
- public void activate() throws Exception {
+ public void init(BundleContext ctx, DependencyManager dm) throws
Exception {
ComponentBuilder builder = component();
Component comp = builder.impl(Hello.class).build();
- DependencyManager dm = getDM();
dm.add(comp);
}
}
@@ -58,9 +127,9 @@ Here is a shorter version:
public class Activator extends DependencyManagerActivator {
@Override
- public void activate() throws Exception {
+ public void init(BundleContext ctx, DependencyManager dm) throws
Exception {
Component comp = component().impl(Hello.class).build());
- getDM().add(comp);
+ dm.add(comp);
}
}
@@ -79,30 +148,26 @@ The following is the same as above, usin
public class Activator extends DependencyManagerActivator {
@Override
- public void activate() throws Exception {
+ 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:
+And to reduce the "code ceremony", here is a more concise version where the
type of the lambda parameter is not declared:
:::java
import org.apache.felix.dm.lambda.DependencyManagerActivator;
public class Activator extends DependencyManagerActivator {
@Override
- public void activate() throws Exception {
+ public void init(BundleContext ctx, DependencyManager dm) throws
Exception {
component(comp -> comp.impl(Hello.class));
}
}
-## Javadoc
-
-You can find the javadoc for the new Dependency Manager Lambda library
[here](../../../../apidocs/).
-
## 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.
+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 ...):
:::java
@@ -111,9 +176,9 @@ Such method accepts a `Consumer<ServiceD
public class Activator extends DependencyManagerActivator {
@Override
- public void activate() throws Exception {
+ public void init(BundleContext ctx, DependencyManager dm) throws
Exception {
component(comp -> comp.impl(Hello.class)
- .withSrv(LogService.class, (ServiceDependencyBuilder srv) ->
srv.filter("(vendor=apache)")));
+ .withSvc(LogService.class, (ServiceDependencyBuilder srv) ->
srv.filter("(vendor=apache)")));
}
}
@@ -124,64 +189,53 @@ The above example adds a service depende
public class Activator extends DependencyManagerActivator {
@Override
- public void activate() throws Exception {
- component(comp -> comp.impl(Hello.class).withSrv(LogService.class,
srv -> srv.filter("(vendor=apache)")));
+ public void init(BundleContext ctx, DependencyManager dm) throws
Exception {
+ component(comp -> comp.impl(Hello.class).withSvc(LogService.class,
srv -> srv.filter("(vendor=apache)")));
}
}
-If you depend on multiple required services (without filters), you can declare
the services in one shot like this:
+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):
:::java
import org.apache.felix.dm.lambda.DependencyManagerActivator;
public class Activator extends DependencyManagerActivator {
@Override
- public void activate() throws Exception {
+ 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));
+ component(comp -> comp.impl(Hello.class).withSvc(LogService.class,
EventAdmin.class));
}
}
-## Service Dependency Component 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:
+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
import org.apache.felix.dm.lambda.DependencyManagerActivator;
public class Activator extends DependencyManagerActivator {
@Override
- public void activate() throws Exception {
- component(comp -> comp.impl(Hello.class).withSrv(LogService.class,
srv -> srv.cb("setLog")));
+ 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)"));
}
}
-The `cb` method accepts a varargs of strings (up to 4 method names):
+## Service Dependency Component callbacks
-1. when using one argument, it is used as the `add` callback.
-1. when using two arguments, the first arg is used as the `add` callback, and
the second one as the `remove` callback.
-1. when using three arguments, the first arg 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 first arg 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.
+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 "`add/change/remove/swap`"
`ServiceDependencyBuilder` methods:
-The add/change/remove callbacks accept the following kind of method signatures
("S" represents the type of the service dependency):
+ :::java
+ import org.apache.felix.dm.lambda.DependencyManagerActivator;
- 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)
+ public class Activator extends DependencyManagerActivator {
+ @Override
+ public void init(BundleContext ctx, DependencyManager dm) throws
Exception {
+ component(comp -> comp.impl(Hello.class).withSvc(LogService.class,
srv -> srv.add("setLog")));
+ }
+ }
Now you can also use a more type-safe callback using a Java 8 method reference:
@@ -190,8 +244,8 @@ Now you can also use a more type-safe ca
public class Activator extends DependencyManagerActivator {
@Override
- public void activate() throws Exception {
- component(comp -> comp.impl(Hello.class).withSrv(LogService.class,
srv -> srv.cb(Hello::setLog)));
+ public void init(BundleContext ctx, DependencyManager dm) throws
Exception {
+ component(comp -> comp.impl(Hello.class).withSvc(LogService.class,
srv -> srv.add(Hello::setLog)));
}
}
@@ -202,16 +256,34 @@ or:
public class Activator extends DependencyManagerActivator {
@Override
- public void activate() throws Exception {
- component(comp -> comp.impl(Hello.class).withSrv(LogService.class,
srv -> srv.cb(Hello::setLog, Hello::unsetLog)));
+ public void init(BundleContext ctx, DependencyManager dm) throws
Exception {
+ component(comp -> comp.impl(Hello.class).withSvc(LogService.class,
srv -> srv.add(Hello::setLog).remove(Hello::unsetLog)));
}
}
-## Defining Service Dependency Object instance callback
+The following callback methods signatures are supported when using method
references:
+
+For add/change/remove method references:
-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`").
+ :::java
+ method(S service)
+ method(S service, ServiceReference<S> serviceRef),
+ method(S service, Map<String, Object> serviceProperties)
+ method(S service, Dictionary<String, Object> serviceProperties)
+ method(S service, Component serviceComponent)
+ method(S service, Component serviceComponent, ServiceReference<S>
serviceRef)
+
+and for swap method references:
+
+ :::java
+ method(S oldService, S newService)
+ method(S oldService, S newService, Component component))
+ method(ServiceReference<S> oldRef, S old, ServiceReference<S> newRef, S
newService)
+ method(ServiceReference<S> oldRef, S old, ServiceReference<S> newRef, S
newService, Component component)
+
+## Defining Service Dependency Object instance callback
+Sometimes, you want to inject the dependency to a separate object that is not
part of the component implementation classes.
For example, the following example injects a dependency in a DependencyHandler
instance:
:::java
@@ -219,9 +291,9 @@ For example, the following example injec
public class Activator extends DependencyManagerActivator {
@Override
- public void activate() throws Exception {
+ 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")));
+ component(comp -> comp.impl(Hello.class).withSvc(LogService.class,
srv -> srv.add(depHandler, "setLog")));
}
}
@@ -232,9 +304,9 @@ or using method reference:
public class Activator extends DependencyManagerActivator {
@Override
- public void activate() throws Exception {
+ 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)));
+ component(comp -> comp.impl(Hello.class).withSvc(LogService.class,
srv -> srv.add(depHandler::setLog)));
}
}
@@ -245,9 +317,9 @@ You can chain multiple callbacks:
public class Activator extends DependencyManagerActivator {
@Override
- public void activate() throws Exception {
+ 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)));
+ component(comp -> comp.impl(Hello.class).withSvc(LogService.class,
srv -> srv.add(Hello::setLog).add(depHandler::setLog)));
}
}
@@ -262,12 +334,12 @@ Now you can pass properties directly to
public class Activator extends DependencyManagerActivator {
@Override
- public void activate() throws Exception {
+ 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 `-parameters` option, you can also use
the "`FluentProperty`" lambda that allows to declare
+or if you build your application using the `-parameters` javac option, you can
also use the "`FluentProperty`" lambda that allows to declare
service properties as a suite of "`key -> value`" lambdas, like this:
:::java
@@ -275,16 +347,114 @@ service properties as a suite of "`key -
public class Activator extends DependencyManagerActivator {
@Override
- public void activate() throws Exception {
+ public void init(BundleContext ctx, DependencyManager dm) throws
Exception {
component(comp ->
comp.impl(Hello.class).provides(HelloService.class, p1 -> "v1", p2 -> 123));
}
}
+## Depending on a configuration.
+
+Configuration dependency can be defined using the "`withCnf`" ComponentBuilder
method.
+
+Two families of callbacks are supported:
+
+- reflection based callbacks: you specify a callback method name
+- method reference callbacks: you specify a java8 method reference
+
+Callbacks may accept a Dictionary, a Component, or a user defined
configuration type interface. If you only specify a pid, by default the
callback method name is assumed to be "updated".
+
+Configuration types are a new feature that allows you to specify an interface
that is implemented by DM and such interface is then injected to your callback
instead of the actual Dictionary. Using such configuration interface provides a
way for creating type-safe configurations from a actual Dictionary that is
normally injected by Dependency Manager. The callback accepts in argument an
interface that you have to provide, and DM will inject a proxy that converts
method calls from your configuration-type to lookups in the actual map or
dictionary. The results of these lookups are then converted to the expected
return type of the invoked configuration method.
+As proxies are injected, no implementations of the desired configuration-type
are necessary!
+
+The lookups performed are based on the name of the method called on the
configuration type. The method names are "mangled" to the following form:
[lower case letter] [any valid character]*. Method names starting with get or
is (JavaBean convention) are stripped from these prefixes. For example: given a
dictionary with the key "foo" can be accessed from a configuration-type using
the following method names: foo(), getFoo() and isFoo().
+
+The return values supported are: primitive types (or their object wrappers),
strings, enums, arrays of primitives/strings, Collection types, Map types,
Classes and interfaces. When an interface is returned, it is treated equally to
a configuration type, that is, it is returned as a proxy.
+
+Arrays can be represented either as comma-separated values, optionally
enclosed in square brackets. For example: [ a, b, c ] and a, b,c are both
considered an array of length 3 with the values "a", "b" and "c".
Alternatively, you can append the array index to the key in the dictionary to
obtain the same: a dictionary with "arr.0" => "a", "arr.1" => "b", "arr.2" =>
"c" would result in the same array as the earlier examples.
+
+Maps can be represented as single string values similarly as arrays, each
value consisting of both the key and value separated by a dot. Optionally, the
value can be enclosed in curly brackets. Similar to array, you can use the same
dot notation using the keys. For example, a dictionary with
+
+ "map" => "{key1.value1, key2.value2}"
+
+and a dictionary with
+
+ "map.key1" => "value1", "map2.key2" => "value2"
+
+result in the same map being returned. Instead of a map, you could also define
an interface with the methods getKey1() and getKey2 and use that interface as
return type instead of a Map.
+
+In case a lookup does not yield a value from the underlying map or dictionary,
the following rules are applied:
+
+- primitive types yield their default value, as defined by the Java
Specification;
+- string, Classes and enum values yield null;
+- for arrays, collections and maps, an empty array/collection/map is returned;
+- for other interface types that are treated as configuration type a
null-object is returned.
+
+Sample codes:
+
+Code example with a component that defines a Configuration Dependency using a
specific callback method reference, and the method accepts in argument a
configuration type (the pid is assumed to be the fqdn of the configuration
type):
+
+ :::java
+ public interface MyConfig {
+ String getAddress();
+ int getPort();
+ }
+
+ public class ServiceImpl {
+ void modified(MyConfig cnf) {
+ if (cnf != null) {
+ String addr = cnf.getAddress();
+ int port = cnf.getPort();
+ ...
+ }
+ }
+ }
+
+ public class Activator extends DependencyManagerActivator {
+ public void init(BundleContext ctx, DependencyManager dm) throws
Exception {
+ component(comp -> comp.impl(ServiceImpl.class).withCnf(conf ->
conf.update(MyConfig.class, ServiceImpl::modified)));
+ }
+ }
+
+
+Code example with a component that defines a Configuration Dependency using a
specific callback method reference which accepts a Dictionary in argument:
+
+ :::java
+ public class Activator extends DependencyManagerActivator {
+ public void init(BundleContext ctx, DependencyManager dm) throws
Exception {
+ component(comp -> comp
+ .impl(ServiceImpl.class)
+ .withCnf(conf ->
conf.pid("my.pid").update(ServiceImpl::modified)));
+ }
+ }
+
+
+Code example which defines a configuration dependency injected in the
"ServiceImpl.updated(Dictionary)" callback:
+
+
+ :::java
+ public class Activator extends DependencyManagerActivator {
+ public void init(BundleContext ctx, DependencyManager dm) throws
Exception {
+ component(comp -> comp.impl(ServiceImpl.class).withCnf("my.pid"));
+ }
+ }
+
+
+Code example with a component that defines a Configuration Dependency using a
specific callback method name:
+
+
+ :::java
+ public class Activator extends DependencyManagerActivator {
+ public void init(BundleContext ctx, DependencyManager dm) throws
Exception {
+ component(comp -> comp.impl(ServiceImpl.class).withCnf(conf ->
conf.pid("my.pid").update("modified")));
+ }
+ }
+
+
## Managing components outside of Activators.
You can manage Components outside of the Activator by using some static
factory methods from the `DependencyManagerActivator` class.
-For example, consider 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
+For example, consider a use case where you want to retrieve some information
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
@@ -292,7 +462,7 @@ For example, consider a use case where y
public class Activator extends DependencyManagerActivator {
@Override
- public void activate() throws Exception {
+ public void init(BundleContext ctx, DependencyManager dm) throws
Exception {
component(comp -> comp.impl(Pojo.class).withCnf("pojo.pid"));
}
}
@@ -311,7 +481,7 @@ what it has parsed, it will possibly add
void init(Component c) { // lifecycle dm callback that allow you to
add more dependencies
if (xmlConfigurationRequiresEventAdmin) {
- component(c, comp -> comp.withSrv(EventAdmin.class));
+ component(c, comp -> comp.withSvc(EventAdmin.class));
}
}
}
@@ -348,10 +518,94 @@ And an example where you create a new DM
volatile DependencyManager m_dm;
void createComponent() {
- component(m_dm, comp ->
comp.impl(NewComponent.class).withSrv(LogService.Class, EventAdmin.class));
+ component(m_dm, comp ->
comp.impl(NewComponent.class).withSvc(LogService.Class, EventAdmin.class));
+ }
+ }
+
+## Creating Aspect Components
+
+Like with the original DM API, you can create aspects (service interceptors),
using the "`aspect`" factory method.
+This method accepts in argument a ServiceAspectBuilder.
+
+Code example which provides a "LogService" aspect that performs spell-checking
of each log message. The aspect decorates a LogService.
+The aspect also depends on an Dictionary service that is internally used to
perform log spell checking.
+The LogService and Dictionary services are injected in the aspect
implementation using reflection on class
+fields:
+
+ ::::java
+ public class Activator extends DependencyManagerActivator {
+ public void init(BundleContext ctx, DependencyManager dm) throws
Exception {
+ aspect(LogService.class, (ServiceAspectBuilder asp) ->
asp.impl(SpellCheckLogAspect.class).rank(10).withSvc(Dictionary.class));
+ }
+ }
+
+Same more concise example which does not declare the type of the lambda
builder argument:
+
+ ::::java
+ public class Activator extends DependencyManagerActivator {
+ public void init(BundleContext ctx, DependencyManager dm) throws
Exception {
+ aspect(LogService.class, asp ->
asp.impl(SpellCheckLogAspect.class).rank(10).withSvc(Dictionary.class));
+ }
+ }
+
+Same example, but using callbacks for injecting LogService and Dictionary
services in the aspect implementation class:
+
+ :::java
+ public class Activator extends DependencyManagerActivator {
+ public void init(BundleContext ctx, DependencyManager dm) throws
Exception {
+ aspect(LogService.class, asp -> asp
+ .impl(SpellCheckLogAspect.class).rank(10)
+ .add(SpellCheckLogAspect::setLogService)
+ .withSvc(Dictionary.class, svc ->
svc.add(SpellCheckLogAspect::setDictionary)));
+ }
+ }
+
+## 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.
+Using the "`adapter`" factory method, you can pass to it consumer of an
`ServiceAdapterBuilder` that
+can be used to construct a DM adapter component.
+
+Code example that adapts a "Device" service to an HttpServlet service. The
adapter is created using a ServiceAdapterBuilder that is passed to the lambda.
+
+ :::java
+ public class Activator extends DependencyManagerActivator {
+ public void init(BundleContext ctx, DependencyManager dm) throws
Exception {
+ adapter(Device.class, (ServiceAdapterBuilder adapt) ->
adapt.impl(DeviceServlet.class).provides(HttpServlet.class).properties(alias ->
"/device");
+ }
+ }
+
+Same more concise example which does not declare the type of lambda parameter:
+
+ :::java
+ public class Activator extends DependencyManagerActivator {
+ public void init(BundleContext ctx, DependencyManager dm) throws
Exception {
+ adapter(Device.class, adapt ->
adapt.impl(DeviceServlet.class).provides(HttpServlet.class).properties(alias ->
"/device");
}
}
+## Creating a Bundle Adapter component
+
+A Bundle Adapter is used to create a Component when a bundle that matches a
given filter is found.
+To build a DM adapter, you can use the "`bundleAdapter`" factory method: it
takes in argument a consumer of a
+BundleAdapterBuilder object, which is used to construct a real DM
BundleAdapter component.
+
+Example that creates a BundleAdapter service for each started bundle (the
bundle is added using a method reference):
+
+ :::java
+ public class Activator extends DependencyManagerActivator {
+ public void init(BundleContext ctx, DependencyManager dm) throws
Exception {
+ bundleAdapter(adapt -> adapt
+ .impl(BundleAdapterImpl.class)
+ .provides(BundleAdapter.class)
+ .mask(Bundle.INSTALLED | Bundle.RESOLVED | Bundle.ACTIVE)
+ .add(BundleAdapterImpl::bundleStarted)
+ .withSvc(LogService.class, "(vendor=apache)")
+ .withSvc(EventAdmin.class, ConfigurationAdmin.class));
+ }
+ }
+
+Notice that the adapter also depends on three services (LogService,
EventAdmin, and ConfigurationAdmin services).
## CompletableFuture dependency.
@@ -379,10 +633,9 @@ So, the Activator looks like this:
public class Activator extends DependencyManagerActivator {
@Override
- public void activate() throws Exception {
+ public void init(BundleContext ctx, DependencyManager dm) throws
Exception {
component(comp -> comp.impl(Pojo.class).provides(PojoService)
- .withCnf("foo.pid").withSrv(HttpClient.class)
- .withSrv(Tracked.class, srv ->
srv.optional().cb(Pojo::bindTracked));
+
.withCnf("foo.pid").withSvc(HttpClient.class).withSvc(Tracked.class, srv ->
srv.optional().add(Pojo::bindTracked));
}
}
@@ -409,7 +662,7 @@ injected in the "bindTracked" method:
CompletableFuture<String> futurePage = m_httpClient.doGET(m_url);
// Add a required dependency to the result of the CF, and inject
the result in our setPage method.
- component(c, comp -> comp.withFuture(futurePage, future ->
future.cbi(this::setPage)));
+ component(c, comp -> comp.withFuture(futurePage, future ->
future.complete(this::setPage)));
}
void setPage(String content) {
@@ -435,81 +688,6 @@ Also, notice that when the page is injec
synchronization at all because in DM, all lifecycle and dependency callbacks
are safely scheduled in a "serial queue" associated to the
component.
-## Another Example with CompletableFuture and RxJava.
-
-We just introduced the new FutureDependency. Allowing to use a
CompletableFuture as a dependency is useful because CF is a powerful abstraction
-that allows to wrap any asynchronous events or async libraries with a standard
jdk tool (CompletableFuture).
-In this section, we present a way to "wait for" RxJava "Observables" using DM.
-
-RxJava, like the CompletableFuture java8 tool, allows to react on events, but
using non blocking push paradigm.
-
-TODO: finish this section.
-
-## 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:
@@ -592,3 +770,7 @@ The Activator is then getting injected w
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.
+## Javadoc
+
+You can find the javadoc for the new Dependency Manager Lambda library
[here](../../../../apidocs/).
+