The simplest service is a bean annotated with @Service
.
package com.acme; @Service class Foo { }
@Service
beans published?Unless otherwise specified, @Service
beans are published as follows:
The service above is registered with the service type com.acme.Foo
.
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
.
@Service
beans?There are several ways to specify which service types @Service
beans are published.
@Service
annotation to type use clauses.@Service
annotation on the bean type may specify a set of types as an argument of the annotation.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
.
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.
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.
service.scope
on my @Service
beans?The annotation @ServiceInstance
is used to specify the service.scope
.
@Service @ServiceInstance(PROTOTYPE) class Foo { }
Available values are SINGLETON
, BUNDLE
, and PROTOTYPE
.
Beans scoped @ApplicationScoped
or @Dependent
, as well as beans having the stereotype @SingleComponent
or @FactoryComponent
can be annotated with @Service
and become services.
@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 { }
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.
@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
.
@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
:
<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.
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).
@Retention(RUNTIME) @BeanPropertyType public @interface Config { int http_port() default 8080; }
BeanPropertyTypes
?Adding metatype annotations to your BeanPropertyTypes is the recommended way of providing a schema for your configuration(s).
@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(); }
TODO
The simplest form of getting a service into a bean is with the @Reference
annotation (simple called a reference).
@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; }
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
@Inject @Reference Person person;
ServiceReference<S>
- you can get the service properties directly from the ServiceReference
interface
@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.)
@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.
@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.
@Inject @Reference BeanServiceObjects<Person> persons;
Making a reference optional is simply using the Optional
type around the service type.
@Inject @Reference Optional<Person> person; // OR @Inject @Reference Optional<Map.Entry<Map<String, Object>, Person>> person;
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.
@Inject @Reference Collection<Person> persons; // OR @Inject @Reference List<Map.Entry<Map<String, Object>, Person>> persons;
Unless otherwise specified, the minimum cardinality is zero (0) making multi-cardinality references effectively optional by default.
@Inject @Reference List<Map.Entry<Map<String, Object>, Person>> persons; // min cardinality is 0, therefore this will resolve when no person services exist
In order to specify a minimum cardinality use the @MinimumCardinality
annotation on a multi-cardinality reference.
@Inject @MinimumCardinality(3) @Reference List<Map.Entry<Map<String, Object>, Person>> persons;
Simply do not provide a minimum cardinality.
There are a number of ways to specify a target filter for references:
target
property on the @Reference
annotation.target
value.)@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.
Tracking any and all service types in a reference is supported providing the following criteria are met:
@Reference.value
must specify the single value Reference.Any.class
.@Reference.target
must specify a valid, non-empty filter value.java.lang.Object
.@Inject @Reference(value = Reference.Any.class, target = "(foo=bar)") List<Map.Entry<Map<String, Object>, Object>> fooAreBars;