In the previous article, Modular Java: Dynamic modularity describes how to bring dynamic modularity to applications by using Services (service). They are implemented by one (or more) of the output interfaces that can be dynamically discovered at run time. Although this approach makes the client and server completely decoupled, it brings up a question of how (when) to start the service.
Boot order
In a downright dynamic system, services can be loaded not only when the system is running but also in a different order. Sometimes, this is a big problem: regardless of the startup order of A and B, it's OK if no event (or thread) is present before the system is ready and ready to receive the event, which service starts first.
However, there are many situations that do not conform to this simple assumption. The classic example is logging: Typically, when a service starts up and does something else, it connects and starts writing the log. What happens if the log service is not available at this time?
The client should be able to handle the situation when the service does not exist, assuming that the service can be loaded dynamically at runtime. In this case, it may be smart to move to another mechanism (such as output to standard output), or be in a blocking state waiting for the service to be available (not a good answer for the logging system). However, it is impractical to make the service available before it is started.
Start level
OSGi provides a mechanism to control the order in which bundle is started, using the startup level (start levels). This concept is based on the UNIX run level concept: The system starts at level 1 and then monotonically increments until the target startup level is reached. Each OSGi container provides a different default target level: The Equinox default value is 6, and Felix is 1.
The startup level can be used to create a boot sequence between bundle, so that critical bundle services (such as logging) start at a lower level than those that need it bundle. However, because the available startup level values are limited and the installer prefers to select a single number as the startup level, it does not ensure that you can solve the problem only in the boot sequence.
Another point to note is that bundle with the same boot level are independently initiated (possibly in parallel), so if you have a bundle with the same boot level as the log service, no one can guarantee that the log service will be ready when needed. In other words, the startup level solves most of the problems, but it does not solve all the problems.
Declarative services
One solution to this problem is the declarative service for OSGi (hereinafter referred to as ds--declarative services). In this way, each component is organized by an external bundle and determines when they are available. Declarative services are organized together in an XML configuration file that describes what needs to be (consumed) or what services are provided.
In the last example of the previous article, we used Servicetracker to get the service and, if necessary, wait for the service to be available. It can be useful if we delay the creation of the shorten command until the shortening service is available.
The DS defines a component (component) concept that is more granular than bundle, but is more granular than the concept of a service (because one component can consume/provide multiple services). Each component has a name, corresponding to a Java class, and can be activated or invalidated by invoking the class's methods. Unlike the OSGi Java API, the DS allows the development of components with pure Java Pojo, and there is no need to rely on OSGi from the program. The spin-off benefit is that the DS is easier to test and simulate (Test/mock).
To illustrate this approach, we will continue to use the preceding example. We need two components: one is the shortening service itself, the other is the shortencomand that calls it.
The first task is to configure and register the Shorten service with the DS. Instead of registering the service through Bundle-activator, we can have the DS register it when the service starts.
So how does DS know who to activate and connect? We need to add an entry to bundle's manifest header, which indicates one (or more) XML component definition file.
Bundle-ManifestVersion: 2
...
Service-Component: OSGI-INF/shorten-tinyurl.xml [, ...]*
The contents of this Osgi-inf/shorten-tinyurl.xml component definition file are as follows:
<?xml version="1.0" encoding="UTF-8"?>
<scr:component name="shorten-tinyurl" xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0">
<implementation class="com.infoq.shorten.tinyurl.TinyURL"/>
<service>
<provide interface="com.infoq.shorten.IShorten"/>
</service>
</scr:component>
When the DS processes this component, its effect is context.registerservice with the code (Com.infoq.shorten.IShorten.class.getName (), New Com.infoq.shorten.tinyurl.TinyURL (), null); basically the same. The Trim () service requires a similar declaration, which is contained in the following source code.
A single component can provide multiple services based on different interfaces, if needed. A bundle can also contain multiple components, using the same or different classes, each offering a different service.