This post covers some basic OSGi concepts. Also I’ll present a Tycho example of service definition in declarative approach.
Motivation to modules
Slicing complex system in modules is the crucial approach for the complexity reduction1. Developing for JVM is by default developing in one code space. Modularization is supported by concept of packages and visibility levels. However these techniques seem to be very soft concepts, thus the teams building everything in huge weakly modularized code bases or try to utilyze additioan libraries and tools. This situation also do 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 the OSGi platform) i believe its worth to dive into to the topic.
OSGI Bundle
Open Services Gateway initiative Framework short OSGi is a service platform for software components (dynamic modules) in java. OSGi components are called Bundles and every application consists 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 an that without restart of a whole platform. In other words, OSGi introduces a kind of run-time capable modularization system on top of the java core platform. This is pretty interesting.
Oracle is hardly trying to introduce modularization to the java platform (See Jigsaw Project). 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 proposing this 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 today.
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. Due modularity and “dynamic” nature of OSGi, services play an important role.
In the early days, services had to be discovered or registered explicitly to org.osgi.framework.BundleContext
. Below is an example with the 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 {
//explicit service registration.
context.registerService(HalloService.class.getName(), new HalloServiceImpl(), null );
}
}
Registration of HalloService is done explicitly. And here is how we would discover such a service.
public void start(BundleContext context) throws Exception {
ServiceReference reference = context.getServiceReference(HalloService.class.getName());
service = (HalloService) context.getService(reference); // service is retrieved.
}
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)2. Its all about relaxing Activator concept by introducing meta description in form of XML and later also in annotations.
Below is an example of the implementation of org.osgi.service.log.LogListener
that registers himself to OSGi Platform provided LogReaderService. And that service is injected by the OSGi-Runtime according to XML description of the Component. The 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);
}
}
The 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" 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 a reference
(A specified dependency of a component). The Reference binds 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
do specify Event Methods association with a Reference. Having that is basically enough for SCR to manage the life-cycle and injection of LogReaderService
to the ConsoleLogger
component.
However, to see something on the console we need something to call the platform provided org.osgi.service.log.LogService
like:
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 Log-example project on my GitHub
Build
Before seeing any results we must build our bundles. To be able to 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 the 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 Log example bundle.
Execute
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 with 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 messages to the console at the start of the 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 the above example. Here is an example of a 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 components provide service. Here the exposed service is the implementation of OSGi standard Service interface EventHandler which is part of the Admin Event Service (AES) Specification that describes the Publish-Subscribe pattern for OSGi. Property event. The topic is also used by AES.
The factory approach is not covered here. We should prefer Blueprint Container if it starts to be complicated that way.