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 <[email protected]>
AuthorDate: Sun Apr 7 12:29:35 2019 -0400
[CDI] some documentation
Signed-off-by: Raymond Auge <[email protected]>
---
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