Chapter 15, “Declarative Services,” introduced DS and the common usage scenarios; this chapter dives deeply into the component XML schema to provide you with a better understanding of how to use DS and how to work with its component lifecycle. In particular, we
• Detail the Declarative Services XML schema v1.1.0, which is used to describe DS components
• Discuss the DS component lifecycle and how components interact with the dynamic changes of the OSGi service model
The element structure of a DS component XML document is relatively simple. Every DS component has an implementation class and may optionally define properties and identify services it references and provides. The element structure is summarized in Table 24-1 and is detailed in the following sections.
In Release 4, version 4.1, of the OSGi specification, the DS XML schema was defined by http://www.osgi.org/xmlns/scr/v1.0.0
. This is the schema used by Equinox 3.4. For Release 4, version 4.2, a new DS XML schema—http://www.osgi.org/xmlns/scr/v1.1.0
—was introduced. All DS components in this book use this schema.
By default a DS component uses the v1.0.0 schema. Since there are now multiple schemas, we recommend that you always define an XML namespace to use the schema with which your component complies. An XML namespace is typically defined using the <component>
element and the namespace scr
. For example:
The scr
namespace identifier must be specified only on the <component>
element. Specifying scr
on any nested elements will cause DS to report errors against the XML document.
Contributed XML documents can contain any number of <component>
elements nested at any level. An XML namespace must be used if you are declaring multiple components in a single document or are nesting a <component>
element in an XML document that uses a different namespace. To use the namespace, the root element must include a namespace declaration, and the recommended prefix for the namespace is scr
, for example:
xmlsn:scr="http://www.osgi.org/xmlns/scr/v1.1.0"
To declare multiple components in a single XML document, make sure that the root element is not a <component>
element and that all <component>
elements use the declared namespace, which in this case is scr
:
While it is possible to describe multiple components in a single XML document and to nest DS components inside documents with different XML schemas, the PDE tooling in Eclipse 3.5.x does not support this.
<component>
ElementEach <component>
element describes a single DS component. This element is required. The <component>
element has the following attributes:
name
—A bundle-unique name of the component. This attribute is optional, defaulting to the value of the component’s <implementation>
element’s class
attribute. If multiple components share an implementation class, setting this attribute is required to avoid duplicates.
The value of this attribute is used by other components within the same bundle that wish to enable or disable it using the ComponentContext
API; see the description of the enabled
attribute for details.
Depending on the value of the <component>
element’s configuration-policy
attribute, the value of this attribute may also be used as a framework-unique persistent identifier by the ConfigurationAdmin
service; see Section 24.2.1.2 for more on the configuration-policy
attribute.
activate
—The name of the implementation class method DS calls when the component’s configuration is satisfied and the component is activated. This attribute is optional, defaulting to activate
. See Section 24.2.2 for more on component activation.
deactivate
—The name of the implementation class method DS calls when the component’s configuration is no longer satisfied and the component is deactivated. This attribute is optional, defaulting to deactivate
. See Section 24.2.2 for more on component deactivation.
modified
—The name of the implementation class method DS calls when the ConfigurationAdmin
service’s Configuration
for the component has been modified. This attribute is optional, and there is no default value. Setting this attribute does not make sense if the configuration-policy
attribute has been set to ignore
. See Section 24.2.5.2 for more on configuration modification.
immediate
—Controls whether the component’s implementation class should be instantiated and the component activated immediately upon its configuration being satisfied. This attribute is optional. The default value of the attribute depends on other characteristics of the component:
true
—When the component does not provide any services and is not a factory component. In this case the immediate
attribute is implicitly true
and cannot be set to false
.
false
—When the component is a factory component. In this case the immediate
attribute is implicitly false
and cannot be set to true
.
It makes sense to explicitly set this attribute to true
only when the component provides a service and you do not want the component’s activation to be delayed until the first request for a provided service is received. It never makes sense to explicitly set this attribute to false
. See Section 24.2.4, “Component Immediacy,” for more on component immediacy.
enabled
—Controls whether the component is enabled upon creation. This attribute is optional, defaulting to true
. It never makes sense to explicitly set this attribute to true
.
A component can programmatically enable and disable other components within the same bundle using the ComponentContext
’s enableComponent
and disableComponent
methods and passing the component’s name
attribute as a parameter. See Section 24.2.1.1, “Component Enablement,” for more on component enablement and disablement.
configuration-policy
—Controls whether the component depends on the availability of a ConfigurationAdmin
service that has a Configuration
with a persistent identifier equal to the component’s name
attribute. This attribute is optional, defaulting to optional
. Legal configuration-policy
values are
require
—Use this value if the component’s configuration can be satisfied only when there is a ConfigurationAdmin
service that has a Configuration
for the component.
optional
—Use this value if the component’s configuration can be satisfied regardless of whether there is a ConfigurationAdmin
service and regardless of whether it has a Configuration
for the component.
ignore
—Use this value if the component does not wish to interact with the ConfigurationAdmin
service. A component that uses this value does not need its name
attribute to be a framework-unique persistent identifier but rather just a bundle-unique name.
factory
—The component’s factory ID. This attribute is optional. When this attribute is set, an org.osgi.service.component.ComponentFactory
service is registered. Ideally a component’s factory ID should uniquely identify the factory, but there is no requirement for this to be the case. A component factory cannot be an immediate component.
<implementation>
ElementThe <implementation>
element describes the Java class that implements the behavior for the component. This element is required.
class
—The fully qualified name of the class that implements the behavior of the component. This attribute is required. This must be a public, concrete class, with a default constructor, and must be a subtype of all the types described by the component’s <provide>
elements, if any. Section 24.1.7, “The <provide>
Element,” discusses this further.
<property>
ElementThe <property>
element describes a single property of the component. Component properties are accessible to the component instance and will be registered with every service provided by the component. This element is optional. See Section 24.2.5, “Component Properties,” for more on component properties.
name
—The name of the property. This attribute is required.
value
—The value of the property. This attribute is required unless the element’s body describes the value of the property; this is discussed later.
type
—The type of the property. This attribute is optional, defaulting to String
. This attribute dictates how the value is parsed. Legal values are
When using the value
attribute, the property will always be an object type as specified by the type
attribute. For example, specifying a value
attribute of 10
and a type
attribute of Integer
will result in an Integer
object. The following snippet declares two single-valued properties:
<property name="toast.devsim.host" value="localhost"/>
<property name="toast.devsim.port" value="8083" type="Integer"/>
While the value
attribute is used to describe a single property value, the body of the <property>
element is used to describe multiple property values, formatted one per line. In this case the property value is an array of the primitive types as specified by the type
attribute. For example, if the type
attribute is Long
, the property value is a long[]
. Multiple values of type String
are represented as a String[]
. The following declaration results in an int[]
property value:
It is not possible to externalize and translate a <property>
element’s value
attribute or body content.
<properties>
ElementThe <properties>
element describes properties of the component, as defined in a properties file. As with the <property>
element, these properties are accessible to the component instance and will be registered with each service provided by the component. This element is optional. See Section 24.2.5, “Component Properties,” for more on component properties.
entry
—A bundle-relative path to a properties file. This attribute is required. For portability reasons, favor forward slashes over backslashes as a path separator.
Rather than embedding the properties in the component XML document, this element identifies a file from which properties are loaded. All properties that are loaded using a <properties>
element are of type String
. When a bundle contains multiple components, it can be convenient to store common properties in a properties file that is loaded by each component.
<service>
ElementThe <service>
element describes the services to be registered with the OSGi service registry. This element is optional; however, if it exists, it must have at least one <provide>
nested element, as described in Section 24.1.7, “The <provide>
Element.”
servicefactory
—This attribute describes whether the component behaves as a service factory. This attribute is optional, defaulting to false
. A service factory is special in that a unique instance of the component’s implementation class is created for each bundle that requests any of its provided services, rather than sharing a single instance with all requesting bundles.
<provide>
ElementThe <provide>
element identifies a single Java type under which a component’s implementation is registered with the OSGi service registry. This element is required but only as a child of the optional <service>
element.
interface
—The fully qualified name of a Java type. Despite its name, the attribute’s value may be either an interface name or a class name. The component’s implementation class must always be a subtype of the Java type named by this attribute. This rule is enforced by DS at runtime when it instantiates the component’s implementation class.
<reference>
ElementThe <reference>
element describes a single prerequisite service of the component. This element is optional. A referenced service is satisfied when DS has acquired it from the OSGi service registry. Likewise, a referenced service is unsatisfied when it can no longer be acquired from the OSGi service registry.
name
—A component-unique name for the referenced service. This attribute is optional, defaulting to the value of the interface
attribute. The value of the attribute is used by the component’s implementation class to programmatically locate the referenced service via the ComponentContext'
s API. In the rare case where a component has multiple <reference>
elements with the same interface
attribute, it is necessary to explicitly set the name
attribute.
interface
—The fully qualified Java type of the referenced service. This attribute is required. The rules for this attribute follow those of the <provide>
element’s interface
attribute. Again, while it is legal to specify a Java class, you should strive to specify a Java interface.
bind
—The name of a method in the component’s implementation class that is used to bind the referenced service. This attribute is optional. See Section 24.2.3, “Accessing Referenced Services,” for more on binding referenced services.
unbind
—The name of a method in the component’s implementation class that is used to unbind the referenced service. This attribute is optional. See Section 24.2.3, “Accessing Referenced Services,” for more on unbinding referenced services.
cardinality
—The number of referenced service instances that the component must acquire before its configuration is satisfied. This attribute is optional, defaulting to 1..1
. There are only four legal values for this attribute:
0..1
—Optional and unary
0..n
—Optional and multiple
1..1
—Required and unary
1..n
—Required and multiple
See Section 24.2.1.3, “Acquisition of Referenced Services,” for more on referenced service cardinality values.
policy
—This attribute describes how changes in the referenced service are handled by the component. This attribute is optional, defaulting to static
. Legal values are
static
—When using this policy, the component’s implementation sees changes in referenced services only while deactivated. This means that if the component is activated, DS will deactivate the component before it sees the change. If the component’s configuration continues to be satisfied, DS will activate the component once more.
dynamic
—When using this policy, the component’s implementation dynamically sees changes in the referenced service. If the component is activated, DS will not deactivate the component before it sees the change. This policy requires the component’s implementation to be tolerant of dynamic changes to referenced services.
Using the static
policy ensures that a component’s activate
and deactivate
methods are not called asynchronously while a referenced service is being bound and unbound. This is not true when using the dynamic
policy.
target
—This attribute is an LDAP filter allowing for finer-grained selection of a referenced service. This attribute is optional. If your LDAP filter includes illegal XML characters, such as <
, >
, or &
, you must encode them. For example, use <
instead of <
, >
instead of >
, and &
instead of &
.
A component has a lifecycle that controls when it is activated and deactivated. A bundle must be started before DS can process its components. When a bundle is in the ACTIVE
state, or is in the STARTING
state and has its Bundle-Activation
manifest header set to lazy
, DS will parse its Service-Component
manifest header’s list of XML documents and creates the components described by each <component>
element.
For each component, DS ensures that its configuration is satisfied before activating it. DS deactivates an activated component when its configuration becomes unsatisfied, or when its hosting bundle is stopped. Unless the hosting bundle is stopped, a component is deactivated and reactivated as its configuration goes from being satisfied to being unsatisfied, to being satisfied once more. The details of how configurations are satisfied are discussed in the next section.
A component’s configuration is considered satisfied when
• The component is enabled
• If the <component>
element’s configuration-policy
attribute is set to require
, there is a registered ConfigurationAdmin
service that has a Configuration
with a persistent identifier equal to the <component>
element’s name
attribute
• All of the component’s referenced services have been acquired per their cardinality
attribute
You’ll recall from Section 24.1.8, “The <reference>
Element,” that the <-reference>
element’s cardinality
attribute describes whether the service is required or optional and how many instances of the service are required and desired by the component.
The first configuration constraint to be checked is whether the component is enabled. By default a component is enabled as soon as it is created. An enabled component’s configuration is managed by DS, whereas a disabled component’s is not and is effectively dormant. Most components are enabled by default and are never disabled.
Component enablement and disablement are useful when initialization behavior must be performed before a component is enabled, or when the component is enabled only while a condition remains true. For example, perhaps a roadside assistance component is enabled only after the billing component has queried a remote billing server to check that the driver has paid for roadside assistance.
The automatic enablement of a component can be suppressed by setting the <component>
element’s enabled
attribute to false
. Once disabled, a component can be enabled only via another ComponentContext
in the same bundle by calling that ComponentContext
’s enableComponent
method and passing as a parameter the name of the component to enable. Passing null
to the enableComponent
method enables all components in the bundle.
A component can disable another component by calling the ComponentContext
’s disableComponent
method and passing as a parameter the name of the component to disable. Unlike the enableComponent
method, it is not legal to pass null
to the disableComponent
method, and doing so will result in an exception being thrown.
The enableComponent
and disableComponent
methods execute asynchronously, meaning that they may return before the component’s enablement or disablement is complete.
ConfigurationAdmin
Service ConfigurationIf the <component>
element’s configuration-policy
attribute is set to require
, the component’s configuration is satisfied only while there is a registered ConfigurationAdmin
service that has a Configuration
with a persistent identifier that equals the <component>
element’s name
attribute.
If the <component>
element’s configuration-policy
attribute is set to optional
or ignore
, the satisfaction of the component’s configuration is unaffected by the availability of the ConfigurationAdmin
service or whether it has Configuration
for the component.
The third configuration constraint to be satisfied is the cardinality of each of the component’s referenced services. A referenced service’s cardinality is described using the <reference>
element’s cardinality
attribute.
A referenced service’s cardinality
consists of two values: a lower bound and an upper bound. As discussed in Section 24.1.8, “The <reference>
Element,” there are four cardinality
values: two that describe a required service, and two that describe an optional service. The two cardinality
values that describe a required service have a lower bound of 1
:
1..1
—The component uses exactly one service.
1..n
—The component uses one or more services.
The two cardinality
values that describe an optional service have a lower bound of 0
:
0..1
—The component uses at most one service.
0..n
—The component uses zero, one, or many services.
Choosing the cardinality
is a simple matter of answering two questions:
Is the service required or optional?
Required—Set the lower bound to 1
.
Optional—Set the lower bound to 0
.
Does the component use one or many instances of the service?
One—Set the upper bound to 1
.
Many—Set the upper bound to n
.
While the cardinality
that you choose applies only to a particular referenced service, it takes only a single unavailable required referenced service to cause the component’s configuration to remain unsatisfied and the component to not be activated.
The <component>
element’s activate
, deactivate
, and modified
attributes name the methods of the component implementation class that DS calls when the component is activated, deactivated, and modified.
Activation and modification methods can take zero or more arguments, where each argument is of one of the following types:
ComponentContext
—A ComponentContext
is similar to the bundle’s BundleContext
in that it provides an OSGi-defined API for querying and controlling the component and other components defined by the bundle.
BundleContext
—The hosting bundle’s BundleContext
.
Map
—An immutable Map
containing the component’s properties.
DS searches for activation and modification methods in the component’s implementation class in the following order:
1. A one-argument method that takes a ComponentContext
2. A one-argument method that takes a BundleContext
3. A one-argument method that takes a Map
4. A two-argument method that takes a ComponentContext
, BundleContext
, or Map
, in any order; if DS finds multiple matching methods, it will arbitrarily choose one
5. A zero-argument method
If upon calling the activate
method an exception is thrown, DS will log an error to the LogService
, if available, and the component is not activated.
Once a component is activated, it remains so until its configuration becomes unsatisfied or its defining bundle is stopped, at which time the component is deactivated.
Deactivation methods can take zero or more arguments, where each argument is of one of the following types:
ComponentContext
—Similar to the bundle’s BundleContext
in that it provides an OSGi-defined API for querying and controlling the component and other components defined by the bundle
BundleContext
—The hosting bundle’s BundleContext
Map
—An immutable Map
containing the component’s properties
int
or Integer
—The reason the component is being deactivated
DS searches for deactivation methods in the component’s implementation class in the following order:
1. A one-argument method that takes a ComponentContext
2. A one-argument method that takes a BundleContext
3. A one-argument method that takes a Map
4. A one-argument method that takes an int
5. A one-argument method that takes an Integer
6. A two-argument method that takes a ComponentContext
, BundleContext
, Map
, int
, or Integer
in any order; if DS finds multiple matching methods, it will arbitrarily choose one
7. A zero-argument method
When DS calls a deactivation method that takes an int
or an Integer
, one of the following deactivation reasons is passed as a parameter:
0
—Unspecified
1
—The component was disabled
2
—A reference became unsatisfied
3
—A configuration was changed
4
—A configuration was deleted
5
—The component was disposed
6
—The bundle was stopped
Throughout its lifetime a component may be activated and deactivated many times as its configuration becomes satisfied and unsatisfied. Each time a component is activated, a new instance of the component’s implementation class and the ComponentContext
is instantiated, and the component’s referenced services are bound.
The component’s activate
, deactivate
, and modified
methods will not be called asynchronously by DS. When the component has a <reference>
element that uses the dynamic
policy, DS will call its bind
and unbind
methods asynchronously to these methods.
The <reference>
element’s policy
attribute describes how the component will handle changes in the referenced service. There are two values for the policy
attribute:
static
—This policy ensures that once activated, the component does not see referenced service changes without being deactivated first. Once the component has been deactivated, it will be activated only when its configuration is satisfied once more. This is the default policy.
dynamic
—This policy allows the referenced service to change dynamically without deactivating the component first.
The decision regarding the policy
to choose is often influenced by the cardinality
of the referenced service. Generally speaking, the most common cardinality
and policy
pairings are
1..1
and static
—This is the default, simplest, and most common pairing. The component requires exactly one instance of the referenced service, is activated only when the service is available, and is deactivated when the service becomes unavailable.
0..1
and static
—Specifying a cardinality with a lower bound of 0
makes the referenced service optional. This is common in scenarios such as logging or where graceful fallback is possible. Note that the component author must carefully handle the case where a service is, or is not, present. The presence or absence of optional referenced services does not affect the activation of the component.
0..1
and dynamic
—This variation on the optional service requirement can be useful in highly fluid scenarios. The main difference is that the service can appear and disappear at any time. Pragmatically the coding patterns required here are complicated, so care should be taken.
0..n
and dynamic
—These settings are common in listener or Whiteboard Pattern cases, as discussed in Chapter 15, “Declarative Services.” The component will bind with any number of services and be told of their transitions without affecting the activation state of the component. See the Portal
component in Chapter 13, “Web Portal,” for an example.
When using the dynamic
policy, it is common to use bind
and unbind
methods to manage changes in the referenced services. For the multiple cardinalities (1..n
and 0..n
) this typically means that the referenced service is added to a collection when bound and removed from the collection when unbound. For the 0..1
cardinality, the referenced service is typically cached in a field when bound and the field is set to null
when unbound.
If you use the dynamic
policy with one of the unary cardinalities (0..1
and 1..1
), when a bound referenced service is unregistered, DS will always try to rebind a replacement service before it unbinds the current service. This can come as a big surprise since while the cardinality dictates that only a single referenced service is required, the component must be able to handle it being dynamically switched for another referenced service due to the cardinality constraints. For an example of this, see Section 17.2, “Using the LogService
in Toast.”
If you use the static
policy with one of the optional cardinalities (0..1
and 0..n
), the component’s configuration can be satisfied with zero referenced services. The static
policy dictates that once the component’s configuration is satisfied, it will not be deactivated and reactivated if a referenced service becomes available later. For this reason we suggest using the dynamic
policy with the optional cardinalities.
There are two strategies that a component can use to access its referenced services:
Event Strategy—Using this strategy, DS dispatches events to the component as the availability of its referenced services changes. Set the <reference>
element’s bind
and unbind
attributes to the name of the implementation class methods that DS calls to handle these events.
Lookup Strategy—Using this strategy, the <reference>
element’s bind
and unbind
attributes are typically not set. Instead, the component’s implementation looks up each referenced service using its ComponentContext
’s API.
It is not necessary for a component to use the same strategy for accessing all of its referenced services, since the strategy you pick is influenced by the referenced service’s cardinality
and policy
.
We recommend that you start by using the Event Strategy since it is simple, fits well with POJO APIs, and works well regardless of the referenced service’s cardinality
and policy
. The Toast application uses the Event Strategy in all but a few cases. For an example of the Lookup Strategy, see the org.equinoxosgi.toast.backend.portal
bundle where the PortalServlet
dynamically looks up IPortalAction
referenced services based on an HTTP request.
The Event Strategy allows referenced services to be mapped to the API of the component’s implementation class via the <reference>
element’s bind
and unbind
attributes. The signature of the bind
and unbind
method can be one of the following:
void <method>(<service-type>)
—This method takes a simple argument that is typed to the <reference>
element’s interface
attribute or one of its super-types. At runtime the parameter is the actual service object. This is the most commonly used signature.
void <method>(<service-type>, Map properties)
—This method’s first argument is as described in the previous signature. The second argument is a Map
that contains the referenced service’s registered properties.
void <method>(ServiceReference reference)
—This method delays the loading and instantiation of the referenced service’s class by taking a ServiceReference
argument.
DS searches for methods in the component’s implementation class in the following order:
1. Search for a single-argument method that takes a ServiceReference
.
2. Search for a single-argument method that is typed to the <reference>
element’s interface
attribute.
3. Search for a single-argument method that is typed to a super-type of the <-reference>
element’s interface
attribute. If DS finds multiple matching methods, it will arbitrarily choose one.
4. Search for a two-argument method, with the first argument typed as the <reference>
element’s interface
attribute and the second argument typed as a Map
.
5. Search for a two-argument method, with the first argument typed as a super-type of the <reference>
element’s interface
attribute and the second argument typed as a Map
. If DS finds multiple matching methods, it will arbitrarily choose one.
DS requires that the visibility of these methods be such that they are accessible to the component’s implementation class. We recommend that you always make these methods public
. We say this because they are necessary to use the class outside of OSGi and are really part of the component implementation class’s public API in a POJO context. Since these methods are never part of any provided service API, they will never be accessible to consumers of the provided service regardless of their visibility modifier.
Remember, it is desirable for your component implementation classes to remain pure POJOs, independent of OSGi APIs and mechanisms such as DS. For this reason, we recommend that you think carefully before defining bind
and unbind
methods that take a ServiceReference
argument; doing so creates a dependency upon OSGi.
When the bind
method takes a service instance argument, the referenced service’s implementation class clearly must have been loaded and instantiated before being passed as a parameter to the bind
method. By contrast, when the bind
method takes a ServiceReference
argument, the referenced service’s implementation class is neither loaded nor instantiated before the bind
method is called. This laziness is particularly valuable when using the multiple cardinalities, 0..n
and 1..n
, since a component often does not use every referenced service as soon as it is bound, but rather caches each ServiceReference
and selects one based on criteria such as the referenced service’s registered properties. The reference service’s class is loaded and instantiated when the ServiceReference
is dereferenced using the ComponentContext
’s API locateService(String, ServiceReference)
.
While the Event Strategy involves DS injecting services by binding and unbinding the component’s referenced services, the Lookup Strategy involves the component’s implementation querying DS for its referenced services.
Of the two strategies, the Lookup Strategy is generally considered the lazier since a referenced service is reified into a service object only when a request for it is made. When using this strategy, referenced services are looked up via the component’s ComponentContext
that provides the following API:
locateService(String name)
—This method locates a referenced service using the <reference>
element’s name
attribute.
locateService(String name, ServiceReference reference)
—This method locates a referenced service using the <reference>
element’s name
attribute and a ServiceReference
. The name
argument is necessary because a single ServiceReference
can represent multiple service types, so specifying the name
ensures that you locate a service object of the appropriate type. A component can obtain a ServiceReference
in a variety of ways, but the most common way is by using the Event Strategy and having the ServiceReference
dependency injected. When using this method, therefore, the Event Strategy and the Lookup Strategy are used together.
locateServices(String name)
—This method locates all the referenced services using the <reference>
element’s name
attribute. While this method can be used regardless of the referenced service’s cardinality
, it is most commonly used with a multiple cardinality
, namely, 0..n
and 1..n
.
The two locateService
methods return an Object
and the locateServices
method returns an Object[]
, so it is necessary to cast the services to their actual types before use. Care must be taken to ensure that the name
parameter passed to the methods is correct; otherwise an exception will be thrown when casting to the actual service type.
Since the ComponentContext
is available only while the component is activated, locating services is typically done from the component’s activate
method. This method should be typed to take a ComponentContext
argument. Unfortunately ComponentContext
is an OSGi class, and this makes it too easy to pollute a perfectly good POJO with an OSGi dependency. We recommend the following alternatives:
• Implement the component’s implementation class as an OSGi-dependent wrapper that delegates to a cached instance of the POJO. For this to work, the wrapper class must implement all the provided service interfaces and delegate to the cached POJO.
• Resign yourself to the fact that your component’s implementation class is OSGi-dependent and will never run as a POJO.
The concept of component immediacy is important to understand since it controls when a component is activated. Activation causes a component’s implementation class to be loaded and instantiated. One of the benefits that DS brings to the OSGi service model is lazy class loading and instantiation of the component’s implementation class. This can result in significant performance gains in terms of startup time and memory consumption.
An immediate component is activated as soon as its configuration is satisfied. By contrast, a delayed component has its activation delayed until one of its provided services is requested.
The <component>
element’s immediate
attribute is used to request the component’s activation characteristics, but it is important to remember that this attribute is merely a hint to DS rather than a demand. A component is immediate when
• It does not provide any services. DS considers the component to be implicitly immediate.
• Its immediate
attribute is explicitly set to true
.
There is no guarantee that DS will honor a component’s immediacy hint. Regardless of the value of its immediate
attribute,
• If the component is a factory component, it will not be immediate. A factory component must always have its component activation delayed since its purpose is to provide a service through which component instances are dynamically created and activated.
• If the component does not provide any services, and is not a factory component, it will always be immediate. Without any provided services, a component is at the top of the food chain and must therefore be activated immediately.
Since many components provide services, immediate components are rare. Examples of immediate components include
• A user interface component that needs to display a shell when the application starts.
• A component that registers servlets rather than services. A servlet must always be registered before an HTTP request for it is received.
• A component that performs asynchronous data collection from a device such as a thermometer, which may or may not provide services.
Remember that the activation of a component may be the cause of a potentially large chain of component activations, class loading, and instantiation of objects across the entire system. For performance reasons, care must be taken before making a component immediate. Since a well-designed OSGi application is composed of many finely grained components, it is important to understand the true cost of activating a single component.
A component’s properties can be set in three ways, in order of precedence:
• A component factory accepts properties passed via the ComponentFactory
’s newInstance
API that is used to create new component instances.
• If a <component>
element’s configuration-policy
attribute is set to either require
or optional
, the ConfigurationAdmin
service can be used to create and update a Configuration
for the component. See Section 24.1.2, “The <component>
Element.”
• Finally, a component’s properties can be set statically in the component’s XML document using <property>
and <properties>
elements. See Sections 24.1.4, “The <property>
Element,” and 24.1.5, “The <properties>
Element.”
This means that when a property is passed to a component by a component factory, it is not possible to configure the property via the ConfigurationAdmin
service, since the properties passed by the component factory will always override those of the configuration managed by ConfigurationAdmin
. Likewise, when a component is using ConfigurationAdmin
to manage its properties, updating its Configuration
will override the properties defined in the component’s XML document.
Regardless of how a component’s properties are set, they are presented to the component as a single immutable Dictionary
via its ComponentContext
’s getProperties
method. See Chapter 13, “Web Portal,” for an example of using properties.
A component’s properties are also registered as properties of any service that it provides. The exception to this rule is that properties whose name starts with a dot (“.
”) are considered private to the component and will not be registered with provided services.
ConfigurationAdmin
ServiceThe DS specification has always included close integration with the ConfigurationAdmin
service as a way of remotely and persistently configuring a component’s properties.
In v1.0.0 of the DS schema, DS always attempts to acquire the ConfigurationAdmin
service to look for a Configuration
with a persistent identifier equal to the <component>
element’s name
attribute. If a Configuration
is found, its properties are used to override those defined in the component’s XML declarations. In this context properties are defined as
• A property described by the <property>
element.
• A property described by a <properties>
element.
• Each referenced service’s target property as described by its <reference>
element’s optional target
attribute. Target properties are discussed in Section 24.2.5.3, “Referenced Service Target Properties.”
This is certainly a useful capability since otherwise there is no other way to configure a component’s properties, which are often statically declared in XML and for which there is no programmatic API that allows them to be changed.
In v1.1.0 of the DS schema the <component>
element attribute configuration-policy
was introduced, which allows a component to more accurately describe its integration with the ConfigurationAdmin
service. A component can now
• Require a ConfigurationAdmin
service and a Configuration
before its configuration is considered satisfied. If the component is a factory component and there is no ConfigurationAdmin
service and a Configuration
, the ComponentFactory
service will not be registered.
• Optionally use the ConfigurationAdmin
service with a matching Configuration
.
• Ignore the ConfigurationAdmin
service. In v1.0.0 of the DS schema every component interacted with the ConfigurationAdmin
service, if available. Now there is a way of opting out.
The <component>
element’s configuration-policy
attribute is discussed in Section 24.1.2, “The <component>
Element.”
In v1.0.0 of the DS schema, updates to a component’s Configuration
resulted in the component always being deactivated and reactivated regardless of the changes to the properties. In v1.1.0 of the DS schema, the <component>
element’s modified
attribute was added. This attribute can be set to the name of a method in the component’s implementation class that DS calls when the component’s Configuration
has been updated such that its configuration remains satisfied. The signature of the method named in this attribute is discussed in Section 24.2.2, “Component Activation, Deactivation, and Modification.”
As discussed in Section 24.1.8, “The <reference>
Element,” a <reference>
element has an optional target
attribute that is used to finely tune the referenced services that the component acquires. The target
attribute’s value is an LDAP filter that is used by DS to satisfy the constraints of the referenced service based on its registered properties, for example:
In this example the referenced service’s target
property is the LDAP filter (http.port=80)
. The component’s configuration will be satisfied only if an HttpService
is acquired that has an http.port
property with the value 80
. Recall that the <property>
and <properties>
elements are used by a component to describe the properties that will be registered with its provided services.
While a referenced service’s target
property is described statically in XML, it can be configured dynamically, just like any other component property, but by using its target property key. A target property key is the concatenation of the <reference>
element’s name
attribute and the suffix .target
, which would be http.target
in our example.
The Declarative Services XML schema is certainly not complicated, but to use DS effectively it is helpful to understand its subtleties. In this chapter we dived deeply into the schema to describe every element and attribute, as well as how the various attribute values affect components’ behavior.
We have also discussed the DS component lifecycle with the goal of teaching you to build flexible and pluggable applications composed from OSGi services. Toast is built entirely of DS components and services and uses DS to good effect. With the knowledge you have gained from Chapter 15, “Declarative Services,” and the discussion in this chapter, we hope that you’ll quickly be able to leverage the benefits of building applications composed of loosely coupled and highly cohesive components and services.