This page last changed on Nov 03, 2005 by jdeolive.

Open Services Gateway Initiative (OSGi)

http://www.osgi.org

The OSGi Alliance Service Platform enables new & exciting services and applications for networked devices.OSGi technology improves time-to-market and reduces costs of manufacturing and development with unique new after-market sales opportunities for device manufacturers and service providers

THere are a number of implementations of the OSGi framework avaiable:

THis article is intended to be a view of OSGi from 10,000 ft. For a much more thorough introduction to OSGi work through this oscar tutorial.

With OSGi, plugins are referred to as Bundles. A bundle is essentially just a jar file which contains OSGi specific meta-data in in its manifest. This meta-data includes:

  • the id and version of the bundle
  • dependencies on other bundles
  • which packages are available to other bundles

Example

Lets jump into our example. We will create 3 bundles:

  • One containing the service interface called example.service.
  • One containing the implementation for WMS called example.service.wms.
  • One containing the implementatino for WFS called example.service.wfs.
Tool Support

As of Eclipse 3.2M1, Eclipse can create OSGi manifests for other OSGi frameworks.

Creating the three projects we get:

In each project we see the MANIFEST.MF file mentioned above. Also notice the Activator class in each bundle. A bundle must have an activator class and it must be listed in the manifest. An activator is an implementation of the org.osgi.framework.BundleActivator interace.

BundleActivator
interface BundleActivator {
   void start(BundleContext context) throws Exception;

   void stop(BundleContext context) throws Exception;
}

The activator serves a number of purposes.

1. Provides lifecycle events to the bundle.
2. Provides the bundle with a context, which gives the bundle access to other types of functionality such as the ServiceRegistry (explained in more detail below).

Bundle Manifests

As stated above each bundle requires a manifest.

example.service
Manifest-Version: 1.0
Bundle-Name: Service Plug-in
Bundle-SymbolicName: example.service
Bundle-Version: 1.0.0
Bundle-Activator: example.service.Activator
Bundle-Classpath: .
Export-Package: example.service

Important things to note:

  • The activator class example.service.Activator is specified as the bundle activator.
  • The example.service package is exported since our other bundles depend on the Service interface.
example.service.wms
Manifest-Version: 1.0
Bundle-Name: Wms Plug-in
Bundle-SymbolicName: example.service.wms
Bundle-Version: 1.0.0
Bundle-Activator: example.service.wms.Activator
Import-Package: example.service

Important things to note:

  • The activator class example.service.wms.Activator is specified as the bundle activator.
  • The Require-Bundle attribute specifies that this bundle imports the example.service package. This is due to the fact that the WMS class implements the Service interface.
  • No packages from this bundle are exported.
example.service.wfs
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Wfs Plug-in
Bundle-SymbolicName: example.service.wfs
Bundle-Version: 1.0.0
Bundle-Activator: example.service.wfs.Activator
Import-Package: example.service

The wfs bundle manifest is similar to the wms bundle manifest.

Bundle Activators

As stated above each bundle requires an activator.

example.service.Activator
package example.service;

public class Activator implements BundleActivator {

	public void start(BundleContext context) throws Exception {
	}

	public void stop(BundleContext context) throws Exception {
	}

}

Nothing special in the activator for the example.service bundle. It does nothing.

example.service.wms.Activator
package example.service.wms;

public class Activator implements BundleActivator {

	public void start(BundleContext context) throws Exception {
		context.registerService("example.service.Service", new WMS(), new Properties());
	}

	public void stop(BundleContext context) throws Exception {
	}

}

In the wms activator, we see that a new WMS service is created and registered with the context. The service is registered under the name of a class, usually an interface that the class implements. As we will see later, this now allows our WMS service to be accessible to other bundles.

example.service.wfs
package example.service.wfs;

public class Activator implements BundleActivator {

	public void start(BundleContext context) throws Exception {
		context.registerService("example.service.Service", new WFS(), new Properties());
	}

	public void stop(BundleContext context) throws Exception {
	}

}

And similar to wms, the wfs service is registered as well.

Running the Example

So we have a few bundles, how do we run them. Well first we need an osgi runtime. We will use Oscar.

  1. Download oscar-1.0.5.jar
  2. Install oscar with the command
    java -jar oscar-1.0.5.jar
    
  3. Download example.service.jar
  4. Download example.service.wfs.jar
  5. Download example.service.wms.jar

To start up osgi navigate to the directory to where you installed Oscar, and execute oscar.bat (or oscar.sh). When prompted for a profile, just enter 'example'. This starts up a console which we can use to control Oscar. The first thing we will do is install our bundles. This can be done by executing the following commands at the osgi console. The final command ps lists all bundles.

[root@localhost Oscar]# sh oscar.sh

Welcome to Oscar.
=================

Enter profile name: example

-> start file:///home/jdeolive/example.service.jar
-> start file:///home/jdeolive/example.service.wms.jar
-> start file:///home/jdeolive/example.service.wfs.jar
-> ps
START LEVEL 1
   ID   State         Level  Name
[   0] [Active     ] [    0] System Bundle (1.0.5)
[   1] [Active     ] [    1] Shell Service (1.0.2)
[   2] [Active     ] [    1] Shell TUI (1.0.0)
[   3] [Active     ] [    1] Bundle Repository (1.1.2)
[   4] [Active     ] [    1] Service Plug-in (1.0.0)
[   5] [Active     ] [    1] Wms Plug-in (1.0.0)
[   6] [Active     ] [    1] Wfs Plug-in (1.0.0)

replace /home/jdeolive with a valid path.

Example Client

So this is nice but it doesn't do anything. WE have installed our services but noone is around to consume them. What we need is a client. Our client will be a simple servlet that performs the following task:

When a http request comes in, the url of the request is used to locate a particular service. If a service is located then the service outputs its capabilities document, otherwise an error is output.

Similar to our services, our client will also be a bundle. Lets call it example.service.client. The following is its manifest:

example.service.client
Manifest-Version: 1.0
Bundle-Name: Client Plug-in
Bundle-SymbolicName: example.service.client
Bundle-Version: 1.0.0
Bundle-Activator: example.service.client.Activator
Require-Bundle: example.service
Import-Package: javax.servlet,
 javax.servlet.http,
 org.osgi.service.http,
 example.service

Note the imports: javax.servlet,javax.servlet.http,and org.osgi.service.http. The first two are simply the java servlet api. The thrid is an http osgi bundle whose purpose is to provide a mechanism to publish our servlet. The last is the Service API provided by the example.service bundle.

example.service.client.Activator
public class Activator implements BundleActivator {

	public void start(BundleContext context) throws Exception {
		//get a reference to the http service
		ServiceReference ref = 
			context.getServiceReference(HttpService.class.getName()); 
		HttpService http = (HttpService) context.getService(ref);
		
		//register the capabilities servlet
		http.registerServlet(
			"/example", new CapabilitiesServlet(context),new Properties(),null
		);
	}

	public void stop(BundleContext context) throws Exception {
	}

}

The activator for our client looks up the http service and uses it to install the servlet under the context: /example.

CapablitiesServlet
public class CapabilitiesServlet extends HttpServlet {

	BundleContext context;
	
	public CapabilitiesServlet(BundleContext context) {
		this.context = context;
	}
	
	protected void doGet(HttpServletRequest request, HttpServletResponse response) 
		throws ServletException, IOException {
	
		//strip the service id from the url
		String req = request.getRequestURI();
		String serviceId = req.substring(req.lastIndexOf('/')+1);
		
		//find a service that matches up
		ServiceReference[] refs = null;
		try {
			refs = context.getServiceReferences(Service.class.getName(),null);
		} 
		catch (InvalidSyntaxException e) {
			new IOException().initCause(e);
		}
				
		for (int i = 0; i < refs.length; i++) {
			Service service = (Service) context.getService(refs[i]);
			//perform the id match
			if (serviceId.equals(service.getServiceId())) {
				//do it
				service.getCapabilities(response.getOutputStream());
				return;
			}
		}
		
		//getting here means could not find the service
		response.getOutputStream()
			.write(("Could not locate service with id:"+ serviceId).getBytes());
	}
}

The servlet uses the BundleContext to obtain references to the various services that have been installed.

Running the Client

First download example.service.client.jar. Then execute the following commands at the oscar command line:

-> obr install Servlet
Installing: Servlet
-> obr install "Http Service"
Installing: HTTP Service
-> ps
START LEVEL 1
   ID   State         Level  Name
[   0] [Active     ] [    0] System Bundle (1.0.5)
[   1] [Active     ] [    1] Shell Service (1.0.2)
[   2] [Active     ] [    1] Shell TUI (1.0.0)
[   3] [Active     ] [    1] Bundle Repository (1.1.2)
[   4] [Active     ] [    1] Service Plug-in (1.0.0)
[   5] [Active     ] [    1] Wms Plug-in (1.0.0)
[   6] [Active     ] [    1] Wfs Plug-in (1.0.0)
[   7] [Installed  ] [    1] Servlet (1.0.0)
[   8] [Installed  ] [    1] HTTP Service (1.1.2)
-> start 8
15:45:49.041 EVENT  Starting Jetty/4.2.x
15:45:49.125 EVENT  Started SocketListener on 0.0.0.0:80
15:45:49.131 EVENT  Started org.mortbay.http.HttpServer@bb7465
-> start file:///home/jdeolive/example.service.client.jar
15:46:06.840 EVENT  Started ServletHttpContext[/]
->
You need administrative priveleges on the machine in order to run the Http Service over port 80.

The first two commands install the OSGi Http Service from the Oscar Bundle Repository. The last commands installes our client bundle. Once the commands have completed successfully try the following:

http://localhost/example/wms
http://localhost/example/wfs

Evaluation

So you might be saying "So what, what is this going to buy me when I am developing applications?". And the answer is "Well not much by itself." The power of OSGi is what is built on top of the core. Much work has gone on to develop bundles that provide services that the application programmer wishes to use. For instance, the following is a repository of bundles which provide services such as HTTP,Logging,JMX, and many more.

I would like to mention Equinox, which is an implementation of OSGi that is used with Eclipse. While still in the early stages of development, it shows much promise. As a background, part of Eclipse 3.0 inolved a transition from there custom plugin model to OSGi. Go here for a full brackground.

The point I would like make is that much of the Eclipse internals are being factored out into Equinox. Anyone who has developed with Eclipse can tell you that it has a pretty sweet plugin model. The idea of extensions and extension points are a very powerful construct.

Enough about the technology, lets apply our criteria.

Plug-in Architecture.

Not much has to be said here, OSGi has a top knotch plug-in architecture. It was so good that IBM decided to build the Eclipse plug-in architecture around it. Lets discuss some of the high points.

Explicit declaration of imports and exports. This has a couple of benefits. The first is that it provides the framework enough information to tie bundles together, while at the same keeping the individual bundles isolated from each other. All dependency complexities are handled by the framework. However most programmers are not used to doing this and forgetting to do can lead to the dreaded "ClassNotFoundException". It also makes describing bundle dependencies pretty verbose.

One think I like is that it allows programmers to clearly define what is api and what is not. We have all run into the problem of client code becoming dependent on framework code that isn't part of the API. With this type of explicit import/export system interface is clearly seperated from implementation.

Ease of Development

The OSGi API is well documented and they have published a set of Java interfaces which have been very well javadoc'd.

Tooling support comes from Eclipse. The tools however are still geared to the specific Eclipse OSGi implemetnation, however the 3.2 series has introduced some support for generating OSGi manifests for other OSGi frameworks.

The strong programming model is also there. Component interaction is well defined via bundle context and service registry.

Integration

This is where we start to see OSGi fall a bit short. I ran an experiment where I tried to fire up Equinox from a servlet in a J2EE app and the two were in conflict. So for now it seems that OSGi is its own beast. That being said there is work going on to remedy this situation. If you are interested in the specifics see the following bug report.

Longevity

It appears that OSGi is here to stay. IBM has put a fairly large steak in it. There is also some cool stuff going on with other standards such as JSR-277 (Java Module System).


example.service.jar (application/octet-stream)
example.service.jar (application/octet-stream)
example.service.jar (application/octet-stream)
example.service.client.jar (application/octet-stream)
example.service.wfs.jar (application/octet-stream)
example.service.wfs.jar (application/octet-stream)
example.service.wfs.jar (application/octet-stream)
example.service.wms.jar (application/octet-stream)
osgi-example1.png (image/png)
eclipse-osgi.png (image/png)
osgi_arch.jpg (image/jpeg)
Document generated by Confluence on May 14, 2014 23:00