OSGi Bundles: Declarative Services

This post covers some basic OSGi concepts. Also i'll present a Tycho example of service definition in declarative approach.

Why modules

Slicing complex system in modules is crucial approach of complexity reduction 1. This and other aspects explain the growing popularity of OSGi technology in the past years. Since OSGi is very popular today (Nearly every modern Java Application Server and IDE is based on OSGi platform) it is worth to be familiar with it.

OSGI Bundle

OSGi2 is a service platform for software components (dynamic modules) in java. OSGI Components are called Bundles and every application consist of at least one bundle. Bundles are managed by an OSGi platform during the run-time. The OSGi spec defined bundle life-cycle, so Bundles can be

  • installed
  • started
  • stopped
  • updated
  • uninstalled

during the run-time without restart of a whole platform.
In other words OSGi introduces a kind of modularization system on top of java core platform.

OSGI bundle

Oracle is hardly trying to introduce modularization to java platform3. This goal was initially targeted for java 7 but now maybe we will see it in java 9. And it looks like at the moment oracle engineers say - java platform modularization will not be an adoption of OSGi. Even if OSGi Alliance was suggesting such way to go. However OSGi is already partly specified in JSR-291 Dynamic Component Support for JavaTM SE but i'm not sure whether it matters somehow.;)

Manifest

Every OSGi bundle is defined by the manifest. Manifest states symbolic name, version, exported and imported packages (as well as plugin and dynamic dependencies) and more. Here is one example.

Manifest-Version: 1.0  
Bundle-ManifestVersion: 2  
Bundle-Name: Logservice  
Bundle-SymbolicName: org.holbreich.osgi.logservice  
Bundle-Version: 1.0.0.qualifier  
Bundle-RequiredExecutionEnvironment: JavaSE-1.7  
Import-Package: org.osgi.framework;version="1.3.0"  

Services

Further OSGi provides pre-defined services like Logging, Administration, Http-Server and more. OSGi Platform implements Service management infrastructure, that services use to register and discover. An due modularity and "dynamic" nature of OSGi, services play important role, i would even say, that programming OSGi is always programming Services and "thinking SOA"4.

In the early days services had to be discovered or registered explicitly to BundleContext5. Below is an example with Activator class. Activator's start() method is called every time the Bundle starts.

public class Activator implements BundleActivator {

   @Override
   public void start(BundleContext context) throws Exception {
      //explicite service registration.
      context.registerService(HalloService.class.getName(), new HalloServiceImpl(), null );
   }
}

Registration of HalloService is done explicitelly. And here is how we would discover such service.

public void start(BundleContext context) throws Exception {  
   ServiceReference reference = context.getServiceReference(HalloService.class.getName());
   service = (HalloService) context.getService(reference); // service is retreived.
}

Declarative Service Definition

But OSGi specification developed and became additional possibilities to define, control and link services. The more advanced concept is called Declarative Services (DS)6. That it is all about relaxing Activator concept by introducing meta description in form of XML and later also in annotations.

Below is an example of implementation of org.osgi.service.log.LogListener which registers himself to OSGi Platform provided LogReaderService. And that service is injected by the OSGi Runtime according to XML description of a Component. Component is a POJO which life cycle is controlled by Service Component Runtime (SCR).

public class ConsoleLogger implements LogListener {  
    private LogReaderService logReaderService;

    public void unsetLogReaderService() {
      logReaderService=null;
    }

    public void setLogReaderService(LogReaderService logReaderService) {
        this.logReaderService = logReaderService;
    }

    protected void activate(ComponentContext componentContext){
        logReaderService.addLogListener(this);
    }

    protected void deactivate(ComponentContext componentContext){
       logReaderService.removeLogListener(this);
    }

   @Override
   public void logged(LogEntry entry) {
       String logmessage = String.format("[%s]  ", "Level", entry.getBundle().getSymbolicName(), entry.getMessage());
       System.out.println(logmessage);
   }
}

Corresponding component declaration can be found in OSGI-INF/component.xml and looks like this:

<?xml version="1.0" encoding="UTF-8"?>  
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0&quot; name="org.holbreich.osgi.logservice">  
<implementation class="org.holbreich.osgi.logservice.ConsoleLogger"/>  
<reference name="logService" interface="org.osgi.service.log.LogReaderService" bind="setLogReaderService" unbind="unsetLogReaderService" />  
</scr:component>  

We see Component Implementation class definition and we see there is Reference (A specified dependency of a component). The Reference binds Target Service LogReaderService to our component. In this case this is platform provided Service. Of cause it is possible to declare custom services. The attributes bind and unbind specify Event Methods association with a reference. So this is enough for SCR to manage the life-cycle and injection of LogReaderService to the ConsoleLogger component.

To see something on the console we need someone to call platform provided org.osgi.service.log.LogService.

this.logService.log(LogService.LOG_INFO, "Start LogExample component");  

This could be done from any bundle on the current OSGi Runtime.

You can find Logservice project and the logexample project on GitHub (Pease notify that i'm pointing you to the Tag named: simple_services1)

Build

Before see any results we must build our bundles. To be able build it IDE independent there is a pom.xml file included. It uses tycho 0.15 to build and deliver the bundle. You can e.g. execute following to get desired artifacts.

mvn package .{path to pom.xml}pom.xml  

And you will find org.holbreich.osgi.logservice-1.0.0-SNAPSHOT.jar in your target folder. Now build Logexample bundle.

Executute

Let's see how it works in a real OSGi Platform. Equinox would be fine, but i tried it with Apache Felix.
First Download the following:

  • Apache felix distribution
  • Bundle: Config Admin
  • Bundle: SCR (Declarative Services)
  • Bundle: Log

That's all we need.

Then unzip apache felix distribution to some folder.
Also copy downloaded bundles to /bundle folder of apache felix. Now you are ready to start felix.

$java -jar ./bin/felix.jar
____________________________  
Welcome to Apache Felix Gogo

g!  

No exception should occur. Now let's check what our platform looks like, type: lb

g! lb  
START LEVEL 1  
 ID|State |Level|Name
 0|Active | 0|System Bundle (4.0.3)
 1|Active | 1|Apache Felix Bundle Repository (1.6.6)
 2|Active | 1|Apache Felix Configuration Admin Service (1.4.0)
 3|Active | 1|Apache Felix Gogo Command (0.12.0)
 4|Active | 1|Apache Felix Gogo Runtime (0.10.0)
 5|Active | 1|Apache Felix Gogo Shell (0.10.0)
 6|Active | 1|Apache Felix Declarative Services (1.6.0)
 7|Active | 1|Apache Felix Log Service (1.0.1)

Now we see the system bundle, Apache Felix Gogo Shell Bundles and also additional downloaded bundles. All bundles are in the active state which is good.
Now we can install custom bundles whit install command.

g! install file:/c://tmp/org.holbreich.osgi.logservice-1.0.0-SNAPSHOT.jar  
Bundle ID: 8  
g! install file:/c://tmp/org.holbreich.osgi.logexample-1.0.0-SNAPSHOT.jar  
Bundle ID: 9

g! lb  
START LEVEL 1  
 ID|State |Level|Name
 0|Active | 0|System Bundle (4.0.3)
 1|Active | 1|Apache Felix Bundle Repository (1.6.6)
 2|Active | 1|Apache Felix Configuration Admin Service (1.4.0)
 3|Active | 1|Apache Felix Gogo Command (0.12.0)
 4|Active | 1|Apache Felix Gogo Runtime (0.10.0)
 5|Active | 1|Apache Felix Gogo Shell (0.10.0)
 6|Active | 1|Apache Felix Declarative Services (1.6.0)
 7|Active | 1|Apache Felix Log Service (1.0.1)
 8|Installed | 1|Logservice (1.0.0.201209042045)
 9|Installed | 1|Logexample (1.0.0.201209042048)

Now bundles are installed but are not active yet. Let's start them by command start

g! start 8  
g! start 9  
g! [Level]  Start LogExample component  
[Level]  BundleEvent STARTED

Works! We see that log service bundle was printing message to console on start of example bundle.

Further Details

There are three component types:

  • Immediate Component – The component configuration of an immediate component must be activated immediately after becoming satisfied. Immediate components may provide a service.
  • Delayed Component – When a component configuration of a delayed component becomes satisfied, SCR will register the service specified by the service element without activating the component configuration. It will be activated on demand.
  • Factory Component – If a component’s description specifies the factory attribute of the component element, SCR will register a Component Factory service. This service allows client bundles to create and activate multiple component configurations and dispose of them. If the component’s description also specifies a service element, then as each component configuration is activated, SCR will register it as a service.

Simple immediate Component was presented twice in above example. Here is an example of delayed component:

<xml version="1.0" encoding="UTF-8">  
<scr:component name="example.handler" xmlns:scr="http://www.osgi.org/xmlns/scr/v1.2.0">  
 <implementation class="com.acme.HandlerImpl"/>
 <property name="event.topics">some/topic</property>
 <service> 
    <provide interface="org.osgi.service.event.EventHandler"/> 
  </service> 
</scr:component>  

We see that and delayed component provide service. Here the exposed service is implementation of OSGi standard Service interface EventHandler which is part of the Admin Event Service (AES) Specification that describes Publish-Subscribe pattern for OSGI. Property event.topic is also used by AES.

Factory approach is not covered here. We should prefer Blueprint Container if it starts to be complicated that way.


  1. See Separation of Concerns principle (SoC)

  2. Open Services Gateway initiative Framework

  3. Project Jigsaw

  4. Forgive me that buzzword. Here reduce the meaning of SOA to architecture concepts like loose coupling

  5. org.osgi.framework.BundleContext

  6. Chapter 112 in the OSGi Service Platform Service Compendium (PDF)