The OSGi service registry enables a bundle to publish objects to a shared registry, advertised via a given set of Java interfaces. Published services also have service properties associated with them in the registry.
Spring Dynamic Modules provides an osgi
namespace for Spring (see
Appendix H, Spring Dynamic Modules Schema) that can be used to export
Spring beans as OSGi services, and
to define references to services obtained via the service registry. The
namespace elements may be used nested inside another top-level namespace
(typically the Spring beans
namespace), or within the top-level
osgi
element.
The following example shows the use of the osgi
namespace within the familiar Spring beans element:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:osgi="http://www.springframework.org/schema/osgi" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/osgi http://www.springframework.org/schema/osgi/spring-osgi.xsd"> <osgi:service id="simpleServiceOsgi" ref="simpleService" interface="org.xyz.MyService" /> </beans>
Use Spring Framework | |
Import Spring Dynamic Modules schema and associate a prefix with its namespace ( | |
Make sure to import Spring beans schema version 2.5. | |
Use Spring Dynamic Modules elements using the declared namespace prefix (in this example |
Using the OSGi namespace as a top-level namespace, the same service would be declared as follows:
<?xml version="1.0" encoding="UTF-8"?> <beans:beans xmlns="http://www.springframework.org/schema/osgi" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans" xsi:schemaLocation="http://www.springframework.org/schema/osgi http://www.springframework.org/schema/osgi/spring-osgi.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <service id="simpleServiceOsgi" ref="simpleService" interface="org.xyz.MyService" /> </beans:beans>
| |
Use Spring Dynamic Modules schema as the default namespace. | |
Import Spring Framework | |
Make sure to import Spring beans schema version 2.5. | |
Use Spring Dynamic Modules elements without any prefix. |
Using the OSGi namespace as a top-level namespace is particularly convenient when following the recommendation of Section 5.1, “Bundle format and Manifest headers” to use a dedicated configuration file for all OSGi-related declarations.
The service
element is used to define a bean
representing an exported OSGi service. At a minimum you must specify the
bean to be exported, and the service interface that
the service advertises.
For example, the declaration
<service ref="beanToPublish" interface="com.xyz.MessageService"/>
exports the bean with name beanToPublish
with
interface com.xyz.MessageService
. The published
service will have a service property with the name
org.springframework.osgi.bean.name
set to the name of
the target bean being registered (beanToPublish
in
this case).
The bean defined by the
service
element is of type
org.osgi.framework.ServiceRegistration
and is the
ServiceRegistration
object resulting from registering
the exported bean with the OSGi service registry. By giving this bean an
id you can inject a reference to the
ServiceRegistration
object into other beans if
needed. For example:
<service id="myServiceRegistration" ref="beanToPublish" interface="com.xyz.MessageService"/>
As an alternative to exporting a named bean, the bean to be
exported to the service registry may be defined as an anonymous inner
bean of the service element. Typically the top-level namespace would be
the beans
namespace when using this style:
<osgi:service interface="com.xyz.MessageService"> <bean class="SomeClass"> ... </bean> </osgi:service>
If the bean to be exported implements the
org.osgi.framework.ServiceFactory
interface then the
ServiceFactory
contract is honored as per section 5.6
of the OSGi Service Platform Core Specification. As an alternative to
implementing this OSGi API, Spring Dynamic Modules introduces a new bean
scope, the bundle
scope. When a bean with bundle
scope is exported as an OSGi service then one instance of the bean will
be created for each unique client (service importer) bundle that
obtains a reference to it through the OSGi service registry. When a
service importing bundle is stopped, the bean instance associated with
it is disposed. To declare a bean with bundle
scope
simply use the scope
attribute of the
bean
element:
<osgi:service ref="beanToBeExported" interface="com.xyz.MessageService"/> <bean id="beanToBeExported" scope="bundle" class="com.xyz.MessageServiceImpl"/>
The OSGi Service Platform Core Specification defines the term service interface to represent the specification of a service's public methods. Typically this will be a Java interface, but the specification also supports registering service objects under a class name, so the phrase service interface can be interpreted as referring to either an interface or a class.
There are several options for specifying the service
interface(s) under which the exported service is registered. The
simplest mechanism, shown above, is to use the
interface
attribute to specify a fully-qualified
interface name. To register a service under multiple interfaces the
nested interfaces
element can be used in place of
the interface
attribute.
<osgi:service ref="beanToBeExported"> <osgi:interfaces> <value>com.xyz.MessageService</value> <value>com.xyz.MarkerInterface</value> </osgi:interfaces> </osgi:service>
It is illegal to use both interface
attribute and
interfaces
element at the same time - use only one of them.
Using the auto-export
attribute you can avoid
the need to explicitly declare the service interfaces at all by analyzing the
object class hierarchy and its interfaces.
The auto-export
attribute can have one of four
values:
disabled
: the default value; no auto-detected of service
interfaces is undertaken and the interface
attribute or interfaces
element must be used
instead.
interfaces
: the service will be registered using all of the
Java interface types implemented by the bean to be exported
class-hierarchy
: the service will be registered using the
exported bean's implementation type and super-types
all-classes
: the service will be registered using the exported
bean's implementation type and super-types plus all interfaces
implemented by the bean.
auto-export
and interface(s)
option are not exclusive; both
can be used at the same time for fine grained control over the advertised interfaces if there is such
a need. However, the former option should be enough for most cases.
For example, to automatically register a bean under all of the interfaces that it supports you would declare:
<service ref="beanToBeExported" auto-export="interfaces"/>
Given the interface hierarchy:
public interface SuperInterface {} public interface SubInterface extends SuperInterface {}
then a service registered as supporting the
SubInterface
interface is not
considered a match in OSGi when a lookup is done for services
supporting the SuperInterface
interface. For this
reason it is a best practice to export all interfaces supported by the
service being registered explicitly, using either the
interfaces
element or
auto-export="interfaces"
.
As previously described, an exported service is always
registered with the service property
org.springframework.osgi.bean.name
set to the name
of the bean being exported. Additional service properties can be
specified using the nested service-properties
element. The service-properties
element contains
key-value pairs to be included in the advertised properties of the
service. The key must be a string value, and the value must be a type
recognized by OSGi Filters. See section 5.5 of the OSGi Service
Platform Core Specification for details of how property values are
matched against filter expressions.
The service-properties
element must contain
at least one nested entry
element from the Spring
beans namespace. For example:
<service ref="beanToBeExported" interface="com.xyz.MyServiceInterface"> <service-properties> <beans:entry key="myOtherKey" value="aStringValue"/> <beans:entry key="aThirdKey" value-ref="beanToExposeAsProperty"/> </service-properties> </service>
The Spring Dynamic Modules roadmap includes support for exporting properties registered in the OSGi Configuration Administration service as properties of the registered service. See Appendix F, Roadmap for more details.
Spring will manage explicit dependencies of a service element,
ensuring for example that the bean to be exported as a service is
fully constructed and configured before exporting it. If a service has
implicit dependencies on other components (including other service
elements) that must be fully initialized before the service can be
exported, then the optional depends-on
attribute
can be used to express these dependencies.
<service ref="beanToBeExported" interface="com.xyz.MyServiceInterface" depends-on="myOtherComponent"/>
The OSGi Service Platform Core Specification (most current
version is 4.1 at time of writing) does not specify what types and
resources are visible through the context class loader when an
operation is invoked on a service obtained via the service registry.
Since some services may use libraries that make certain assumptions
about the context class loader, Spring Dynamic Modules enables you to
explicitly control the context class loader during service execution.
This is achieved using the option
context-class-loader
attribute of the service
element.
The permissible values for the
context-class-loader
attribute are
unmanaged
(the default) and
service-provider
. When the
service-provider
value is specified, Spring Dynamic
Modules ensures that the context class loader can see all of the
resources on the class path of the bundle exporting the service.
When setting context-class-loader
to service-provider
, the service object will be proxied to
handle the class loader. If the service advertises any concrete class then CGLIB library is required .
When registering a service with the service registry, you may
optionally specify a service ranking (see section 5.2.5 of the OSGi
Service Platform Core Specification). When a bundle looks up a service
in the service registry, given two or more matching services the one
with the highest ranking will be returned. The default ranking value
is zero. To explicitly specify a ranking value for the registered
service, use the optional ranking
attribute.
<service ref="beanToBeExported" interface="com.xyz.MyServiceInterface" ranking="9"/>
As a summary, the following table lists the attributes names, possible values and a short description for each of them.
Table 6.1. OSGi <service> attributes
Name | Values | Description | |||
---|---|---|---|---|---|
interface | fully qualified class name (such as java.lang.Thread ) | the fully qualified name of the class under which the object will be exported | |||
ref | any bean name | Reference to the named bean to be exported as a service in the service registry. | |||
context-class-loader | unmanaged | service-provider | Defines how the context class loader will be managed when an operation is invoked on the
exported service. The default value is unmanaged which means that no management of
the context class loader is attempted. A value of service-provider guarantees that
the context class loader will have visibility of all the resources on the class path of
bundle exporting the service. | ||
auto-export | disabled | interfaces | class-hierarchy | all-classes | Enables Spring to automatically manage the set of service interfaces advertised for the
service. By default this facility is disabled . A value of interfaces advertises all
of the Java interfaces supported by the exported service. A value of class-hierarchy
advertises all the Java classes in the hierarchy of the exported service. A value of
all-classes advertises all Java interfaces and classes. |
ranking | any integer value | Specify the service ranking to be used when advertising the service. Default value is 0. |
The service defined by a service
element is
registered with the OSGi service registry when the application context
is first created. It will be unregistered automatically when the
bundle is stopped and the application context is disposed.
If you need to take some action when a service is unregistered
because its dependencies are not satisfied (or when it is registered),
then you can define a listener bean using the nested
registration-listener
element.
The declaration of a registration listener must use either the
ref
attribute to refer to a top-level bean
definition, or declare an anonymous listener bean inline. For
example:
<service ref="beanToBeExported" interface="SomeInterface"> <registration-listener ref="myListener" registration-method="serviceRegistered" unregistration-method="serviceUnregistered"/> <registration-listener registration-method="register"> <bean class="SomeListenerClass"/> </registration-listener> </service>
Listener declaration referring to a top-level bean declaration. | |
Indicate the | |
Declare only a | |
Nested listener bean declaration. |
The optional registration-method
and
unregistration-method
attributes specify the names
of the methods defined on the listener bean that are to be invoked
during registration and unregistration. A registration and unregistration
callback methods must have a signature matching one of the following formats:
public void anyMethodName(ServiceType serviceInstance, Map serviceProperties);
public void anyMethodName(ServiceType serviceInstance, Dictionary serviceProperties);
where ServiceType
can be any type compatible
with the exported service interface of the service.
The register callback is invoked when the service is initially registered at startup, and whenever it is subsequently re-registered. The unregister callback is invoked during the service unregistration process, no matter the cause (such as the owning bundle stopping).
Spring-DM will use the declared ServiceType
argument type
and invoke the registration/unregistration method only when a service of a compatible type
will be registered/unregistered.
serviceProperties
represents a map holding all the properties
of the registered/unregistered service. To preserve compatibility with the OSGi specification
this argument can be cast, if needed, to a java.util.Dictionary
.
While we discourage, it is possible to implement a Spring-DM specific interface, namely
org.springframework.osgi.service.exporter.OsgiServiceRegistrationListener
which avoids the need
to declare the registration-method
and unregistration-method
.
However, by implementing OsgiServiceRegistrationListener
, your code
becomes Spring-DM aware (which goes against the POJO philosophy).
It is possible for a listener to implement OsgiServiceRegistrationListener
interface and
declare custom methods. In this case, the Spring-DM interface methods will be called first, followed by the custom methods.
Spring Dynamic Modules supports the declaration of beans that represent services accessed via the OSGi Service Registry. In this manner references to OSGi services can be injected into application components. The service lookup is made using the service interface type that the service is required to support, plus an optional filter expression that matches against the service properties published in the registry.
For some scenarios, a single matching service that meets the
application requirements is all that is needed. The
reference
element defines a reference to a single
service that meets the required specification. In other scenarios,
especially when using the OSGi whiteboard
pattern, references to all available
matching services are required. Spring Dynamic Modules supports the
management of this set of references as a List
,
Set
collection.
The reference
element is used to define a
reference to a service in the service registry.
Since there can be multiple service matching a given description,
the service returned is the service that would be returned by a call to
BundleContext.getServiceReference
. This means that
the service with the highest ranking will be returned, or if there is
a tie in ranking, the service with the lowest service id (the service
registered first with the framework) is returned (please see Section 5
from the OSGi spec for more information on the service selection algorithm).
The interface
attribute identifies the service
interface that a matching service must implement. For example, the
following declaration creates a bean
messageService
, which is backed by the service
returned from the service registry when querying it for a service
offering the MessageService
interface.
<reference id="messageService" interface="com.xyz.MessageService"/>
Just like the service
declaration, when specifying
multiple interfaces, use the nested interfaces
element instead
of interface
attribute:
<osgi:reference id="importedOsgiService"> <osgi:interfaces> <value>com.xyz.MessageService</value> <value>com.xyz.MarkerInterface</value> </osgi:interfaces> </osgi:reference>
It is illegal to use both interface
attribute and
interfaces
element at the same time - use only one of them.
The bean defined by reference element implements all of the
advertised interfaces of the service that are visible to the bundle (called
greedy proxying).
If the registered service interfaces include Java class types (as
opposed to interface types) then support for these types is subject to
the restrictions of Spring's AOP implementation (see the Spring
Reference Guide). In short, if the specified interfaces are classes
(rather then interfaces), then cglib
library must be
available, and final
methods are not
supported.
The optional filter
attribute can be used
to specify an OSGi filter expression and constrains the service
registry lookup to only those services that match the given
filter.
For example:
<reference id="asyncMessageService" interface="com.xyz.MessageService" filter="(asynchronous-delivery=true)"/>
will match only OSGi services that advertise MessageService
interface and have the property named asynchronous-delivery
set to value true
.
The bean-name
attribute is a convenient
short-cut for specifying a filter expression that matches on the
bean-name
property automatically set when exporting a bean using the
service
element (see Section 6.1, “Exporting a Spring bean as an OSGi service”).
Consider the following exporter/importer declarations:
<bean id="messageServiceBean" scope="bundle" class="com.xyz.MessageServiceImpl"/> <!-- service exporter --> <osgi:service id="messageServiceExporter" ref="beanToBeExported" interface="com.xyz.MessageService"/>
<osgi:reference id="messageService" interface="com.xyz.MessageService" bean-name="messageServiceBean"/>
the name used with |
will match only OSGi services that advertise MessageService
interface and have the property named org.springframework.osgi.bean.name
set
to value defaultMessageService
. In short, this means finding all Spring-DM exported
beans that implement interface MessageService
and are named
defaultMessageService
.
The cardinality
attribute is used to
specify whether or not a matching service is required at all times.
A cardinality value of 1..1
(the default)
indicates that a matching service must always be available. A
cardinality value of 0..1
indicates that a
matching service is not required at all times (see section 4.2.1.6
for more details). A reference
with cardinality
1..1
is also known as a
mandatory service reference and, by default,
application context creation is deferred until the reference is
satisfied.
Note | |
---|---|
It is an error to declare a mandatory reference to a service that is also exported by the same bundle, this behavior can cause application context creation to fail through either deadlock or timeout. |
The depends-on
attribute is used to specify
that the service reference should not be looked up in the service
registry until the named dependent bean has been
instantiated.
The OSGi Service Platform Core Specification (latest
version is 4.1 at time of writing) does not specify what types and
resources are visible through the context class loader when an
operation is invoked on a service obtained via the service registry.
Since some services may use libraries that make certain assumptions
about the context class loader, Spring Dynamic Modules enables you
to explicitly control the context class loader during service
invocation. This is achieved using the option
context-class-loader
attribute of the
reference
element.
The permissible values for the
context-class-loader
attribute are:
client
- during the service invocation,
the context class loader is guaranteed to be
able to see types on the classpath of the invoking bundle.
This is the default option.
service-provider
- during the service invocation,
the context class loader is guaranteed to be
able to see types on the classpath of the bundle exporting
the service.
unmanaged
- no context class loader
management will occur during the service invocation
As a summary, the following table lists the reference
element
attributes names, possible values and a short description for each of them.
Table 6.2. OSGi <reference> attributes
Name | Values | Description | |||
---|---|---|---|---|---|
interface | fully qualified class name (such as java.lang.Thread ) | The fully qualified name of the class under which the object will be exported. | |||
filter | OSGi filter expression (such as ((asynchronous-delivery=true) ) | OSGi filter expression that is used to constrain the set of matching services in the service registry. | |||
bean-name | any string value | Convenient shortcut for specifying a filter expression that matches on the bean-name property that is automatically advertised for beans published using the <service> element. | |||
context-class-loader | client | service-provider | unmanaged | Defines how the context class loader is managed when invoking operations on a service
backing this service reference. The default value is client which means that the context
class loader has visibility of the resources on this bundle's classpath. Alternate
options are service-provider which means that the context class loader has visibility of
resources on the bundle classpath of the bundle that exported the service, and unmanaged
which does not do any management of the context class loader. | |
cardinality | 0..1 | 1..1 | Defines the required cardinality of the relationship to the backing service. If not specified,
the default-cardinality attribute will apply. A value is '1..1' means that a backing service
must exist (this is a mandatory service reference). A value of '0..1' indicates that it is
acceptable to be no backing service (an optional service reference). | ||
timeout | any positive long | The amount of time (in milliseconds) to wait for a backing service to be
available when an operation is invoked. If not specified, the default-timeout attribute will apply.
|
The bean defined by the reference
element
is unchanged throughout the lifetime of the application context
(the object reference remains constant). However, the OSGi service
that backs the reference may come and go at any time. For a
mandatory service reference (cardinality 1..1
),
creation of the application context will block until a matching
service is available. For an optional service reference
(cardinality 0..1
), the
reference bean will be created immediately, regardless of whether or
not there is currently a matching service.
When the service backing a reference
bean
goes away, Spring Dynamic Modules tries to replace the backing
service with another service matching the reference criteria. An
application may be notified of a change in backing service by
registering a listener
. If no matching service is
available, then the reference
is said to be
unsatisfied. An unsatisfied mandatory service
causes any exported service (service
bean) that
depends on it to be unregistered from the service registry until
such time as the reference is satisfied again. See
Section 6.5, “Relationship between the service exporter and service importer” for more information.
When an operation is invoked on an unsatisfied
reference
bean (either optional or mandatory),
the invocation blocks until the reference becomes satisfied. The
optional timeout
attribute of the
reference
element enables a timeout value (in
milliseconds) to be specified. If a timeout value is specified and
no matching service becomes available within the timeout period, an
unchecked ServiceUnavailableException
is
thrown.
Spring-DM can automatically convert a managed OSGi service to
service reference. That is, if the property into which a reference bean
is to be injected, has type ServiceReference
(instead of the service
interface supported by the reference), then the managed OSGi
ServiceReference
for the service will be injected
in place of the service itself:
public class BeanWithServiceReference { private ServiceReference serviceReference; private SomeService service; // getters/setters ommitted }
<reference id="service" interface="com.xyz.SomeService"/> <bean id="someBean" class="BeanWithServiceReference"> <property name="serviceReference" ref="service"/> <property name="service" ref="service"/> </bean>
Automatic managed service to | |
Managed service is injected without any conversion |
Note | |
---|---|
The injected ServiceReference is managed by Spring-DM and will change
at the same time as the referenced backing OSGi service instance.
|
Sometimes an application needs access not simply to any service
meeting some criteria, but to all services
meeting some criteria. Spring-DM allows the matching services may be held in a
List
or Set
(optionally sorted).
The difference between using a List
and a
Set
to manage the collection is one of equality.
Two or more services published in the registry (and with distinct
service ids) may be "equal" to each other, depending on the
implementation of equals used by the service implementations. Only one
such service will be present in a set, whereas all services returned
from the registry will be present in a list. For more details on collections,
see this
tutorial.
The set
and list
schema elements
are used to define collections of services with set or list semantics
respectively.
These elements support the attributes
interface
, filter
,
bean-name
, cardinality
, and
context-class-loader
, with the same semantics as for
the reference
element. The allowable values for the
cardinality
attribute are 0..N
and 1..N
.
A cardinality value of
0..n
indicates that it is permissible for their to
be no matching services. A cardinality value of
1..n
indicates that at least one matching service
is required at all times. Such a reference is considered a
mandatory reference and any exported services
from the same bundle (service
defined beans) that
depend on a mandatory reference will automatically be unregistered
when the reference becomes unsatisfied, and reregistered when the
reference becomes satisfied again.
The bean defined by a list
element is of type
java.util.List
. The bean defined by a
set
element is of type
java.util.Set
.
The following example defines a bean of type List
that
will contain all registered services supporting the
EventListener
interface:
<list id="myEventListeners" interface="com.xyz.EventListener"/>
The members of the collection defined by the bean are managed dynamically by Spring. As matching services are registered and unregistered in the service registry, the collection membership will be kept up to date. Each member of the collection supports the service interfaces that the corresponding service was registered with and that are visible to the bundle.
Spring-DM supports sorted collections as well, both for set and list.
It is possible to specify a sorting order using either the
comparator-ref
attribute, or the nested
comparator
element. The
comparator-ref
attribute is used to refer to a
named bean implementing java.util.Comparator
. The
comparator
element can be used to define an inline
bean. For example:
<set id="myServices" interface="com.xyz.MyService" comparator-ref="someComparator"/> <list id="myOtherServices" interface="com.xyz.OtherService"> <comparator> <beans:bean class="MyOtherServiceComparator"/> </comparator> </list>
To sort using a natural ordering instead of an explicit
comparator, you can use the natural-ordering
element inside of comparator
. You need to specify
the basis for the natural ordering: based on the service references,
following the ServiceReference
natural ordering
defined in the OSGi Core Specification section 6.1.2.3; or based on
the services themselves (in which case the services must be
Comparable
).
<list id="myServices" interface"com.xyz.MyService"> <comparator><natural-ordering basis="services"/></comparator> </list> <set id="myOtherServices"interface="com.xyz.OtherService"> <comparator><natural-ordering basis="service-references"/></comparator> </set>
Note | |
---|---|
For a sorted set, a SortedSet implementation will be created.
However, since the JDK API does not provide a dedicated SortedList interface,
the sorted list will implement only the List interface. |
All OSGi services imported by a Spring-DM service collection publish and are type-compatible with the classes
declared by the interfaces
property. However, some services might expose additional (optional)
classes that could be relevant to your application.
For these cases, Spring-DM collections offer a dedicated attribute called greedy-proxying
which
will cause the creates proxies to use all the classes advertised by the imported services, visible to the consuming
importing bundle. Thus, it is possible to cast the imported proxies to classes different then those specified in the
interfaces
. For example, with the following list definition:
<list id="services" interface="com.xyz.SomeService" greedy-proxying="true"/>
one can do the following iteration (assuming MessageDispatcher
type is imported by the bundle):
for (Iterator iterator = services.iterator(); iterator.hasNext();) { SomeService service = (SomeService) iterator.next(); service.executeOperation(); // if the service implements an additional type // do something extra if (service instanceof MessageDispatcher) { ((MessageDispatcher)service).sendAckMessage(); } }
Note | |
---|---|
Before using greedy proxies and instanceof statements, consider using a different
interface/class for your services which provides better
polymorphism
and is more
object-oriented. |
list
and set
elements support all the attributes available to
reference
element except the timeout
attribute.
See the following table as a summary of the list
and set
element
attribute names, possible values and a short description for each of them.
Table 6.3. <list>/<set> attributes
Name | Values | Description | |||
---|---|---|---|---|---|
interface | fully qualified class name (such as java.lang.Thread ) | The fully qualified name of the class under which the object will be exported. | |||
filter | OSGi filter expression (such as ((asynchronous-delivery=true) ) | OSGi filter expression that is used to constrain the set of matching services in the service registry. | |||
bean-name | any string value | Convenient shortcut for specifying a filter expression that matches on the bean-name property that is automatically advertised for beans published using the <service> element. | |||
context-class-loader | client | service-provider | unmanaged | Defines how the context class loader is managed when invoking operations on a service
backing this service reference. The default value is client which means that the context
class loader has visibility of the resources on this bundle's classpath. Alternate
options are service-provider which means that the context class loader has visibility of
resources on the bundle classpath of the bundle that exported the service, and unmanaged
which does not do any management of the context class loader. | |
cardinality | 0..N | 1..N | Defines the required cardinality of the relationship to the backing service. If not specified,
the default-cardinality attribute will apply. A value is '1..N' means that a backing service
must exist (this is a mandatory service reference). A value of '0..N' indicates that it is
acceptable to be no backing service (an optional service reference). | ||
comparator-ref | any string value | Named reference to a bean acting as comparator for the declaring collection. Declaring a comparator automatically makes the declaring collection sorted. | |||
greedy-proxying | true | false | Indicates whether the proxies created for the imported OSGi services will be generated using
just the classes specified (false ) or all the classes exported by the service and visible to
the importing bundle (true ). The default value is false . |
The table below lists the attributes available for the comparator/natural
sub element.
Table 6.4. collection <comparator> attributes
Name | Values | Description | |
---|---|---|---|
basis | service | service-reference | Indicate the element on which natural ordering should apply - service for considering
the service instance and service-reference for considering the service reference instead of the service. |
A collection of OSGi services will change its content during the lifetime of the application context since it needs to reflect the state of the OSGi space. As service are registered and unregistered, they will be added or removed from the collection.
While a reference
declaration will try to
find a replacement if the backing service is unregistered, the collection
will simply remove the service from the collection.
Like reference
, a collection with cardinality 1..N
is said to be mandatory while a collection with cardinality 0..N
is referred to as being optional.
If no matching service is available then only mandatory collections become
unsatisfied.
That is if no service is available invoking an operation on:
ServiceUnavailableException
.Just like reference
, mandatory collections
will trigger the unregistration of any exported service that depends
upon it. See
Section 6.5, “Relationship between the service exporter and service importer” for more information.
The recommend way of traversing a collection is by using an Iterator
.
However, since OSGi services can come and go, the content of the managed service collection will be adjusted
accordingly. Spring-DM will transparently update all Iterator
s held by
the user so it is possible to safely traverse the collection while it is being modified. Moreover, the
Iterator
s will reflect all the changes made to the collection, even if
they occurred after the Iterator
s were created (that is during the iteration).
Consider a case where a collection shrinks significantly (for example a big number of OSGi
services are shutdown) right after an iteration started.
To avoid dealing with the resulting 'dead' service references,
Spring-DM iterators do not take collection snapshots (that can be inaccurate)
but rather are updated on each service event so they reflect the latest collection state,
no matter how fast or slow the iteration is.
It is important to note that a service update will only influence Iterator
operations that are executed after the event occurred. Services already returned by the iterator will not be
updated even if the backing service has been unregistered. As a side note, if an operation is invoked on
such a service that has been unregistered, a ServiceUnavailableException
will be thrown.
To conclude, while a reference
declaration will search for candidates in case the
backing service has been unregistered, a service collections will not replace unregistered services returned
to the user. However, it will remove the unregistered services from the collection so future iterations will not
encounter them.
Please note that the Iterator
contract is guaranteed meaning that
next()
method always obey the result of the previous
hasNext()
invocation.
Table 6.5. Dynamic service collection Iterator
contract
hasNext() returned value | next() behaviour |
---|---|
true | Always return a non-null value, even when the collection has shrunk as services when away. |
false | per Iterator contract, NoSuchElementException is thrown.
This applies even if other services are added to the collection |
The behaviour described above, offers a consistent view over the collection even if its structure changes during iteration.
To simply refresh the iterator, call hasNext()
again. This will force the
Iterator
to check again the collection status for its particular entry in the iteration.
In addition, any elements added to the collection during iteration over a sorted collection will only be visible if the iterator has not already passed their sort point.
Whether you are using reference
or set
or list
, Spring Dynamic
Modules will manage the backing service. However there are cases
where the application needs to be aware when the backing service
is updated.
Such applications, that need to be aware of when the service
backing a reference
bean is bound and unbound, can
register one or more listeners using the nested
listener
element.
This element is available on both reference
and
set
, list
declarations.
In many respects, the service importer listener declaration
is similar to the service exporter listener declaration
(Section 6.1.7, “Service registration and unregistration lifecycle”).
The listener
element refers to a bean (either by name,
or by defining one inline)
that will receive bind and unbind notifications. If this bean
implements Spring-DM's
org.springframework.osgi.service.importer.OsgiServiceLifecycleListener
interface, then the bind
and
unbind
operations in this interface will be
invoked. Instead of implementing this interface (or in addition),
custom bind and unbind callback methods may be named.
An example of declaring a listener that implements
OsgiServiceLifecycleListener
:
<reference id="someService" interface="com.xyz.MessageService"> <listener ref="aListenerBean"/> </reference>
An example of declaring an inline listener bean with custom bind and unbind methods:
<reference id="someService" interface="com.xyz.MessageService"> <listener bind-method="onBind" unbind-method="onUnbind"> <beans:bean class="MyCustomListener"/> </listener> </reference>
If the listener bean implements the
OsgiServiceLifecycleListener
interface
and the listener definition specifies custom
bind and unbind operations then both the
OsgiServiceLifecycleListener
operation and the
custom operation will be invoked, in that order.
The signature of a custom bind or unbind method must be one of:
public void anyMethodName(ServiceType service, Dictionary properties); public void anyMethodName(ServiceType service, Map properties); public void anyMethodName(ServiceReference ref);
where ServiceType
can be any type. Please note that
bind and unbind callbacks are invoked only
if the backing service matches the type declared in the method signature(
ServiceType
). If you want the callbacks to be called
no matter the type, use java.lang.Object
as a
ServiceType
.
The properties
parameter contains the set of properties
that the service was registered with.
If the method signature has a single argument of type
ServiceReference
then the
ServiceReference
of the service will be passed to
the callback in place of the service object itself.
When the listener is used with a reference
declaration:
reference
becomes unsatisfied).When the listener is used with a collection declaration (set
or
list
):
Again note that service collections there is no notion of service rebind: services are added or removed from the collection.
Bind and unbind callbacks are made synchronously as part of
processing an OSGi serviceChanged
event for the
backing OSGi service, and are invoked on the OSGi thread that
delivers the corresponding OSGi
ServiceEvent
.
The table below lists the attributes available for the reference
listener
sub element.
Table 6.6. OSGi <listener> attributes
Name | Values | Description |
---|---|---|
ref | bean name reference | Name based reference to another bean acting as listener. |
bind-method | string representing a valid method name | The name of the method to be invoked when a backing service is bound. |
unbind-method | string representing a valid method name | The name of the method to be invoked when a backing service is bound. |
While the importer listener provides access to the OSGi service bound at a certain point, it is important to note that the given argument is not
the actual service but a proxy. This can have subtle side effects especially with regards to service class name
and identity. The reason behind using a proxy is to prevent the listener from holding strong reference to the service (which can disappear
at any point). Listeners interested in tracking certain services should not rely on instance equality (==
). Object equality
(equals
/hashcode
) can be used but only if the backing service has exposed the aforementioned methods
as part of its contract (normally by declaring them on a certain published interface/class). If these methods are not published, the proxy will invoke its own method, not the targets. This is on purpose since,
while the proxy tries to be as transparent as possible, it is up to the developer to define the desired semantics.
Thus, it is recommended (especially for reference
importers) to do tracking based on just the service interface/contract
(not identity), service properties (see org.osgi.framework.Constants#SERVICE_ID
) or service notification (bind/unbind).
It is sometime useful for an imported service to know which bundle is using it
at a certain time. To help with this scenarion, in Spring-DM imported services publish
the importing bundle BundleContext
through
LocalBundleContext
class. Each time a method on the importer is invoked,
the caller BundleContext
will be made available, using
a ThreadLocal
, through getInvokerBundleContext()
.
Please be careful when using this class since it ties your code to Spring-DM API.
As mentioned above, Spring-DM exporter and importer allow listeners to be used for receiving notifications on when services are bound, unbound, registered or unregistered. Below you can find some guidance advices when working with listeners:
java.util.Map
instead of java.util.Dictionary
class. The first is an interface while the latter is a deprecated, abstract class. To preserve compatibility, Spring-DM
will pass to the listeners a Map
implementation that can be casted, if needed, to a
Dictionary
.public class MyListener { void register(Object service, Map properties); void register(Collection dataService, Map properties); void register(SortedSet orderedDataService , Map properties); }
| |
| |
|
There are cases where an exporter/importer listener needs a reference back to the bean it is defined on:
<bean id="listener" class="cycle.Listener"> <property name="target" ref="importer" /> </bean> <osgi:reference id="importer" interface="SomeService"> <osgi:listener bind-method="bind" ref="listener" /> </osgi:reference>
Listener bean | |
Dependency listener -> importer | |
Importer declaration | |
Dependency importer -> listener |
The declaration above while valid, creates a dependecy between the listener
and the importer it is defined upon.
In order to create the importer, the listener
has to be resolved and created but in order to do that,
the importer called service
needs to be retrieved (instantiated and configured). This cycle needs to be broken
down so that at least one bean can be fully created and configured. This scenario is supported by Spring-DM
for both exporter and importers however, if the listener is defined as a nested bean, the cycle cannot be resolved:
<osgi:reference id="importer" interface="SomeService"> <osgi:listener bind-method="bind"> <bean class="cycle.Listener"> <property name="target" ref="importer" /> </bean> </osgi:listener> </osgi:reference>
OSGi service importer | |
Dependency between importer -> listener | |
Nested listener declaration | |
Dependency nested listener -> importer |
The example above will fail since service
bean cannot be initialized as it depends on the
listener. The same cycle was seen before but in this case there is subtle yet big different from
the container perspective - the listener is declared as a nested/inner-bean (hence the missing bean id
).
Inner beans have the same life cycle as their declaring parents and do not have any name. By definition, they are not tracked
by the container and are simply created on demand. Since the importer cannot be partially created and the nested listener cannot
be cached, the container cannot break the cycle and create the beans. While the two configurations shown above seem similar, one works
while the other does not. Another reason to not use cycles unless you really, really have to.
To conclude, if you need inside the listener to hold a reference to the exporter/importer on which the listener is declared,
either declare the listener as a top-level bean (as shown before) or consider doing dependency lookup.
However, the latter approach requires extra contextual information such as the BeanFactory
to use and the bean
name and is more fragile then dependency injection.
Note | |
---|---|
For those interested in the technical details, neither the exporter and importer cannot be partially initialized since
they require the application context |
The osgi
namespace offers two
global attributes for specifying default behaviours for all
importers declared in that file.
Thus, when using the osgi
namespace to enclose
set
, list
or
reference
elements, one can use:
default-timeout
-
can be used to specify the default timeout (in milliseconds) for all
importer elements that do not explicitly specify one. For
example:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:osgi="http://www.springframework.org/schema/osgi" osgi:default-timeout="5000"> <reference id="someService" interface="com.xyz.AService"/> <reference id="someOtherService" interface="com.xyz.BService" timeout="1000"/> </beans:beans>
Declare | |
Declare | |
This | |
This |
default-cardinality
-
can be used to specify the default cardinality for all
importer elements that do not explicitly specify one.
Possible values are 0..X
and 1..X
where X
is substituted at runtime to 1
for reference
elements or N
for collection types such as set
or list
.
Consider the following example:
<beans:beans xmlns="http://www.springframework.org/schema/osgi" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans" xmlns:osgi="http://www.springframework.org/schema/osgi" osgi:default-cardinality="0..X" default-lazy-init="false"> <reference id="someService" interface="com.xyz.AService"/> <set id="someSetOfService" interface="com.xyz.BService"/> <list id="anotherListOfServices" interface="com.xyz.CService" cardinality="1..N"/> </beans:beans>
Declare Spring Dynamic Modules schema as the default namespace. | |
Import Spring Framework beans schema and associate a prefix with its namespace
( | |
Import Spring Dynamic Modules schema and associate a prefix with its namespace
( | |
Declare | |
| |
The | |
The | |
The |
The default-*
attributes allow for concise and shorter declarations as well
as easy propagation of changes (such as increasing or decreasing the timeout).
An exported service may depend, either directly or indirectly,
on other services in order to perform its function. If one of these
services is considered a mandatory dependency
(has cardinality 1..x
) and the
dependency can no longer be satisfied
(because the backing service has gone away and there is no suitable
replacement available) then the exported service that depends on it
will be automatically unregistered from the service registry - meaning
that it is no longer available to clients. If the mandatory dependency
becomes satisfied once more (by registration of a suitable service),
then the exported service will be re-registered in the service
registry.
This automatic unregistering and re-registering of exported
services based on the availability of mandatory dependencies only
takes into account declarative dependencies. If exported service
S
depends on bean A
, which in
turn depends on mandatory imported service M
, and
these dependencies are explicit in the Spring configuration file as
per the example below, then when M
becomes
unsatisfied S
will be unregistered. When
M
becomes satisfied again, S
will be re-registered.
<osgi:service id="S" ref="A" interface="SomeInterface"/> <bean id="A" class="SomeImplementation"> <property name="helperService" ref="M"/> </bean> <!-- the reference element is used to refer to a service in the service registry --> <osgi:reference id="M" interface="HelperService" cardinality="1..1"/>
If however the dependency from A
on
M
is not established through configuration as shown
above, but instead at runtime through for example passing a reference
to M
to A
without any
involvement from the Spring container, then Spring Dynamic Modules
will not track this dependency.