As we have seen, the OSGi service mechanism greatly improves modularity and flexibility. But the programmatic API for registering and acquiring services is often challenging, as are the complexities of managing dynamic services. OSGi Release 4 introduced the Declarative Services (DS) specification to address these issues.
In Chapter 6, “Dynamic Services,” we introduced DS, and the subsequent chapters have assumed their use throughout Toast. This chapter presents a deep dive into the capabilities and use of the DS mechanism. In particular, we
• Recap the Declarative Services model and programming techniques
• Identify common usage scenarios for Declarative Services
• Discuss how to launch and debug an OSGi application that uses Declarative Services
• Take a look at the PDE tooling for Declarative Services
Working with the OSGi service model using the programmatic APIs can be complex and error-prone. In an attempt to simplify their code, developers tend to make optimistic assumptions regarding the availability of services, resulting in runtime exceptions at one extreme and object retention issues at the other.
In large-scale scenarios, using the programmatic API to work with the service model can also result in premature class loading and object instantiation, contributing to delayed application startup and unnecessary memory consumption.
An alternative is to be lazy and declarative. Equinox has supported such techniques for some time through mechanisms such as extension points and delayed bundle activation. DS brings laziness to the OSGi service model and simultaneously makes it much easier for developers to work with services, increasing the quality, scalability, and startup performance of their applications.
When using DS, a bundle declares components that can reference and provide services. A component is declared by a bundle in an XML document that is processed at runtime by OSGi’s Service Component Runtime (SCR) to create a component instance. A bundle can have multiple XML documents, and each document can contain multiple component declarations.
It is interesting to note that DS implementations are add-on bundles to the OSGi framework; that is, DS capabilities can be added to any R4.1 framework implementation—there is no need to build it into the framework.
Let’s review some of the most common design scenarios and how they are implemented with DS. We’ll start with the simplest component and then move to more typical scenarios of providing services, referencing services, and both referencing and providing services. Finally, we discuss some advanced scenarios, namely, component factories and the Whiteboard Pattern.
The following snippet shows how to declare the simplest possible component, the DS equivalent of “Hello, World”:
A component XML document must be XML 1.0–compliant and must be UTF-8 encoded. Each component XML document must be added to the bundle’s Service-Component
manifest header:
The value of this header is a comma-separated list of bundle-relative file name paths. The last segment of each path may include a wildcard, for example:
You must use only forward slashes as path separators. Backslashes are illegal, even if they are doubled up. If a file path does not refer to a component XML document, DS will log an error to the LogService
, if available. The Service-Component
header is ignored for fragment bundles, but a fragment bundle may contribute XML documents to its host. Clearly this is a case where using a wildcard in the file path is useful. The XML describes the component and the POJO class that implements its behavior. Storing component XML documents below the OSGi-INF
folder is a reasonable choice but is not required.
As the component does not provide any services, DS instantiates and activates it immediately. The component’s implementation class can be any public class that has a public default constructor. The component’s implementation class should already be familiar to you:
The implementation class can hook into its component’s lifecycle by adding two methods as shown in the following snippet:
The symmetry of the component class’s activate
and deactivate
methods is important in that it reflects the lifecycle of a component. In this example the activate
method is called immediately upon the component’s configuration being satisfied; then once the activate
method has been called, the deactivate
method is called when the component’s configuration is no longer satisfied, or when its hosting bundle stops. The concept of a component’s configuration being satisfied, or not, is discussed in Section 24.1.2 of Chapter 24, “Declarative Services Reference.”
Notice that the component’s implementation class is simply an Object
—it does not have to extend or implement another type. DS discovers the component implementation class’s activate
and deactivate
methods using reflection. While activate
and deactivate
are the default method names, you can specify different methods by setting the <component>
element’s activate
and deactivate
attributes. In this way DS adapts to work with your POJO class rather than the other way around. The tutorial chapters in Part II often do this, using the method names startup
and shutdown
, since they’re more POJO-friendly than activate
and deactivate
. The next section also includes an example of this.
A component can reference services by specifying nested <reference>
elements. A component that references other services is activated only when every reference is satisfied based on its policy and cardinality.
In the following example, the <reference>
elements do not specify the policy
and cardinality
attributes. This implies that the default policy of static
and the default cardinality of 1..1
should be used. As such, an IEmergencyMonitor
service and an ICrustShell
service must be available before the component’s configuration is satisfied and the component can be activated.
When a referenced service is available, DS binds it to the component’s implementation by calling the method named in the <reference>
element’s bind
attribute. Likewise, when a reference service becomes unavailable, DS unbinds it from the component’s implementation by calling the method named in the <reference>
element’s unbind
attribute.
Both the bind
and unbind
attributes are optional, and as this snippet shows, unbinding is often not necessary, such as when it involves simply setting a field to null
. Specifying the unbind
attribute is necessary only when there is real work to be done, such as removing a listener from the soon-to-be-unbound referenced service. Of course, specifying an unbind
attribute can assist with debugging since it gives you a place to log a message or set a breakpoint and observe that the referenced service has been unbound.
When using the static
policy, the bind
method is always called before the component is activated, and the unbind
method is always called after the component is deactivated.
Once the component’s configuration is satisfied, DS activates it by instantiating the EmergencyScreen
implementation class and calling its startup
method. When the component’s configuration is no longer satisfied, or when the bundle stops, DS deactivates the component by calling the EmergencyScreen
’s shutdown
method:
When referencing a service, a component can specify one of four cardinality values to describe how many referenced services are required and desired. Similarly, they can specify one of two policy values to describe how the component handles referenced service changes. While these combinations result in eight possible pairings, the two most common parings are
1..1
and static
—It is not a coincidence that these are the default attribute values and the pairing that you’ll use most often. With a cardinality of 1..1
and a static
policy, the referenced service is bound before the component is activated, and the component is deactivated before the referenced service is unbound. Toast uses these values in all but a couple of cases.
0..n
and dynamic
—The 0..n
cardinality implies that the referenced service is optional and multiple, so the dynamic
policy makes sense—you don’t want the activated component to be deactivated before a referenced service is bound and the component reactivated. And you don’t want the activated component to be deactivated before one of the many referenced services is unbound and the component reactivated. Toast uses these values in the back end portal’s service action lookup component.
Again, since in our example the <reference>
elements do not specify the policy
and cardinality
attributes, the defaults of static
and 1..1
are used.
In addition to referencing OSGi services, DS components can provide services to the OSGi service registry. This is done by listing the provided services in the component’s XML document and having the component class implement each provided service. The following snippet shows an example from the GPS bundle:
Notice that the <provide>
element is nested within the <service>
element and that each element describes a single provided service. The component provides the IGps
service, so the implementation class FakeGps
must implement IGps
, and DS will register the component object as an IGps
service.
While a component can have only one implementation class, it can provide multiple services. To support this, the component class must implement each of the provided service interfaces, and each service must be described by a <provide>
element. Note that the same component implementation instance will be registered for each provided service.
It is common for a component to both reference and provide services. This is effectively a merge of the previous two scenarios. The component lifecycle ensures that a component’s services are provided only while its configuration is satisfied. By default a component’s implementation class is instantiated and the component activated on the first use of a provided service; that is, the component is lazily instantiated when it is referenced. The following snippet shows this in action:
The emergency component references the services IGps
, IAirbag
, and IChannel
and provides the service IEmergencyMonitor
, which is implemented by the component’s implementation class.
In an OSGi application it is common for DS components to provide services. Such components are considered lazy, and DS delays the loading and instantiation of each component’s implementation class until one of its provided services is requested. The performance and scalability benefits that this brings are compelling reasons for using DS. But not all components can or want to be lazy.
Most applications include a few components whose implementation class must be eagerly loaded and instantiated. DS calls these immediate components. Immediate components often reside at the top of the food chain, while others need to perform initialization behavior before the first service request, or are independent of services altogether.
To request that DS treat a component as immediate, set the <component>
element’s immediate
attribute to true
. DS will respect this request unless the component is a factory component; see Section 15.2.7, “Factory Components,” for more details. Setting the immediate
attribute to false
never affects the immediacy of a component.
The Toast component org.equinoxosgi.toast.swt.emergency
is an example of an immediate component. Once the implementation class EmergencyScreen
has been instantiated, it registers itself with the ICrustShell
. Since this component does not provide any services, it must be immediate since there is no way for it to be activated otherwise.
The Toast component org.equinoxosgi.toast.backend.portal
is another example of an immediate component. Once the implementation class Portal
has been instantiated, it creates and registers an instance of the PortalServlet
class as a servlet with the org.osgi.service.http.HttpService
. This component’s only interaction is via HTTP, so being lazy is not an option.
Other examples of immediate components include those that wish to start a thread, add a listener to a referenced service, communicate with hardware or an external system, or simply need the chance to perform initialization behavior prior to the first request for a provided service.
Immediate components can present a performance and scalability risk to an application since they often cause other components to be activated, classes to be loaded, and objects to be created earlier than is absolutely necessary.
As an alternative to the traditional Observer Pattern, the Whiteboard Pattern1 has been proposed for use in OSGi applications. The Whiteboard Pattern is not inherently OSGi-specific and could be implemented without OSGi, but it does require a publish/subscribe mechanism through which interested parties are discovered. The OSGi service registry is perfect for this.
1. See www.osgi.org/wiki/uploads/Links/whiteboard.pdf.
With the Whiteboard Pattern, the event source bundle and the event listener bundles are completely decoupled via the OSGi service registry: An event listener expresses its interest in the event source by providing a service, and an event source then discovers the event listener services through which change events are dispatched.
In addition to the loose coupling of the event source and the event listeners, the virtues of the Whiteboard Pattern include a simplified implementation of both the event source and the event listeners. There is no need for the event source to maintain a list of event listeners, and each event listener needs no knowledge of the event source. The OSGi service registry takes care of this by maintaining the set of interested event listener services and notifying the event source when the services are changed.
An additional benefit is the reduced possibility of object retention caused by an event listener neglecting to unregister its interest in the event source; the OSGi framework guarantees that all services are automatically unregistered when the registering bundle stops. Poorly coded event sources can, however, still incorrectly retain references to uninterested event listeners.
Despite these improvements over the Listener Pattern, there are some disadvantages of the Whiteboard Pattern:
• The OSGi service registry consists of a single namespace, making the set of event listeners global.
• Without care the application can become unnecessarily coupled to the OSGi framework.
• Given the active nature of the service registry, applications with many listeners may not scale well.
Since event listeners are services and the OSGi service registry maintains a global, flat list of services, listeners will hear all events regardless of the source. Of course, this can be highly desirable, but it can also unnecessarily complicate system configuration.
Domain objects using the Whiteboard Pattern typically rely on the OSGi service registry to maintain the event listeners. This is counter to the POJO and dependency injection approach that we have used throughout this book. In particular, it inhibits reuse in non-OSGi scenarios and complicates testing.
Of course, this coupling can be avoided by introducing the notion of a lookup mechanism and then supplying a service-based implementation. This allows us to retain the benefits of the Whiteboard Pattern while allowing the code to run without the OSGi framework.
We saw an example of this in Chapter 13, “Web Portal,” where the back end Portal
registers the PortalServlet
that handles HTTP requests by dispatching to a matching IPortalAction
. In this way the portal servlet is easily, and infinitely, extendable. Here we take a closer look at the setup, starting with the back end portal component:
The back end portal is an immediate DS component whose implementation class, Portal
, creates the PortalServlet
and registers it with the HttpService
.
When the PortalServlet
is created, it is given an IActionLookup
service. The PortalServlet
handles an HTTP request by locating an IPortalAction
using its IActionLookup
service and then executing it. The IActionLookup
service has a getAction
API that returns the IPortalAction
object matching a specified ID. The code is roughly as follows:
This action lookup behavior is defined by the IActionLookup
service and is completely independent of OSGi. To hook in OSGi services using the Whiteboard Pattern, we have a ServiceActionLookup
class that implements the IActionLookup
interface in terms of OSGi services using the Whiteboard Pattern. This action lookup service is then injected into the Portal
and used to handle portal web requests. The next snippet shows the service-based action lookup mechanism. The code looks a little complex but is reasonably straightforward.
The PortalAction
inner class is a wrapper for user-supplied actions to ensure that the real action services are not accessed until required. Given the lazy nature of DS components, this defers the instantiation of DS-supplied IPortalAction
services. The rest of ServiceActionLookup
implements a cache of PortalAction
wrappers and the required getAction
method.
Notice that the addAction
method takes a ServiceReference
argument rather than an IPortalAction
. This is the key to delaying the loading and instantiation of each bound IPortalAction
until it is needed by the PortalServlet
to fulfill an HTTP action request.
To hook this into DS using the Whiteboard Pattern, we need a component with a <reference>
element for the IPortalAction
service and have it use the dynamic
policy and the 0..n
cardinality. This allows any number of IPortalAction
services to be referenced by the component. As IPortalAction
services are registered, the ServiceActionLookup
is notified by DS calling addAction
, which creates and caches a corresponding PortalAction
that wraps the ServiceReference
. As action services are unregistered, they are unbound from the ServiceActionLookup
component via removeAction
. The component XML required to describe this is shown in the following snippet:
The <reference>
element for the IPortalAction
service uses a cardinality
of 0..n
because many services are expected but none are required. In this case using a policy
of dynamic
makes sense since we do not want changes in the available IPortalAction
services to affect the activation of the ServiceActionLookup
component.
While this certainly requires more code and is not quite how you would write your POJO servlet, it is very reasonable when you are trying to make the set of portal actions extensible while decoupling your business logic from OSGi.
The following snippet shows how the tracking action is contributed to the portal using DS by providing an IPortalAction
service:
The implementation of the tracking action is as follows:
So far we have discussed how to use DS to describe components that are statically and automatically created once their configuration has been satisfied. When components need to be created dynamically, or when multiple instances of a component are needed, a DS factory component should be used. A factory component implicitly provides an org.osgi.service.component.ComponentFactory
service that is used by other bundles and components to create and dispose of instances of the component.
To help explain factory components, we show you how to declare an airbag factory component for Toast that allows multiple airbags of varying kinds that reside throughout the vehicle to be dynamically configured.
To keep things simple, the scenario presented here starts with the code in the Samples Manager for Chapter 6, “Dynamic Services,” and walks you through editing the org.equinoxosgi.toast.dev.airbag
bundle to make its DS component a factory component. We then implement a second DS component that configures Toast using the factory-component-provided ComponentFactory
service to create six distinct airbag components.
While all cars have airbags hidden within the steering column and behind the front console, it’s common these days to also see curtain airbags. Let’s start by enhancing the Toast domain logic to support different kinds of airbags, mounted on the left and the right, throughout the vehicle.
The new APIs getKind
, getOrientation
, and getRow
allow us to query the type of airbag we have and where it is mounted in the vehicle. The KIND_*
and ORIENTATION_*
constants are intended to be used to set the state of IAirbag
instances. The PROPERTY_*
constants will be used to parameterize the creation of IAirbag
instances. The use of these constants will become clear as we proceed through the example.
Now that we’ve enhanced the IAirbag
interface, it is necessary to update the FakeAirbag
implementation. To support airbags of varying characteristics, we have added three fields (one each for kind
, orientation
, and row
) and the methods to satisfy the new IAirbag
interface APIs. We have also overridden the toString
method to allow a FakeAirbag
to describe itself appropriately on the console or the log. Besides these domain logic changes, the most important changes are to the FakeAirbag
’s startup
method:
Remember, the startup
method is the component’s activation method. The declaration of the startup
method has been changed to take a Map
argument, which is called by DS at runtime when the component’s configuration is satisfied and activated. At that time DS will pass in the FakeAirbag
’s properties as created by the airbag factory component. This is discussed in the next section. The Map
contains keys such as those defined by the IAirbag
constants PROPERTY_KIND
, PROPERTY_ORIENTATION
, and PROPERTY_ROW
.
The existing airbag DS component needs to be updated to change it to a factory component. The <component>
element has an optional factory
attribute that is used to identify the component as a factory component.
When this attribute is set, DS ignores the <component>
and instead registers a ComponentFactory
service through which instances of the component can be manufactured. This is rather subtle and is one of the most confusing aspects of the DS component schema. When declaring a factory component, you can think of the <component>
element as a blueprint for what the factory will manufacture.
While uniqueness is not required or enforced for the <component>
element’s factory
attribute, it is certainly recommended since this is used later by others that need to locate the ComponentFactory
service in the OSGi service registry.
We have chosen to use the fully qualified IAirbag
interface name as the component’s factory
identifier. Not only does this uniquely identify the factory, but as the factory will be manufacturing components that provide an IAirbag
service, it certainly appears to be intention-revealing.
We have also added a <property>
element that defines the default value for the kind
property of the airbag components that the factory manufactures. We’ll see shortly how this is used.
The factory component in this example happens to include a <service>
element since it provides an IAirbag
service. This is not a necessary part of being a factory component. It is perfectly legal for the components manufactured by a factory component to be immediate, providing no services of their own.
ComponentFactory
ServiceDS automatically registers a factory component’s ComponentFactory
service with the following properties:
component.name
—The value of this property is defined by the <component>
element’s name
attribute, which in this example is org.equinoxosgi.toast.dev.airbag
.
component.factory
—The value of this property is defined by the <component>
element’s factory
attribute, which in this case is org.equinoxosgi.toast.dev.airbag.IAirbag
; this is the type of the service provided by the component.
Since there can be many registered ComponentFactory
services, these properties serve to identify this particular ComponentFactory
service, ideally uniquely. Bundles and components that wish to use a ComponentFactory
service can use these properties to select the one they want.
ComponentFactory
ServiceSo far we’ve seen how to declare the airbag factory component, but we’ve still not seen how to use its provided ComponentFactory
to dynamically create multiple parameterized airbag components. For this we need a new DS component that is responsible for using the ComponentFactory
to configure the Toast airbag components—we call it the configurator for short. The configurator.xml
file is as follows:
The first thing to notice about the configurator component is that it does not provide any services and is an explicitly immediate component. DS activates immediate components as soon as their configurations are satisfied, which for the configurator means once its referenced ComponentFactory
service is available.
The configurator has two responsibilities: When activated, create Toast’s airbag components via the airbag ComponentFactory
service, and when deactivated, dispose of the airbag components it previously created.
Since DS registers a distinct ComponentFactory
service for each factory component it finds, the configurator component cannot use just any ComponentFactory
! By specifying the <reference>
element’s target
attribute, it is able to select the ComponentFactory
service with a component.factory
property of org.equinoxosgi.toast.dev.airbag.IAirbag
.
The configurator component’s implementation is the AirbagConfigurator
class, which for simplicity resides in the org.equinoxosgi.toast.dev.airbag
bundle—this gives the effect of the bundle configuring itself. Of course, this behavior could equally well reside in a separate bundle that is responsible for configuring all Toast components using all manner of ComponentFactory
services.
Since the AirbagConfigurator
class uses the OSGi-defined ComponentFactory
interface, the bundle must import the package org.osgi.service.component
.
The startup
method is the component’s activation method. This method calls its private createComponent
method six times to create six distinct airbag components. Each call to createComponent
returns an OSGi-defined ComponentInstance
object that represents an airbag component. These objects are cached in a field for later use.
The createComponent
method is simply a helper that constructs a Dictionary
and populates it with the properties to be passed to the ComponentFactory
’s newInstance
method. The newInstance
method dynamically creates an airbag component, passing along instance-specific properties that DS delivers to the FakeAirbag
’s startup
method upon activation. These properties override any properties declared by the factory component’s XML, and since a kind
property is declared in its XML, this property is therefore optional.
The AirbagConfigurator
’s shutdown
method, which is the component’s deactivation method, does the reverse of the startup
method: It iterates through the list of components, calling dispose
on each ComponentInstance
. The dispose
method causes DS to deactivate the airbag component represented by the ComponentInstance
and call the FakeAirbag
’s shutdown
method. Finally, the AirbagConfigurator
’s shutdown
method sets the components
field to null
, which while not strictly necessary allows the method to preserve symmetry with the startup
method.
When the org.equinoxosgi.toast.dev.airbag.configurator
component is activated, it creates six airbag components using the ComponentFactory
service provided by the org.equinoxosgi.toast.dev.airbag
factory component.
Stopping the org.equinoxosgi.toast.dev.airbag
bundle using the console’s stop
command demonstrates how the airbag component factory correctly disposes of airbag components when deactivated. Likewise, using the start
command to restart it causes the airbag components to be manufactured again.
Launching an application that uses DS is like launching any other OSGi application—you just need a few extra bundles. In particular, ensure that the following three bundles are in your launch configuration:
org.eclipse.equinox.ds
—The DS implementation
org.eclipse.equinox.util
—Utilities used by the DS implementation
org.eclipse.osgi.services
—The OSGi standard API
Bundles that use DS often do so entirely via XML and do not specify a static dependency upon the DS implementation bundle org.eclipse.equinox.ds
. This makes it easy to forget to include this bundle in the launch configuration. It is also important to ensure that the org.eclipse.equinox.ds
bundle is started, since only then will it detect bundles that use DS and process their XML.
While we do not recommend that you use start levels, using start levels with DS bundles requires special care. Equinox uses a default start level of 4, so if your DS bundles need a start level lower than 4, let’s say 3, you must remember to set the org.eclipse.equinox.ds
bundle’s start level to 3 or lower to ensure that your bundle’s DS components are processed early enough. If you forget to do this, they will be processed at start level 4 after the org.eclipse.equinox.ds
bundle is started. For this reason some people simply set the org.eclipse.equinox.ds
bundle’s start level to 1 just to be safe.
Given the loose coupling and laziness provided by DS, there can be some additional debugging problems. In particular, problems with parsing XML and other service binding issues can lead to many late-night debugging frustrations. Fortunately, the Equinox DS implementation has a helpful debugging flag that echoes all error messages to the console. Set the following VM argument when you launch your application:
-Dequinox.ds.print=true
The DS specification says that errors encountered while parsing and processing the component XML documents must be written to the LogService
, if available. To use this, install and start the Equinox log bundle, org.eclipse.equinox.log
. This bundle registers a memory-based LogService
and a LogReaderService
that supports the reading of logged events. You can also use the Equinox console’s log
command to dump recent log events to the console.
Equinox’s DS implementation registers a CommandProvider
service that extends the available console commands. The following Service Component Runtime commands are available that are helpful for controlling, understanding, and debugging a DS application:
list [-c] [bundle id]
—List all components, or the components that belong to the bundle with the specified bundle ID. Use -c
to display complete component information. Using –c
and a bundle ID is useful for debugging a particular bundle. The short form is ls
.
component <component id>
—Display the details of the component with the specified component ID. Use the list
command without parameters to display all components and their component IDs. The short form is comp
.
enable <component id>
—Enable the component with the specified component ID. The short form is en
.
disable <component id>
—Disable the component with the specified component ID. The short form is dis
.
enableAll [bundle id]
—Enable all components. Specify a bundle ID to enable only the components that belong to a particular bundle. The short form is enAll
.
disableAll [bundle id]
—Disable all components. Specify a bundle ID to disable only the components that belong to a particular bundle. The short form is disAll
.
Having been introduced to Declarative Services, seen some of the common usage scenarios, and started to learn about the component XML, you’ll be pleased to know that the Eclipse PDE provides some excellent DS tooling. The PDE tooling includes a DS component definition wizard for generating an initial component XML document and an editor for working with components.
The component wizard generates a component XML document after gathering details such as its location and file name, the name of the component it describes, and the component’s implementation class name.
In addition, the wizard updates the ServiceComponent
header in the bundle’s manifest to reference the component XML document. This is particularly helpful since it’s easy to forget, and DS cannot find your component without it.
Figure 15-1 shows the Overview page of the component definition editor. The Component section of the page is used to configure general component settings such as its name, implementation class, and lifecycle methods. Clicking the Class*: link opens the New Java Class wizard, providing a shortcut for creating the component’s implementation class; once created, the link provides a way to quickly navigate to the class. The Browse... button provides a way to pick an existing class.
The Options section is used for less common capabilities such as defining a factory component, setting the component’s configuration policy, and controlling its enablement and immediacy settings.
The Properties section is where single component properties and component property files are declared. For a single property the Edit... button allows the property name, type, and value to be edited, and for a properties file it allows the location and file name to be edited.
Figure 15-2 shows the Services page where referenced and provided services are defined. Each referenced service can be edited to set attributes such as its name, interface, cardinality and policy, bind and unbind method names, and its target.
Provided services are shown in the lower half of the Services page, and each can be edited to change its interface name.
Table 15-1 shows the various images that are used to decorate a component’s services.
Of course, there is also the Source page that supports text editing of component XML, but you’ll likely find that this is unnecessary.
All this is not to say that the tooling is perfect; having made its debut only in Eclipse 3.5, it is still maturing and will likely be enhanced with future releases. It will likely improve its validation of the component XML and its error reporting. While the form-based editors make composing a component easy, errors reported in the Problems view are currently displayed only as markers on the Source page of the editor.
Successfully building a service-oriented OSGi application requires that the bundle developer understands the dynamic nature of the OSGi service model. Until the release of Declarative Services in OSGi R4, it was a significant challenge to build an appropriately behaved application, even of moderate complexity. But with the introduction of Declarative Services it is now possible to build a scalable, dynamic, loosely coupled application from OSGi bundles.
In this chapter we have introduced Declarative Services and some of the common scenarios where it can be applied. We taught you enough to be productive with DS, and we discussed the PDE tooling that supports building DS components.
For a deep dive on the component XML schema and the DS component lifecycle, see Chapter 24, “Declarative Services Reference.”