SeamFramework.orgCommunity Documentation
CDI-OSGi is composed of five bundles:
The three API bundles that provide the API used to define both utilities provided and hooking up system with integration bundle,
The extension bundle that provides CDI-OSGi features for bean bundles by managing them,
An integration bundle that provides CDI features usable by the extension bundle through an OSGi service.
Note that as CDI-OSGi runs in an OSGi environment it is implicit that there is an OSGi core bundle too. This one provide OSGi features for all other bundles, including CDI-OSGi managed bundles. But it is not an actual part of CDI-OSGi.
Three API bundles expose all packages that could be needed by developers; both for client applications using CDI-OSGi and for CDI compliant container integration. Their packages may be imported by bean bundles, extension bundle or implementation bundle.
These three distinct API are:
The extension API that describes all the new features provided by CDI-OSGi in the OSGi environment.
The integration API that describes how CDI compliant containers may be used by CDI-OSGi.
The CDI API that describes all the regular CDI features provided by CDI-OSGi to bean bundles.
These three API are more described below.
The extension API defines all the features provided to OSGi environment using CDI specifications. It exposes all the new utilities and defines the comportment of the extension bundle.
It exposes all the interfaces, events and annotations usable by a developers in order to develop its client bean bundles. It defines the programming model of CDI-OSGi client bean bundles. Mostly it is about publishing and consuming injectable services in a CDI way.
It also describes what the extension bundle needs to orchestrate bean bundles.
So this is where to search for new usages of OSGi.
The integration API defines how a CDI container, such as Weld, should bootstrap with the CDI OSGi extension. So any CDI environment implementation could use the CDI OSGi extension transparently. The CDI compliant container may be provided using an integration bundle.
This aims at providing the minimum integration in order to start a CDI compliant container with every managed bean bundle. Then the extension bundle can get a CDI container for each of its managed bean bundles.
Moreover the integration API allows to mix CDI implementations in the same application by providing an embedded mode. In this mode a bean bundle is decoupled from the extension bundle and is managed on its own. Thus various implementations of CDI container can be used and the behavior of a particular bean bundle can be particularized.
All this bootstrapping mechanism works using the service layer of OSGi. An integration bundle may provide a service that allows the extension bundle to obtain a new container for every bean bundle.
So this is where to search to make CDI-OSGi use a specific CDI compliant container.
The extension bundle is the orchestrator of CDI-OSGi. It may be use by any application that requires CDI-OSGi. It may be just started with OSGi environment. It requests the extension API bundle as a dependency.
The extension bundle is the heart of CDI-OSGi application. Once it is started, provided that it finds a started integration bundle, it manages all the bean bundles. It is in charge of service automatic publishings, service injections, CDI event notifications and bundle communications. It runs in background, everything is transparent to the user. Client bean bundles do not have to do anything in order to use CDI-OSGi functionality.
In order to perform injections the extension bundle search for a CDI container factory service once it is started. Thus it can only work coupled with a bundle providing such a service: an integration bundle.
So this is where the magic happens and where OSGi applications become much more simple.
The integration bundle is responsible for providing CDI compliant containers to the extension bundle. It may be started with the extension bundle and publish a CDI container factory service. It requests the integration API bundle as a dependency.
It is an implementation of the integration API but it can use any CDI implementation in order to fulfill it. So this bundle might not be unique but exist for each vendor specific CDI implementation (such as Weld).
As an extension to OSGi, CDI-OSGi provides several features :
Complete integration with OSGi world by the use of extender pattern and extension bundle. Thus complete compatibility with already existing tools.
Non intruding, configurable and customizable behavior in new or upgraded application. Simple configuration and usage with annotations, completely xml free.
Full internal CDI support for bean bundles: injection, producers, interceptors, decorators ...
Lot of ease features for OSGi usages: injectable services, event notifications, inter-bundle communication ...
OSGi and CDI compliance all along the way ensuring compatibility with all CDI compliant container and easy application realisation or portage.
We will see in the next sections these features in deep through the description of the extension API.
Extension API provides few interfaces that describe all specifics about OSGi service injection.
public interface Service<T> extends Iterable<T> { T get(); Service<T> select(Annotation... qualifiers); Service<T> select(String filter); boolean isUnsatisfied(); boolean isAmbiguous(); int size(); }
It represents a service instance producer parametrized by the service to
inject. It has the same behavior than CDI Instance<T>
except
that it represents only OSGi service beans.
IT allows to:
Wrap a list of potential service implementations as an
Iterable
java object,
Select a subset of these service implementations filtered by
Qualifier
s or LDAP filters,
Iterate through these service implementations,
Obtain an instance of the first remaining service implementations,
Obtain utility information about the contained service implementations.
OSGi services should not be subtyped.
public interface Registration<T> extends Iterable<Registration<T>> { void unregister(); <T> Service<T> getServiceReference(); Registration<T> select(Annotation... qualifiers); Registration<T> select(String filter); int size(); }
This interface represents the registrations of an injectable service in the
service registry. Its behavior is similar to Service<T>
, thus it
might represent the iterable set of all the registrations of a service.
It allows to:
Wrap a list of service registration (i.e. the bindings between a
service and its implementations) as an Iterable
java
object,
Select a subset of these registration filtered by
Qualifier
s or LDAP filters,
Iterate through these service registrations,
Obtain the service implementations list as a
Service<T>
,
Get the number of registrations (i.e the number of registered service implementations).
OSGi services should not be subtyped.
public interface RegistrationHolder { List<ServiceRegistration> getRegistrations(); void addRegistration(ServiceRegistration reg); void removeRegistration(ServiceRegistration reg); void clear(); int size(); }
This interface represents the bindings between a service and its registered
implementations. It is used by Registration
to maintain the list of
registration bindings. It uses OSGi ServiceRegistration
.
It allows to:
Wrap a list of ServiceRegistration
as binding between
a service and its implementations as a List
,
Handle this list with addition, removal, clearing and size operations.
public interface ServiceRegistry { <T> Registration<T> registerService(Class<T> contract, Class<? extends T> implementation); <T, U extends T> Registration<T> registerService(Class<T> contract, U implementation); <T> Service<T> getServiceReferences(Class<T> contract); }
This interface represents a service registry where all OSGi services may be handled.
It allows to:
Register a service implementation with a service, getting back the
corresponding Registration
,
Obtain the service implementations list as a
Service<T>
.
Extension API provides numerous events that notify about life-cycle steps for CDI container management, service management, bundle management, bean bundle validation and bundle communication management. They all use CDI event system.
CDI container events, as well as service and bundle events, are regrouped using three classes. Thus each category of event has:
An abstract class representing all the events of this category, defining the global comportment and allowing to handle all the lifecycle steps in one operation,
An enumeration describing the list of possible event types, allowing to discriminate a particular lifecycle step of the category,
A list of implementation class for each of the above type, allowing to handle a particular lifecycle step only. These implementation classes are regrouped as static inner classes of a global category event class.
The representing abstract class:
public abstract class AbstractBundleEvent { private final Bundle bundle; public AbstractBundleEvent(Bundle bundle) {...} public abstract BundleEventType getType(); public long getBundleId() {...} public String getSymbolicName() {...} public Version getVersion() {...} public Bundle getBundle() {...} }
This abstract class represents all the CDI-OSGi bundle events as a superclass.
It allows to:
Represent all bundle events,
Retrieve the current event type as a
BundleEventType
,
Retrieve the firing bundle and its information.
It may be used in Observes
method in order to listen all bundle
events.
The possible event types:
public enum BundleEventType { INSTALLED, LAZY_ACTIVATION, RESOLVED, STARTED, STARTING, STOPPED, STOPPING, UNINSTALLED, UNRESOLVED, UPDATED, }
This enumeration lists all possible states of a bundle and the corresponding event types.
The implementation classes (in the global event class):
public class BundleEvents { public static class BundleInstalled extends AbstractBundleEvent { public BundleInstalled(Bundle bundle) {...} @Override public BundleEventType getType() {...} } //next classes follow the same template as above public static class BundleLazyActivation extends AbstractBundleEvent {...} public static class BundleResolved extends AbstractBundleEvent {...} public static class BundleStarted extends AbstractBundleEvent {...} public static class BundleStarting extends AbstractBundleEvent {...} public static class BundleStopped extends AbstractBundleEvent {...} public static class BundleStopping extends AbstractBundleEvent {...} public static class BundleUninstalled extends AbstractBundleEvent {...} public static class BundleUnresolved extends AbstractBundleEvent {...} public static class BundleUpdated extends AbstractBundleEvent {...} }
This class wraps all the bundle events as inner static classes. There is one
event class by BundleEventType
.
Each inner class allows to:
Represent a specific bundle event,
Retrieve the same information as
AbstractBundleEvent
.
They may be used in Observes
method in order to listen a specific
bundle event.
The representing abstract class:
public abstract class AbstractServiceEvent { private final ServiceReference reference; private final BundleContext context; private List<String> classesNames; private List<Class<?>> classes; private Map<Class, Boolean> assignable; public AbstractServiceEvent(ServiceReference reference, BundleContext context) {...} public abstract ServiceEventType eventType(); public ServiceReference getReference() {...} public <T> TypedService<T> type(Class<T> type) {...} public Object getService() {...} public boolean ungetService() {...} public boolean isTyped(Class<?> type) {...} public Bundle getRegisteringBundle() {...} public List<String> getServiceClassNames() {...} public List<Class<?>> getServiceClasses() {...} public static class TypedService<T> { private final BundleContext context; private final ServiceReference ref; private final Class<T> type; TypedService(BundleContext context, ServiceReference ref, Class<T> type) {...} static <T> TypedService<T> create(Class<T> type, BundleContext context, ServiceReference ref) {...} public T getService() {...} public boolean ungetService() {...} } }
This abstract class represents all the CDI-OSGi service events as a superclass.
It allows to:
Represent all service events,
Retrieve the current event type as a
ServiceEventType
,
Retrieve the affected ServiceReference
, the
corresponding information and registering
Bundle
,
Manipulate the service,
Retrieve the firing BundleContext
.
It may be used in Observes
method in order to listen all service
events.
The possible event types:
public enum ServiceEventType { SERVICE_ARRIVAL, SERVICE_DEPARTURE, SERVICE_CHANGED }
This enumeration lists all possible states of a service and the corresponding event types.
The implementation classes (in the global event class):
public class ServiceEvents { public static class ServiceArrival extends AbstractServiceEvent { public ServiceArrival( ServiceReference ref, BundleContext context) { super(ref, context); } @Override public ServiceEventType eventType() { return ServiceEventType.SERVICE_ARRIVAL; } } //next classes follow the same template as above public static class ServiceChanged extends AbstractServiceEvent {...} public static class ServiceDeparture extends AbstractServiceEvent {...} }
This class wraps all the service events as inner static classes. There is one
event class by ServiceEventType
.
Each inner class allows to:
Represent a specific service event,
Retrieve the same information as
AbstractServiceEvent
.
They may be used in Observes
method in order to listen a specific
service event.
The representing abstract class:
public abstract class AbstractBundleContainerEvent { private BundleContext bundleContext; public AbstractBundleContainerEvent(final BundleContext context) {...} public abstract BundleContainerEventType getType(); public BundleContext getBundleContext() {...} }
This abstract class represents all the CDI-OSGi bundle container events as a superclass.
It allows to:
Represent all bundle container events,
Retrieve the current event type as a
BundleContainerEventType
,
Retrieve the firing BundleContext
.
It may be used in Observes
method in order to listen all bundle
container events.
The possible event types:
public enum BundleContainerEventType { INITIALIZED, SHUTDOWN }
This enumeration list all possible states of a bundle container and the corresponding event types.
The implementation classes (in the global event class):
public class BundleContainerEvents { public static class BundleContainerInitialized extends AbstractBundleContainerEvent { public BundleContainerInitialized(BundleContext context) { super(context); } @Override public BundleContainerEventType getType() { return BundleContainerEventType.INITIALIZED; } } //next class follows the same template as above public static class BundleContainerShutdown extends AbstractBundleContainerEvent {...} }
This class wraps all the bundle container events as inner static classes.
There is one event class by BundleContainerEventType
.
Each inner class allows to:
Represent a specific bundle container event,
Retrieve the same information as
AbstractBundleContainerEvent
.
They may be used in Observes
method in order to listen a specific
bundle container event.
Two events notify about the validation of bean bundle. After it starts a bean
bundle should validate its required service dependencies. The Valid
and Invalid
events occurs when such dependencies are fulfilled or
unfulfilled. These two new states are described in an enumeration:
BundleState
.
public class Valid { private final Bundle bundle; public Valid(Bundle bundle) {...} public long getBundleId() {...} public String getSymbolicName() {...} public Version getVersion() {...} public Bundle getBundle() {...} }
This class represents all bean bundle validation event.
It allows to:
Represent all bean bundle validation events,
Retrieve the validated bean bundle and its information.
It may be used in Observes
method in order to listen all bean
bundle validation events.
public class Invalid { private final Bundle bundle; public Invalid(Bundle bundle) {...} public long getBundleId() {...} public String getSymbolicName() {...} public Version getVersion() {...} public Bundle getBundle() {...} }
This class represents all bean bundle invalidation event.
It allows to:
Represent all bean bundle invalidation events,
Retrieve the invalidated bean bundle and its information.
It may be used in Observes
method in order to listen all bean
bundle invalidation events.
public enum BundleState { VALID, INVALID }
This enumeration lists the two new states of a bean bundle.
A bean bundle is in VALID
state if all its required service
dependencies are validated otherwise is in INVALID
state. Every
time a bean bundle goes from one state to another a corresponding
Valid
or Invalid
event may be fired.
public class InterBundleEvent { private final Object event; private boolean sent = false; private Class<?> type; public InterBundleEvent(Object event) {...} public InterBundleEvent(Object event, Class<?> type) {...} public Class<?> type() {...} public boolean isTyped(Class<?> type) {...} public <T> Provider<T> typed(Class<T> type) {...} public Object get() {...} public boolean isSent() {...} public void sent() {...} }
This class represents a communication event between bean bundles.
It allows to specify:
The message as an Object
,
The type of the message as a Class
,
The origin of the message (within or outside the bundle).
It may be used in Observes
method in order to listen inter-bundle
communications.
Extension API provides annotations in order to easily use CDI-OSGi features.
@Qualifier @Retention(RetentionPolicy.RUNTIME) public @interface OSGiBundle { @Nonbinding String value(); @Nonbinding String version() default ""; }
This annotation qualifies an injection point that represents a bundle or a bundle relative object.
It allows to specify:
The symbolic name of the bundle, as a required value,
The version of the bundle, as an optional value.
The symbolic name and version are not
actually qualifying the injection point, thus this annotation is for global
bundle injection point with additional data. In order to actually discriminate
on the symbolic name or version see BundleName
and
BundleVersion
annotations.
@Qualifier @Retention(RetentionPolicy.RUNTIME) public @interface BundleName { String value(); }
This annotation qualifies an injection point that represents a bundle or a bundle relative object.
It allows to specify the symbolic name of the bundle, as a required value.
The symbolic name actually discriminate the injection point, thus this
annotation is for specific bundle relative injection point. For global bundle
relative injection point see OSGiBundle
annotation. To discriminate
the bundle version see BundleVersion
.
@Qualifier @Retention(RetentionPolicy.RUNTIME) public @interface BundleVersion { String value(); }
This annotation qualifies an injection point that represents a bundle or a bundle relative object.
It allows to specify the version of the bundle, as a required value.
The version actually discriminate the injection point, thus this annotation is
for specific bundle relative injection point. For global bundle relative
injection point see OSGiBundle
annotation. To discriminate the
bundle symbolic name see BundleName
.
@Qualifier @Retention(RetentionPolicy.RUNTIME) public @interface BundleDataFile { @Nonbinding String value(); }
This annotation qualifies an injection point that represents a bundle data file.
It allows to specify the relative path of the file in the bundle, as a required value.
The file path is not actually qualifying the
injection point, thus this annotation is for global bundle data file injection
point with additional data. To discriminate the bundle use
OSGiBundle
or BundleName
and
BundleVersion
annotations.
@Qualifier @Retention(RetentionPolicy.RUNTIME) public @interface BundleHeader { @Nonbinding String value(); }
This annotation qualifies an injection point that represents a specific bundle header.
It allows to specify the name of the bundle header, as a required value.
The header name is not actually qualifying
the injection point, thus this annotation is for global specific bundle header
injection point with additional data. To discriminate the bundle use
OSGiBundle
or BundleName
and
BundleVersion
annotations. To obtain all the bundle headers see
BundleHeaders
annotations.
@Qualifier @Retention(RetentionPolicy.RUNTIME) public @interface BundleHeaders { }
This annotation qualifies an injection point that represents all headers of a bundle.
To discriminate the bundle use OSGiBundle
or
BundleName
and BundleVersion
annotations. To
obtain a specific bundle header see BundleHeader
annotation.
@Qualifier @Target({ TYPE, METHOD, PARAMETER, FIELD }) @Retention(RetentionPolicy.RUNTIME) public @interface OSGiService { }
This annotation qualifies an injection point that represents a service from the OSGi service registry.
It may be use to obtain an injected OSGi service using Service
interface or directly the service contract interface. The injected service might
be filtered using regular Qualifier
annotations or a LDAP filter
with Filter
annotation. It also might be mark as required for
bundle running using Required
annotation.
@Target({ TYPE }) @Retention(RetentionPolicy.RUNTIME) public @interface Publish { public Class[] contracts() default {}; public Property[] properties() default {}; }
This annotation notices that this type is an OSGi service implementation and should be automatically published in the OSGi service registry.
It allows to specify:
The contract interfaces of implemented service, as an optional
array of Class
es,
The properties of the published service implementation, as an
optional array of Property
.
The published implementation might be discriminated using regular
Qualifier
annotations or a LDAP filter with Filter
annotation.
@Retention(RetentionPolicy.RUNTIME) public @interface Property { String name(); String value(); }
This annotation wraps an OSGi service property used for automatic OSGi service publishing.
It allows to specify:
The name of the property, as a required
String
,
The value of the property, as a required
String
.
It may be used within the Publish
annotation to provide the
published service implementation properties.
@Qualifier @Retention(RetentionPolicy.RUNTIME) public @interface Required { }
This annotation qualifies an injection point that represents a required service.
It may be coupled with the OSGiService
annotation in order to
qualify a service that must be available for the application to start.
@Qualifier @Retention(RetentionPolicy.RUNTIME) public @interface Filter { String value(); }
This annotation qualifies an injection point that represents a LDAP filtered service.
It allows to specify the LDAP filter, as a required
String
.
It may be coupled with a OSGiService
or a Publish
annotation in order to filter the injected or published service implementations.
The LDAP filtering acts on Qualifier
or Property
annotations or regular OSGi properties used in service publishing.
@Qualifier @Target({ PARAMETER }) @Retention(RetentionPolicy.RUNTIME) public @interface Specification { Class value(); }
This annotation qualifies an injection point that represents events whose match one of the specifications.
It allows to specify the specification interfaces of the Event
s
parametrizing types, as a required Class
.
It may be used in an Observes
method to restrict the listened
events.
@Qualifier @Target({ PARAMETER }) @Retention(RetentionPolicy.RUNTIME) public @interface Sent { }
This annotation qualifies an injection point that represents an
InterBundleEvent
from outside the current
Bundle
.
It may be used in an Observes
method to restrict the listened
InterBundleEvent
. It allows to ignore the
InterBundleEvent
from within the current bundle.
CDI-OSGi could have been carried out with an internal CDI compliant container and work very well. Every one of the specifications above would have been fulfilled. But no other specific utilities or CDI evolution could have been added without a complete modification of CDI-OSGi.
Then there is the integration API. With that, developers are able to provide their own integration bundle. Thus it is possible to use any CDI compliant container with a CDI-OSGi application or mix up different CDI compliant containers for different bundles.
Integration API describes what needs the extension bundle to work (and therefore what may provide the integration bundle):
A CDI container with standard comportment and functionality,
A factory for such CDI containers initialized and ready to run.
public interface CDIContainer extends Iterable<CDIContainer> { boolean initialize(); boolean shutdown(); boolean isStarted(); void fire(InterBundleEvent event); Bundle getBundle(); BeanManager getBeanManager(); Event getEvent(); Instance<Object> getInstance(); Collection<String> getBeanClasses(); Collection<ServiceRegistration> getRegistrations(); void setRegistrations(Collection<ServiceRegistration> registrations); CDIContainer select(Bundle bundle); CDIContainer select(String name, String version); int size(); }
This interface represents an iterable list of CDI containers used by CDI-OSGi.
It allows to:
Navigate through the list of CDI containers as an
Iterable
,
Obtain the number of CDI containers,
Select a specific container by its bundle,
Start and stop the selected CDI container,
Obtain the state of the selected CDI container,
Obtain the corresponding Bundle
,
BeanManager
, Event
, managed bean
Class
and Instance
and registred
service as ServiceRegistration
,
Fire InterBundleEvent
.
public interface CDIContainerFactory { String getID(); Set<String> getContractBlacklist(); CDIContainer container(Bundle bundle); }
This interface represents a CDI container factory used by CDI-OSGi in order to
obtain CDIContainer
.
It allows to:
Obtain the CDI container of a specific bean Bundle
(singleton for each bean bundle),
Provide a interface black list for service publishing,
Obtain the ID of the used CDI implementation.
CDI-OSGi provides an extension to OSGi as an extender pattern. The extension bundle, the extender, tracks for bean bundles, the extensions, to be started. Then CDI utilities are enabled for these bean bundles over OSGi environment.
BEGIN start WHILE ! implementation_bundle.isStarted wait END_WHILE obtain_container_factory FOR bean_bundle : started_bundles manage_bean_bundle provide_container END_FOR WHILE implementation_bundle.isStarted wait_event OnBeanBundleStart manage_bean_bundle provide_container OnBeanBundleStop unmanage_bean_bundle END_WHILE stop FOR bean_bundle : namaged_bundles unmanage_bean_bundle stop_bean_bundle END_FOR END
The integration bundle is necessary in a CDI-OSGi application. This section explains the part the integration bundle plays but does not enter in specifics because it depends on the CDI implementation used.
Weld-OSGi chapter presents how an implementation bundle can work.
This section presents the lifecycle of a bean bundle and how it impacts CDI and OSGi regular behaviors. Mostly bean bundles follow the same lifecycle than a regular bundle. There are only two new possible states and they do not modify the behavior from OSGi side.
Figure 2.2. The bean bundle lifecycle
The regular OSGi lifecycle is not modified by the new CDI-OSGi states as they have the same meaning than the ACTIVE state from an OSGi point of view. They only add information about the validation of required service availability.
There are very few things to do in order to obtain a bean bundle from a bean archive or a bundle. Mostly it is just adding the missing marker files and headers in the archive:
Make a bean archive a bean bundle by adding special OSGi marker headers
in its META-INF/Manifest.MF
file.
Or, in the other way, make a bundle a bean bundle by adding a
META-INF/bean.xml
file.
Thus a bean bundle has both META-INF/bean.xml
file and OSGi marker
headers in its META-INF/Manifest.MF
file.
However there is a few other information that CDI-OSGi might need in order to perform a correct extension. In particular a bean bundle can not be manage by the extension bundle but by his own embedded CDI container. For that there is a new manifest header.
The beans.xml file follows no particular rules and should be the same as in a native CDI environment. Thus it can be completely empty or declare interceptors, decorators or alternatives as a regular CDI beans.xml file.
There will be no different behavior with a classic bean archive except for CDI
OSGi extension new utilities. But these don't need any modification on the
META-INF/bean.xml
file.
This header prevents the extension bundle to automatically manage the bean bundle
that set this manifest header to true. So the bean bundle can be manage more finely
by the user or use a different CDI container. If this header is set to false or is
not present in the META-INF/Manifest.MF
file then the bean bundle will
be automatically manage by the extension bundle (if it is started).