You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@aries.apache.org by ro...@apache.org on 2019/04/08 01:43:38 UTC

[aries-cdi] branch master updated: [CDI] some documentation

This is an automated email from the ASF dual-hosted git repository.

rotty3000 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/aries-cdi.git


The following commit(s) were added to refs/heads/master by this push:
     new db41a5e  [CDI] some documentation
db41a5e is described below

commit db41a5e5b9b3da19212aa481f931c23a6edcabad
Author: Raymond Auge <ra...@liferay.com>
AuthorDate: Sun Apr 7 12:29:35 2019 -0400

    [CDI] some documentation
    
    Signed-off-by: Raymond Auge <ra...@liferay.com>
---
 README.md   | 104 +++++++++++++++--
 examples.md | 376 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 473 insertions(+), 7 deletions(-)

diff --git a/README.md b/README.md
index fa94c4b..d411d32 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
 # Aries CDI Integration
 
-This is an implementation of [CDI Integration Specification ](https://osgi.org/specification/osgi.enterprise/7.0.0/service.cdi.html).
+This is an implementation of [OSGi CDI Integration Specification ](https://osgi.org/specification/osgi.enterprise/7.0.0/service.cdi.html) (hereafter referred to simply as _OSGi CDI_).
 
 ## License
 
@@ -14,29 +14,33 @@ The build uses maven so it should look pretty familiar to most developers.
 
 ## Depedencies
 
-The main artifact is the CCR (CDI Component Runtime) implementation, or _extender_ bundle:
+The main artifact is the __CDI Component Runtime__ (__CCR__) implementation. a.k.a. the _extender_ bundle:
 
-```
+```xml
 <dependency>
   <groupId>org.apache.aries.cdi</groupId>
   <artifactId>org.apache.aries.cdi.extender</artifactId>
-  <version>1.0.1</version>
+  <version>${aries-cdi.version}</version>
   <scope>runtime</scope>
 </dependency>
 ```
 
-However all the required dependencies are available using the Aries CDI BOM:
+However all the required dependencies are available using the __Aries CDI BOM__:
 
-```
+```xml
 <dependency>
     <groupId>org.apache.aries.cdi</groupId>
     <artifactId>org.apache.aries.cdi.bom</artifactId>
-    <version>1.0.1</version>
+    <version>${aries-cdi.version}</version>
     <type>pom</type>
     <scope>import</scope>
 </dependency>
 ```
 
+## Tooling
+
+TODO
+
 ## Pre-built runtime
 
 This repository provides an example for how to assemble an executable jar providing a complete runtime for you to just drop in your CDI bundles. It comes complete with logging, Gogo shell, Config Admin, Http Whiteboard support, and OSGi Promises.
@@ -46,3 +50,89 @@ Once you've completed a successfull build, you should be able to execute the com
 `java -jar cdi-executable/target/executable.jar`
 
 and be presented with a gogo shell prompt ready for you to install a CDI bundle.
+
+## Architecture Overview
+
+The goal of OSGi CDI was to remain as true to both technologies as possible. This proved possible due to the extensive feature set provided by each technology.
+
+### Who cares! Examples please
+
+[Examples](examples.md)
+
+### Actors
+
+The main actors in the OSGi CDI architecture are:
+
+* __CDI bundle__ - bundles which contain CDI beans __and__ opted-in to OSGi CDI (best achieved with supporting build [tooling](#tooling).)
+
+* __CDI container__ - an instance of the CDI machinery hosting all beans inside a bundle and managing their instantiation.
+
+* __CDI Component Runtime__ (__CCR__) - is what __Aries CDI__ implements using [the extender pattern](https://enroute.osgi.org/FAQ/400-patterns.html#extender-pattern). It awaits CDI bundles creating a _private_ CDI container for each one.
+
+* __OSGi CDI Components__ (hereafter referred to simple as _components_) - A set of closely related CDI beans having a common _OSGi lifecycle_. A CDI bundle has __1 to N__ _components_. Again, all beans within the same component have a common OSGi lifecycle within the CDI bundle. The collective dependencies declared by all bean making up a component are treated as a single set. As such any single unsatisfied dependency of the component will prevent the entire component from activating, o [...]
+
+* __OSGi CDI Portable Extension__ (hereafter referred to simply as _portable extensions_) - bundles which contain portable extensions __and__ opted-in to providing those extensions in a OSGi CDI compatible way.
+
+* __Service Registry__ - The OSGi service registry is the central actor by which all inter bundle service activity is managed. As such, CDI bundles interact with other bundles via the service registry as well.
+
+    > _The nice thing is you can mix and match through the lingua franca of services. A bundle that is internally implemented with DS can talk to a bundle that is internally implemented with CDI (or Blueprint, etc...)_ [Neil Bartlett - Twitter](https://twitter.com/nbartlett/status/1114148717911859202)
+
+* __Configuration Admin__ - OSGi CDI is well integrated with Configuration Admin the way that __Declarative Services__ (__DS__) is. As such, __all__ _components_ in CDI bundles are configurable via configuration admin.
+
+### How a CDI bundle is created
+
+When a CDI bundle is identified by CCR several steps are taken before any bean is instantiated:
+
+1. Any _portable extensions_ identified by the bundle must be discovered and their associated `javax.enterprise.inject.spi.Extension` services must be located. The bundle's CDI container will remain inactive until all portable extension services are located. Conversely, for a bundle with an active CDI container, if an identified extension goes away the CDI container is torn down.
+2. The beans of the bundle are analysed and categorised into 3 classifications:
+   1. __Container component__:
+      - All beans you would traditionally find in a CDI application; `ApplicationScoped`, `Dependent`, `RequestScoped`, `SessionScoped`, `ConversationScoped`, any custom scopes, etc.; all of these make up the _container component_. 
+      - In fact, all beans that are not specifically `org.osgi.service.cdi.annotations.ComponentScoped` are part of the _container component_.
+      - __Every__ CDI bundle has exactly __1__ _container component_. 
+      - It is perfectly valid for the set of _container component_ beans to be __empty__.
+   2. __Single component__: 
+      - All beans using the _stereotype_ `@SingleComponent` are roots of _a single component_.
+      - Any referred beans (via injection points) that are explicitly scoped `@ComponentScoped` are also part of this _single component_.
+      - Each _single component_ in a bundle has an __independent__ OSGi lifecycle, with one restriction; the _container component_ __must__ be active.
+      - if the _container component_ becomes unresolved, active _single components_ are deactivated.
+      - A bundle may have __0 to N__ _single components_.
+      - _Single components_ are directly analogous to DS components that __are not__ flipped to factory mode.
+   3. __Factory component__:
+      - All beans using the stereotype `@FactoryComponent` are roots of a _factory component_.
+      - Any referred beans (via injection points) that are explicitly scoped `@ComponentScoped` are also part of this _factory component_.
+      - Each _factory component_ in a bundle has an __independent__ OSGi lifecycle, with one restriction; the _container component_ __must__ be active.
+      - if the _container component_ becomes unresolved, active _factory components_ are deactivated.
+      - A bundle may have __0 to N__ _factory components_.
+      - _Factory components_ are directly analogous to DS components that __are__ flipped to factory mode.
+      - _Factory components_ are dependent on factory configuration instances.
+3. The bundle's CDI container remains inactive while there remain unsatisfied dependencies of the _container component_. These may be services or configurations.
+4. Once the ___container component___  is resolved:
+   1. CDI container is created and activated.
+   2. the application scope is activated (_if there are any such beans in the container component_.)
+   3. services provided by the _container component_ are published to the service registry (_if any_.)
+   4. The `javax.enterprise.inject.spi.BeanManager` of the CDI container is published as a service with the service property `osgi.cdi.container.id`. (_always, even if the container component is empty_.)
+   5. _single components_ and _factory components_ remain inactive while there remain unsatisfied dependencies. These may be services or configurations.
+   6. Once a ___single component___  is resolved:
+      1. it becomes active; the exact nature of which is determined by whether the component provides a service or not, and what the service scope is.
+         - if the component __does not provide a service__, the component is simply instantiated.
+         - if the component __provides a singleton scoped service__, the component is instantiated and published into the registry (wrapped in a `ServiceFactory` like DS component services)
+         - if the component __provides a bundle scoped service__, the component is published into the registry (wrapped in a `ServiceFactory`). Service instances are created whenever the `getService` method of the factory is called, and destroyed when the `ungetService` is called. __Note:__ The service registry is the one tracking if a bundle has already _gotten_ factory service instances.
+         - if the component __provides a prototype scoped service__, the component is published into the registry (wrapped in a `PrototypeServiceFactory`). Service instances are created whenever the `getService` method of the factory is called, and destroyed when the `ungetService` is called.
+      2. if any required dependency of the component goes away, any service registration is removed from the registry and all instances are destroy.
+      3. Note that CDI context events whose payload is the component instance are fired at the appropriate moment for each of: 
+         - `@Initialized(ComponentScoped.class)`
+         - `@BeforeDestroy(ComponentScoped.class)`
+         - `@Destroyed(ComponentScoped.class)`
+   7. Once a ___factory component___  is resolved (_when a factory configuration instance is created in addition to all other service & configuration dependencies_):
+      1. an instance becomes active; the exact nature of which is determined by whether the component provides a service or not, and what the service scope is.
+         - if the component __does not provide a service__, the component is simply instantiated.
+         - if the component __provides a singleton scoped service__, the component is instantiated and published into the registry (wrapped in a `ServiceFactory` like DS component services)
+         - if the component __provides a bundle scoped service__, the component is published into the registry (wrapped in a `ServiceFactory`). Service instances are created whenever the `getService` method of the factory is called, and destroyed when the `ungetService` is called. __Note:__ The service registry is the one tracking if a bundle has already _gotten_ factory service instances.
+         - if the component __provides a prototype scoped service__, the component is published into the registry (wrapped in a `PrototypeServiceFactory`). Service instances are created whenever the `getService` method of the factory is called, and destroyed when the `ungetService` is called.
+      2. if any required dependency of the component goes away, any service registration is removed from the registry and all instances are destroy.
+      3. Note that CDI context events whose payload is the component instance are fired at the appropriate moment for each of: 
+         - `@Initialized(ComponentScoped.class)`
+         - `@BeforeDestroy(ComponentScoped.class)`
+         - `@Destroyed(ComponentScoped.class)`
+
+Time to move onto [the examples](examples.md).
\ No newline at end of file
diff --git a/examples.md b/examples.md
new file mode 100644
index 0000000..6b907af
--- /dev/null
+++ b/examples.md
@@ -0,0 +1,376 @@
+# OSGi CDI Examples as an FAQ
+
+## Registering Services
+
+### How do I register a bean as a service?
+
+The simplest service is a bean annotated with `@Service`.
+
+```java
+package com.acme;
+
+@Service
+class Foo {
+}
+```
+
+### Under which service types are my `@Service` beans published?
+
+Unless otherwise specified, `@Service` beans are published as follows:
+
+1. if the bean _does not implement any interfaces_, the service type is __the bean type__.
+2. if the bean _implements any interfaces_, the service types are __all implemented interfaces__.
+
+The service above is registered with the service type `com.acme.Foo`.
+
+```java
+import com.acme.Bar;
+import com.acme.Baz;
+
+@Service
+class Foo implements Bar, Baz {
+}
+```
+
+The service above is registered with the service types `com.acme.Bar` and `com.acme.Baz`.
+
+### How do I specify service types for my `@Service` beans?
+
+There are several ways to specify which service types `@Service` beans are published.
+
+1. One or more types my be specified by applying the `@Service` annotation to type use clauses.
+2. The `@Service` annotation on the bean type may specify a set of types as an argument of the annotation.
+
+```java
+import com.acme.Bar;
+import com.acme.Baz;
+import com.acme.Fum;
+
+class Foo extends @Service Fum implements @Service Bar, Baz {
+}
+```
+
+The above example specifies that the bean should be published with service types `com.acme.Fum` and `com.acme.Bar`, but NOT `com.acme.Baz`.
+
+```java
+import com.acme.Bar;
+import com.acme.Baz;
+import com.acme.Fum;
+
+@Service({Fum.class, Bar.class})
+class Foo extends Fum implements Bar, Baz {
+}
+```
+
+This example produces the same result as the previous example.
+
+### At what `service.scope` will my `@Service` beans is registered?
+
+Unless otherwise specified, `@Service` beans have __`singleton`__ `service.scope`.
+
+The example in the previous section produces a service at __`singleton`__ scope.
+
+### How do I specify a `service.scope` on my `@Service` beans?
+
+The annotation `@ServiceInstance` is used to specify the `service.scope`.
+
+```java
+@Service
+@ServiceInstance(PROTOTYPE)
+class Foo {
+}
+```
+
+Available values are `SINGLETON`, `BUNDLE`, and `PROTOTYPE`.
+
+### Are there any limitations as to which beans can become services?
+
+Beans scoped `@ApplicationScoped` or `@Dependent`, as well as beans having the stereotype `@SingleComponent` or `@FactoryComponent` can be annotated with `@Service` and become services.
+
+```java
+@Service     // Valid! @Dependent & singleton service
+class Foo {
+}
+
+@Service     // Valid! @ApplicationScoped & singleton service
+@ApplicationScoped
+class Foo {
+}
+
+@Service     // Valid! @ComponentScoped & prototype service
+@ServiceInstance(PROTOTYPE)
+@SingleComponent
+class Foo {
+}
+
+@Service     // ERROR! Only @ApplicationScoped, @Dependent, @SingleComponent & @FactoryComponent can be services.
+@SessionScoped
+class Foo {
+}
+
+@Service     // ERROR! @ApplicationScoped can only have singleton scope.
+@ServiceInstance(PROTOTYPE)
+@ApplicationScoped
+class Foo {
+}
+```
+
+### Are there any limitations to which `service.scope` `@Service` beans can have?
+
+Beans scoped `@Dependent` (_default when no scope annotation is defined on the bean_), and beans having the stereotype `@SingleComponent` or `@FactoryComponent` can have any `service.scope`.
+
+Beans scoped as `@ApplicationScoped` may only have `service.scope` __`singleton`__. However, such beans may directly implement `ServiceFactory` or `PrototypeServiceFactory` in order to provide service instances at __`bundle`__, or __`prototype`__ `service.scope` respectively.
+
+### How do I add service properties to my `@Service` beans?
+
+Service properties may be added to `@Service` beans by annotating them with annotations that are meta-annotated using `@BeanPropertyType`. These annotations are then coerced into service properties following a predefined set of coercion rules as defined in the specification.
+
+There are a number of predefined `@BeanPropertyTypes` to handle common cases, such as `@ServiceRanking`, `@ServiceDescription`, `@ServiceVendor` and `@ExportedService`.
+
+```java
+@Service
+@ServiceRanking(100)
+class Foo {
+}
+```
+
+In addition to these, Aries CDI provides an additional suite of __BeanPropertyTypes__ in the dependency `org.apache.aries.cdi.extra`:
+
+```xml
+<dependency>
+  <groupId>org.apache.aries.cdi</groupId>
+  <artifactId>org.apache.aries.cdi.extra</artifactId>
+  <version>${aries-cdi.version}</version>
+</dependency>
+```
+
+In all there exist _BeanPropertyTypes_ for the __Http Whiteboard__, __JAXRS Whiteboard__, __Event Admin__ and __Remote Service Admin__ specifications.
+
+### How do I add service properties that don't exist as a predefined `BeanPropertyType`?
+
+Creating your own BeanPropertyTypes is very simply meta-annotating a _runtime_ annotation with `@BeanPropertyType` (since CDI uses runtime annotation processing, the annotations must have runtime retention).
+
+```java
+@Retention(RUNTIME)
+@BeanPropertyType
+public @interface Config {
+    int http_port() default 8080;
+}
+```
+
+### Can I add Metatype annotations to my `BeanPropertyTypes`?
+
+Adding metatype annotations to your BeanPropertyTypes is the recommended way of providing a schema for your configuration(s).
+
+```java
+@Retention(RUNTIME)
+@BeanPropertyType
+@ObjectClassDefinition(
+    localization = "OSGI-INF/l10n/member",
+    description = "%member.description",
+    name = "%member.name"
+    icon = @Icon(resource = "icon/member-32.png", size = 32)
+)
+public @interface Member {
+    @AttributeDefinition(
+        type = AttributeType.PASSWORD,
+        description = "%member.password.description",
+        name = "%member.password.name"
+    )
+    public String _password();
+
+    @AttributeDefinition(
+        options = {
+            @Option(label = "%strategic", value = "strategic"),
+            @Option(label = "%principal", value = "principal"),
+            @Option(label = "%contributing", value = "contributing")
+        },
+        defaultValue = "contributing",
+        description = "%member.membertype.description",
+        name = "%member.membertype.name"
+    )
+    public String type();
+}
+```
+
+### Can I provide services using producer methods or fields?
+
+TODO
+
+## Referencing Services
+
+### How do I get a service from the registry into my bean?
+
+The simplest form of getting a service into a bean is with the `@Reference` annotation (simple called a _reference_).
+
+```java
+@Inject
+@Reference
+Bar bar; // using field injection
+
+// OR
+
+private final Bar bar;
+
+@Inject
+public Fum(@Reference Bar bar) { // using constructor injection
+    this.bar = bar;
+}
+
+// OR
+
+private Bar bar;
+
+@Inject
+public void addBar(@Reference Bar bar) { // using method injection
+    this.bar = bar;
+}
+
+```
+
+### I want to get the service properties of the referenced service. Are there other service representations I can get in my reference that can help me?
+
+There are a number of service representations that can be injected most of which provide a facility for getting the service properties:
+
+* `S` - where `S` is the raw service type, this is the most basic form of reference
+
+  ```java
+  @Inject
+  @Reference
+  Person person;
+  ```
+
+* `ServiceReference<S>` - you can get the service properties directly from the `ServiceReference` interface
+
+  ```java
+  @Inject
+  @Reference
+  ServiceReference<Person> personReference;
+  ```
+
+* `Map<String, Object>` - the service properties in `Map` form. Notice in this scenario that the reference must be qualified by a service type. (_This can also be used in other scenarios to narrow the services obtained to a more specific type. However, except in the Map use case, this qualified type __must__ be a subtype of the type specified in the reference._)
+
+  ```java
+  @Inject
+  @Reference(Person.class)
+  Map<String, Object> personProperties;
+  ```
+
+* `Map.Entry<Map<String,Object>, S>` - a `Man.Entry` holding the service properties map as key and the service instance as the value.
+
+  ```java
+  @Inject
+  @Reference
+  Map.Entry<Map<String, Object>, Person> personAndProperties;
+  ```
+
+* `BeanServiceObjects<S>` - a special type mapping to `ServiceObjects` providing support for prototype scope services. Get the service properties via the `getServiceReference` method on this interface.
+
+  ```java
+  @Inject
+  @Reference
+  BeanServiceObjects<Person> persons;
+  ```
+
+### Can I make the reference optional?
+
+Making a reference optional is simply using the `Optional` type around the service type.
+
+```java
+@Inject
+@Reference
+Optional<Person> person;
+
+// OR
+
+@Inject
+@Reference
+Optional<Map.Entry<Map<String, Object>, Person>> person;
+```
+
+### Can I get more than one service in a single reference?
+
+A reference can have multi-cardinality by specifying a container type of `java.util.Collection<R>`, or `java.util.List<R>` where `R` is one of the types specified in the previous sections.
+
+```java
+@Inject
+@Reference
+Collection<Person> persons;
+
+// OR
+
+@Inject
+@Reference
+List<Map.Entry<Map<String, Object>, Person>> persons;
+
+```
+
+### What is the default minimum cardinality of multi-cardinality references?
+
+Unless otherwise specified, the minimum cardinality is zero (0) making multi-cardinality references effectively _optional_ by default.
+
+```java
+@Inject
+@Reference
+List<Map.Entry<Map<String, Object>, Person>> persons; // min cardinality is 0, therefore this will resolve when no person services exist
+```
+
+### Can I specify a minimum cardinality when using a multi-cardinality reference?
+
+In order to specify a minimum cardinality use the `@MinimumCardinality` annotation on a multi-cardinality reference.
+
+```java
+@Inject
+@MinimumCardinality(3)
+@Reference
+List<Map.Entry<Map<String, Object>, Person>> persons;
+```
+
+### Can I make a multi-cardinality reference optional?
+
+Simply do not provide a minimum cardinality.
+
+### How do I specify a default target filter for the referenced service?
+
+There are a number of ways to specify a target filter for references:
+
+1. specify a service filter in the `target` property on the `@Reference` annotation.
+2. specify any number of BeanPropertyTypes on the reference all of which will be AND'ed together in the order they are defined (_after having been appended to any specified `target` value_.)
+
+```java
+@Inject
+@Reference(target = "(&(foo=bar)(service.vendor=Acme, Ltd.))")
+Collection<Dog> dogs;
+
+// OR
+
+@Inject
+@Reference(target = "(foo=bar)")
+@ServiceVendor("Acme, Ltd.")
+Collection<Dog> dogs;
+```
+
+Both of the above produce the same target filter.
+
+### Can I track unknown service types that match a target filter?
+
+Tracking any and all *service types* in a reference is supported providing the following criteria are met:
+
+1. `@Reference.value` must specify the single value `Reference.Any.class`.
+2. `@Reference.target` must specify a valid, non-empty filter value.
+3. The reference *service type* must be `java.lang.Object`.
+
+```java
+@Inject
+@Reference(value = Reference.Any.class, target = "(foo=bar)")
+List<Map.Entry<Map<String, Object>, Object>> fooAreBars;
+```
+
+###### TODO
+
+* add links on types throughout
+* dynamic references
+* portable extensions
+* tooling on README
+
+### 
\ No newline at end of file