Modular Java: declarative Modularization

Source: Internet
Author: User
In article 4th of the modular Java series, we will introduce Declarative ModularizationDescribes how to define components and organize them together without relying on osgi APIs for programming.

In the previous article, modular Java: Dynamic modularization describes how to useService)It brings dynamic modularization to applications. They can be dynamically discovered at runtime through one or more output items.Interface. Although this method completely decouples the client and server, it brings about the problem of how to (when) start the service.

Startup sequence

In a dynamic system, services can be started not only during system operation, but also in different order. Sometimes, this is a big problem: No matterAAndBBefore the system reaches the ready state and is ready to receive events, if no event (or thread) appears, which service is started first will not be affected.

However, many situations do not conform to this simple assumption. The classic example is logging: Generally, when a service is started and other operations are performed, it needs to connect and start to write logs. If Log service is unavailable at this time, what will happen?

Assuming that the service can be dynamically loaded and unloaded during operation, the client should be able to cope with situations where the service does not exist. In this case, it may be able to intelligently move to another mechanism (such as output to standard output), or wait for the Service to be available in a blocked state (not a good answer for the logging system ). However, it is impractical to make the service available before it starts.

Startup level

Osgi provides a mechanism to control the order of bundle startup.Start levels). This concept is based on the Unix running level concept: the system starts at level 1 and increases monotonically until the target startup level is reached. Each osgi container provides a different default target level: equinox defaults to 6, and Felix is 1.

The startup level can be used to create the startup sequence between bundle, so that the startup level of key bundle services (such as logging) is lower than those that require it. However, because the available startup level values are limited, and the installer tends to select a single number as the startup level, it cannot ensure that you can solve the problem only by starting the order.

It is worth noting that bundle with the same startup level is independently started (possibly in parallel). Therefore, if you have a bundle with the same startup level as log service, no one can guarantee that the Log service is ready as needed. In other words, the startup level can solve most problems, but not all problems.

Declarative Service

One solution to this problem is osgi'sDeclarative Service(DS -- declarative services ). With this method, each component is organized by an external bundle to decide when they are available. Declarative services are organized together in an xml configuration file, which describes what services are required or provided.

In the last example of the previous article, we use servicetracker to obtain the service. If necessary, we need to wait for the Service to be available. It will be useful if we delay the shorten command creation until the shortening service is available.

DS defines a component concept, which is a more fine-grained concept than bundle, but is more granular than the concept of a service (because a component can consume/provide multiple services ). Each component has a name that corresponds to a Java class and can be activated or invalidated by calling the method of this class. Unlike osgi Java APIs, DS allows developers to develop components using pure Java pojo without relying on osgi from the program. The additional benefit is to make DS easier to test and simulate (test/mock ).

To illustrate this method, we will continue to use the previous example. We need two components: one is the shortening service itself, and the other is the shortencomand that calls it.

The first task is to configure and register the shorten service with DS. Instead of registering the service through bundle-activator.

How does DS know who to activate and connect? We need to add an entry to the bundle manifest header, which indicates one or more XML component definition files.

Bundle-ManifestVersion: 2 ... Service-Component: OSGI-INF/shorten-tinyurl.xml [, ...]* 

This OSGI-INF/shorten-tinyurl.xml component definition file contains the following:

<?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 DS processes this component, its effect is related to the Code context. registerservice (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 included in the source code below.

If needed, a single component can provide multiple services based on different interfaces. A bundle can also contain multiple components and use the same or different classes, each providing different services.

Consumption Service

To consume the service, we need to modify the shortencommand so that it is bound to an instance of the ishorten service:

package com.infoq.shorten.command;  import java.io.IOException; import com.infoq.shorten.IShorten;  public class ShortenCommand { private IShorten shorten; protected String shorten(String url) throws IllegalArgumentException, IOException { return shorten.shorten(url); } public synchronized void setShorten(IShorten shorten) { this.shorten = shorten; } public synchronized void unsetShorten(IShorten shorten) { if(this.shorten == shorten) this.shorten = null; } } class EquinoxShortenCommand extends ShortenCommand {...} class FelixShortenCommand extends ShortenCommand {...} 

Note: Unlike the previous one, this time it was not dependent on osgi APIS; mock is easy to verify whether it works normally. The synchronized modifier ensures that there is no competition in the service get/set.

To notify ds that the ishorten service instance needs to be bound to our equinoxshortencommand component, we need to define the required services. When DS instantiates your component (with the default constructor), it sets the ishorten service by calling the method defined in the BIND property (setshorten.

<?xml version="1.0" encoding="UTF-8"?>  <scr:component name="shorten-command-equinox" xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0">  <implementation class="com.infoq.shorten.command.EquinoxShortenCommand"/> <reference interface="com.infoq.shorten.IShorten" bind="setShorten"  unbind="unsetShorten"  policy="dynamic"
cardinality="1..1"
/> <service>
<provide interface="org.eclipse.osgi.framework.console.CommandProvider"/>
</service>
</scr:component>

Regardless of the Starting sequence of the bundle, once the ishorten service is available, the component will be instantiated and connected to the service. The policy, cardinality, and service content will be explained in the next section.

Policy and base

Policy can be set to static or dynamic. The static policy indicates that once set, the service will not change. If the service is unavailable, the component becomes invalid. If a new service appears, a new instance is created and the service is re-bound. This is obviously much harder than our local Update Service.

When the ishorten service changes, DS calls setshorten () for the new service, and then calls unsetshorten () for the old service ().

The reason why DS calls set before unset is to maintain service continuity. If unset is called before the service is replaced, the shorten service may be null for a short time. This is why the unset method also has a parameter, rather than setting the service to null.

The cardinality of a service is 1 .. 1 by default, and one of the following values can be obtained:

  • 0 .. 1Optional. A maximum of 1
  • 1 .. 1Mandatory, up to 1
  • 0. nOptional. Multiple
  • 1. nMandatory, multiple

If the base is not met (for example, set to force, but the shortening service is not used), the component is invalid. If multiple services are required, each service calls setshorten () once (). Instead, call unsetshorten () for each service to be uninstalled ().

The ability to customize each instance when the component enters the running state is not shown here.

In DS 1.1, the component element also has the activate and deactivate attributes, and the corresponding method is called during the component activation (start) and failure (STOP) processes.

Finally, this component provides an instance of the commandprovider service. This is a specific equinox service that allows console commands, which was previously implemented in the activator of bundle. The advantage of this mode is that the commandprovider service is automatically released as long as the dependent service is available; in addition, the Code itself does not need to depend on any osgi API.

Similar solutions need to be adopted for specific Felix implementations; as osgi command shell has no standards so far. Osgi RFC 147 is an ongoing specification that allows commands to be executed on different consoles. The source code of our example contains the complete definition of the shorten-command-Felix component.

Start the service

The method described above allows us to provide (and consume) shortening services in any order. Once the Command Service is started, it is bound to the shortening Service with the highest priority available; or, if no priority is specified, it is bound to a service with the lowest service level. We will not consider whether or not the next high-priority service should be started, but continue to use the currently bound service. However, if the service is uninstalled, we need to re-bind it to maintain the highest priority shortening service without interrupting the client.

To run this example, both platforms need to download and install some additional Bundle:

  • Felix
    • Config admin (org.apache.felix.configadmin-1.2.4.jar)
    • SCR declarative services (org.apache.felix.scr-1.2.0.jar)
  • Equinox:
    • org.eclipse.equinox.ds
    • org.eclipse.equinox.util
    • org.eclipse.osgi.services

Up to now, you should be familiar with the process of installing and starting bundles. If not, please refer to the static modularization article. We need to install the bundle and our shortening service. The following is the operation process in the Equinox environment, where bundle is placed in the/tmp directory:

$ java -jar org.eclipse.osgi_* -console osgi> install file:///tmp/org.eclipse.osgi.services_3.2.0.v20090520-1800.jar Bundle id is 1 osgi> install file:///tmp/org.eclipse.equinox.util_1.0.100.v20090520-1800.jar Bundle id is 2 osgi> install file:///tmp/org.eclipse.equinox.ds_1.1.1.R35x_v20090806.jar Bundle id is 3 osgi> install file:///tmp/com.infoq.shorten-1.0.0.jar Bundle id is 4 osgi> install file:///tmp/com.infoq.shorten.command-1.1.0.jar Bundle id is 5 osgi> install file:///tmp/com.infoq.shorten.tinyurl-1.1.0.jar Bundle id is 6 osgi> install file:///tmp/com.infoq.shorten.trim-1.1.0.jar Bundle id is 7 osgi> start 1 2 3 4 5 osgi> shorten http://www.infoq.com ... osgi> start 6 7 osgi> shorten http://www.infoq.com http://tinyurl.com/yr2jrn osgi> stop 6 osgi> shorten http://www.infoq.com http://tr.im/HCRx osgi> stop 7 osgi> shorten http://www.infoq.com ... 

After we install and start our dependencies (including the shorten command), The shorten command still cannot display the results on the console. Only when we start the shortening service registered for the shorten command.

When a local shortening service is stopped, it is automatically transferred to the second shortening service. If the second service is stopped, the shorten Command service automatically clears registration.

Note:

Declarative services make it easier to connect to osgi services. However, pay attention to the following points.

  • DS bundle needs to be installed and started to connect the components. In this way, DS bundle is installed as part of the osgi framework startup, such as equinox osgi. bundles or Felix. Auto. Start of Felix.
  • DS usually has other dependencies that need to be installed. Taking equinox as an example, Equinox. util bundle should be included.
  • Declarative services are part of osgi compendium specification, rather than part of core specifications. Therefore, service interfaces are usually provided by an independent bundle. In the Equinox environment, it is provided by osgi. services, but in the Felix environment, the interface is output by the SCR (service component registry -- service component Registration) bundle itself.
  • Declarative services can be configured using properties. Generally, the osgi config Admin Service is used, although this is optional. Therefore, some parts of DS need to run config Admin. In fact, equinox 3.5 has a bug. If you want to use config admin, it needs to be started before DS (declarative services. This usually requires the use of the start-up attribute to ensure that the correct dependency is met.
  • The OSGI-INF directory (together with the XML file) needs to be included in the bundle, otherwise Ds will not see it. You also need to ensure that the service-component header exists in the bundle manifest.
  • You may also want to use service-component: OSGI-INF/*. XML to include all the components, rather than listing their names one by one. This also allows fragment to add new components to a bundle.
  • The bind and unbind methods require synchronized to avoid potential competition. Even though compareandset () is used on atomicreference, it can also be used as a non-synchronized placeholder for a single service.
  • The ds component does not need the osgi interface, so that it can be simulated for testing or use in other control inversion modes (such as spring. However, spring DM and osgi blueprint services can be used to organize services. This will be a future topic.
  • DS 1.0 does not define the default XML namespace; DS 1.1 adds the http://www.osgi.org/xmlns/scr/v1.1.0namespace. If no namespace exists in the file, it is considered to be compatible with DS 1.0.
Summary

In this article, we discuss how to decouple our implementation from osgi APIs and use declarative descriptions of components. Declarative services provide the ability to organize components and register services to help avoid startup sequence dependencies. In addition, the dynamic nature means that when our dependent services start to stop, the components/services also start to stop.

Finally, both DS and manual management services use the same osgi service layer for communication. Therefore, one bundle can provide services manually, and the other can consume services using declarative services (and vice versa ). We should be able to mix and match implementation 1.0.0 and 1.1.0, and they should work transparently.

The installable bundle in the examples described in this article is listed as follows (including the source code ):

  • Com. infoq. shorten-1.0.0.jar
  • Com. infoq. Shorten. command-1.1.0.jar
  • Com. infoq. Shorten. tinyurl-1.1.0.jar
  • Com. infoq. Shorten. trim-1.1.0.jar

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.