This is an implementation of OSGi CDI Integration Specification (hereafter referred to simply as OSGi CDI).
The build uses maven so it should look pretty familiar to most developers.
mvn clean install
The main artifact is the CDI Component Runtime (CCR) implementation. a.k.a. the extender bundle:
<dependency> <groupId>org.apache.aries.cdi</groupId> <artifactId>org.apache.aries.cdi.extender</artifactId> <version>${aries-cdi.version}</version> <scope>runtime</scope> </dependency>
However all the required dependencies are available using the Aries CDI BOM:
<dependency> <groupId>org.apache.aries.cdi</groupId> <artifactId>org.apache.aries.cdi.bom</artifactId> <version>${aries-cdi.version}</version> <type>pom</type> <scope>import</scope> </dependency>
In order to make the best use of OSGi CDI you should use tooling that supports it. Bnd provides OOTB support for OSGi CDI annotations and enables a painless configuration model.
OSGi CDI support in bnd comes with any tool that uses bnd versions 4.1.0+
:
Note there are several improvements for CDI handling in later versions of bnd, so please use the latest version for best experience.
Bean discovery in bnd is handled by the -cdiannotations
instruction. The default value for this is *
(which is functionally equivalent to *;discover=annotated_by_bean
described below.)
Discovery is controlled by applying the attribute discover
to glob pattern used for matching classes in the bundle by their fully qualified names (the default glob *
matches all classes.)
Available discover
options are:
none
- disable bean discoveryannotated
- uses the CDI definition of annotated discovery modeall
- discover all classes which could be beansannotated_by_bean
- defined by bnd, this is the effective default which means to look for classes annotated with org.osgi.service.cdi.annotations.Bean
or packages annotated with org.osgi.service.cdi.annotations.Beans
)In combination the glob and modes give the developer very concise control over discovery.
If you want to emulate the CDI default use:
-cdiannotations: *;discover=annotated
In bnd 4.3.0+
you can rely purely on the discovery mode calculated from existing beans.xml
files in your project. This grants even less configuration friction for existing projects migrating to OSGi CDI.
This repository provides two examples for how to assemble an executable jar providing a complete runtime for you to just drop in your CDI bundles. These come complete with logging, Gogo shell, Config Admin, Http Whiteboard support, and OSGi Promises.
Once you've completed a successful build, you should be able to execute the commands:
java -jar cdi-executable/target/weld-executable.jar
and be presented a gogo shell prompt running in a framework that uses Aries CDI over Apache OpenWebBeans ready for you to install a CDI bundle.
java -jar cdi-executable/target/owb-executable.jar
and be presented with a gogo shell prompt running in a framework that uses Aries CDI over JBoss Weld ready for you to install a CDI bundle.
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.
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.)
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. 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, or upon removal, will cause the component to deactivate.
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
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.
When a CDI bundle is identified by CCR several steps are taken before any bean is instantiated:
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.ApplicationScoped
, Dependent
, RequestScoped
, SessionScoped
, ConversationScoped
, any custom scopes, etc.; all of these make up the container component.org.osgi.service.cdi.annotations.ComponentScoped
are part of the container component.@SingleComponent
are roots of a single component.@ComponentScoped
are also part of this single component.@FactoryComponent
are roots of a factory component.@ComponentScoped
are also part of this factory component.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.)ServiceFactory
like DS component services)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.PrototypeServiceFactory
). Service instances are created whenever the getService
method of the factory is called, and destroyed when the ungetService
is called.@Initialized(ComponentScoped.class)
@BeforeDestroy(ComponentScoped.class)
@Destroyed(ComponentScoped.class)
ServiceFactory
like DS component services)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.PrototypeServiceFactory
). Service instances are created whenever the getService
method of the factory is called, and destroyed when the ungetService
is called.@Initialized(ComponentScoped.class)
@BeforeDestroy(ComponentScoped.class)
@Destroyed(ComponentScoped.class)
Aries CDI now has an SPI for enabling it to be used with any CDI container impl.
The requirements to satisfy this SPI are quite simple:
Aries CDI Extender requires:
a prototype scoped service that implements org.apache.aries.cdi.spi.CDIContainerInitializer
The behaviour of this container should be to start the @ApplicationScoped
context immediately. This allows for services from the container component to be published right away.
Check out the many questions and answers in the FAQ.
Aries CDI enables a number of custom features for OSGi CDI Portable Extensions (further referred to as Extensions).
Implicit Extensions declare the custom service property aries.cdi.extension.mode
whose value is implicit
. When deployed into a framework, such extensions will bind to all CDI Bundles automatically.
Extensions that define requirements on other extensions (using the same osgi.cdi.extension
requirement a CDI Bundle would), will cause Aries CDI to bind those to the CDI Bundle automatically in a transitive fashion.
There are two ways an extension can load annotated types:
it can use the CDI SPI (this is the standard way.) For example:
public void addBeans(@Observes BeforeBeanDiscovery bbd, BeanManager bm) { bbd.addAnnotatedType(bm.createAnnotatedType(Foo.class)); }
it can use the custom attribute aries.cdi.extension.bean.classes
on the osgi.cdi.extension
capability provided by the extension. For example:
Provide-Capability: \ osgi.cdi.extension;\ osgi.cdi.extension='foo';\ version:Version='1.3.0';\ aries.cdi.extension.bean.classes:List<String>='org.acme.Foo'
A common scenario with OSGi CDI Portable Extensions is for extensions to adapt annotated types originating in the CDI Bundle as OSGi services (or with more service types). Common examples are the use cases relying on a whiteboard patterns. In such a case, services are deployed in the whiteboard at registration time. Using OSGi-CDI, you can fully use another programming model and hide OSGi service in a lot of cases. For instance, Aries-CDI uses that for servlet components where @WebServlet
- and other servlet annotations - are translated to an OSGi service registration with the HTTP whiteboard properties on the fly through a CDI extension.
Aries CDI's Extension SPI provides a convenience mechanism in the form of a carrier annotation @AdaptedService
and a helper extensions utilities. It is composed of mainly two points:
ProcessPotentialService
which is equivalent to ProcessAnnotatedType
but it guarantees the underlying annotated type does not have @Service
so that you can process the bean. If you do yourself the @Service
presence check, you can ignore that event type. Note that you can use org.apache.aries.cdi.extension.spi.adapt.FiltersOn
as the CDI @WithAnnotations
to filter this event by annotations or type.MergeServiceTypes
event which can be sent from the BeanManager
to add service types to a bean.For ProcessPotentialService
to be enabled, you must register the extension firing RegisterExtension
event in BeforeBeanDiscovery
event:
void register(@Observes final BeforeBeanDiscovery beforeBeanDiscovery, final BeanManager manager) { manager.fireEvent(new RegisterExtension(this)); }
Assuming you have the following bean:
@MyAutoRegistration public class MyService implements MyApi { // ... MyApi impl }
You can write an extension grabbing all @MyAutoRegistration
types to add them as services and register it in OSGi registry:
public class MyComponentTypeExtension implements Extension { void register(@Observes final BeforeBeanDiscovery beforeBeanDiscovery, final BeanManager manager) { manager.fireEvent(new RegisterExtension(this)); } void forceMyComponentTypeToBeAService( @Observes @FilterOn(annotations = MyAutoRegistration.class, types = MyApi.class) ProcessPotentialService pps, BeanManager beanManager) { beanManager.fireEvent(MergeServiceTypes.forEvent(pat).withTypes(MyApi.class).build()); } }
Alternatively you can use the funcitonal style for that extension:
public class MyComponentTypeExtension implements Extension { void register(@Observes final BeforeBeanDiscovery beforeBeanDiscovery, final BeanManager manager) { manager.fireEvent(new RegisterExtension(this) .registerObserver() .forTypes(MyApi.class) .forAnnotations(MyAutoRegistration.class) .execute((beanManager, processPotentialService) -> beanManager.fireEvent(MergeServiceTypes.forEvent(pat).withTypes(MyApi.class).build())) .done()); } }
This is enough to let Aries-CDI registers the annotated type as a service (as if you would have @Service(MyApi.class)
).
IMPORTANT: only BeanManager
injection is supported for this kind of lifecycle methods.
The dependency for the Extension SPI is:
<dependency> <groupId>org.apache.aries.cdi</groupId> <artifactId>org.apache.aries.cdi.extension.spi</artifactId> <version>...</version> </dependency>