Friday, January 05, 2007

Creating an OSGi bundle

Newcomers to OSGi may like a simple guide to developing a bundle. Peter Kriens has provided an extensive tutorial (December 2008: currently offline pending an overhaul) which is a must for anyone serious about learning OSGi. However, it has a long introduction and assumes you are happy to run Eclipse. So I thought I would provide a trival example that people could get going using only a Java SDK and their favourite text editor.

Here are instructions to create and run a trivial bundle, hopefully in about 10 minutes.

1. Download an OSGi framework (I used Equinox v3.2.1).

2. Paste the following code into the file org/foo/example/Example.java:

package org.foo.example;

import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;

public class Example implements BundleActivator {
public void start(BundleContext bc) {
System.out.println("Hello world");
}
public void stop(BundleContext bc) {
System.out.println("Goodbye world");  
}
}


3. Compile the code:
> javac -cp org.eclipse.osgi_3.2.1.R32x_v20060919.jar org/foo/example/Example.java


4. Paste the following bundle manifest into the file MANIFEST.MF:
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-SymbolicName: org.foo.example.Example
Bundle-Version: 1
Bundle-Activator: org.foo.example.Example
Import-Package: org.osgi.framework;version="1.3.0"
Make sure you have a carriage return and/or newline character at the end of the last line as this is required (see manifest specification in the JAR specification).

5. Package the manifest and code into a bundle:
> jar cvfm example.jar MANIFEST.MF org

Note that the order of the flags is important - the f and m options need to appear in the same order as the jar file and manifest file parameters.

6. Run the resultant bundle example.jar interactively using the Equinox console, e.g.:
> java -jar org.eclipse.osgi_3.2.1.R32x_v20060919.jar -console

osgi> install file:///osgi/blog/example/example.jar
Bundle id is 6

osgi> start 6
Hello world

osgi> stop 6
Goodbye world

osgi> close

(close exits the framework).

That's it! Anyone care to comment how long it took them?

22 comments:

AlBlue said...

This is good stuff, Glyn. This is the kind of thing that people need to be aware of to levarage OSGi outside of Eclipse. Keep up the good work.

jnichols said...

Thanks for this good tutorial. We're using OSGi on the SimpleCenter project (http://www.simplecenter.org), and we're definitely finding people struggling with the OSGi learning curve. Hopefully tutorials like this will help.

Glyn said...

Thanks for the feedback. Are you aware of Neil Bartlett's series of OSGi tutorials on EclipseZone? These should help the people you have in mind.

I heard of SimpleCenter via a recent blog,
but I'm glad you made direct contact. Always interesting to hear about projects adopting OSGi!

Chris Aniszczyk (zx) said...

8 seconds :)

Glyn said...

Nice.

Giri KM said...

A simple startup to the OSGi framework. Helped a lot and very nice.

kathayat said...

HI,

Nice tutorial to go for the beginners.

I just wanted to ask one question. If I wanted to included third party libraty (for example jidc.jar ) to my bundle...how should I proceed ???

THanks

Glyn said...

You can include a third party jar inside your bundle by adding the third party jar to the root directory of the bundle jar file and then adding a bundle classpath header to the bundle's manifest, e.g.:

Bundle-ClassPath: .;jidc.jar

The result is a bundle containing the nested third party jar.

Alternatively, you could convert the third party jar into a bundle with particular exported packages which your application bundle could then import. You could either add a manifest to the third party jar or you could wrap the third party jar in a bundle containing just a manifest and the nested third party jar. The latter approach avoids having to crack open the third party jar if this would invalidate the license.

Putting the third party jar in a separate bundle would allow you to control the packages available to your application from the third party jar so, for example, you could protect your application from being sensitive to changes in internal packages of the third party jar.

kathayat said...

Well, Thanks for the valuable comment.

But still, say I have third party jars jetty-6.1.7.jar, servlet-api-2.5-6.1.7.jar and my application imports packages from these jars like org.mortbay.jetty,org.mortbay.jetty.servlet

So, I think the new menifest file would be like

Manifest-Version: 1.0
Bundle-Description:
Bundle-Name: jetty
Bundle-ClassPath: .;jetty-6.1.7.jar;servlet-api-2.5-6.1.7.jar;jetty-util-6.1.7.jar
Export-Package: org.mortbay.jetty,org.mortbay.jetty.servlet,
Bundle-ManifestVersion: 2
Bundle-Vendor: NTNU
Bundle-SymbolicName: jetty
Bundle-Version: 1.0.1

But the Question is how about

Bundle-Activator:..... ???

Glyn said...

The manifest is approximately correct, but note that bundle classpath entries should be separated by commas (',') rather than semicolons (';').

The bundle activator would presumably have to start up Jetty, but I don't profess any Jetty expertise, so I'll say no more. Sorry!

jonathangreenwood said...

I get a 404 when clicking on your Equinox download link. Perhaps you have time to change it to http://archive.eclipse.org/eclipse/equinox/drops/R-3.2.1-200609210945/download.php?dropFile=org.eclipse.osgi_3.2.1.R32x_v20060919.jar

jonathangreenwood said...

Hi Glyn,

Followed your instructions but I get a message that the activator for the bundle is invalid :-(

osgi> start 4
org.osgi.framework.BundleException: The activator org.foo.example.Example for bundle org.foo.example.Example is invalid
at org.eclipse.osgi.framework.internal.core.AbstractBundle.loadBundleActivator(AbstractBundle.java:141)
at org.eclipse.osgi.framework.internal.core.BundleContextImpl.start(BundleContextImpl.java:962)
at org.eclipse.osgi.framework.internal.core.BundleHost.startWorker(BundleHost.java:317)
at org.eclipse.osgi.framework.internal.core.AbstractBundle.start(AbstractBundle.java:256)
at org.eclipse.osgi.framework.internal.core.FrameworkCommandProvider._start(FrameworkCommandProvider.java:239)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:585)
at org.eclipse.osgi.framework.internal.core.FrameworkCommandInterpreter.execute(FrameworkCommandInterpreter.java:145)
at org.eclipse.osgi.framework.internal.core.FrameworkConsole.docommand(FrameworkConsole.java:293)
at org.eclipse.osgi.framework.internal.core.FrameworkConsole.console(FrameworkConsole.java:278)
at org.eclipse.osgi.framework.internal.core.FrameworkConsole.run(FrameworkConsole.java:213)
at java.lang.Thread.run(Thread.java:613)
Caused by: java.lang.NoClassDefFoundError: org/osgi/framework/BundleActivator
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:675)
at org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader.defineClass(DefaultClassLoader.java:160)
at org.eclipse.osgi.baseadaptor.loader.ClasspathManager.defineClass(ClasspathManager.java:498)
at org.eclipse.osgi.baseadaptor.loader.ClasspathManager.findClassImpl(ClasspathManager.java:468)
at org.eclipse.osgi.baseadaptor.loader.ClasspathManager.findLocalClassImpl(ClasspathManager.java:427)
at org.eclipse.osgi.baseadaptor.loader.ClasspathManager.findLocalClass(ClasspathManager.java:410)
at org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader.findLocalClass(DefaultClassLoader.java:188)
at org.eclipse.osgi.framework.internal.core.BundleLoader.findLocalClass(BundleLoader.java:334)
at org.eclipse.osgi.framework.internal.core.BundleLoader.findClass(BundleLoader.java:386)
at org.eclipse.osgi.framework.internal.core.BundleLoader.findClass(BundleLoader.java:347)
at org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader.loadClass(DefaultClassLoader.java:83)
at java.lang.ClassLoader.loadClass(ClassLoader.java:251)
at org.eclipse.osgi.framework.internal.core.BundleLoader.loadClass(BundleLoader.java:278)
at org.eclipse.osgi.framework.internal.core.BundleHost.loadClass(BundleHost.java:227)
at org.eclipse.osgi.framework.internal.core.AbstractBundle.loadBundleActivator(AbstractBundle.java:134)
... 13 more

What went wrong?

Glyn said...

Hi Jonathan. I fixed the link - thanks for pointing that out.

As for your class loading error, the most likely cause is that you haven't followed step 4 to the letter. If you don't add a carriage return and/or newline character at the end of the last line of the manifest file, the bundle will fail to import the org.osgi.framework package and will be unable to load classes from that package, which is what appears to be happening.

If changing that doesn't fix the problem, please post back.

Jiorgos Miskakis said...

How can you access the service remotely from an external client?

Jiorgos Miskakis said...

How can you access the service remotely from an external client?

Glyn said...

>How can you access the service
>remotely from an external client?

That's another story and not one which OSGi currently addresses. So called "distributed OSGi" is being considered for a later version of OSGi.

Zteve said...

Peter Kriens has provided an extensive tutorial which is a must for anyone serious about learning OSGi. You may wish to note that this tutorial is removed at the moment, because Peter thinks it needs a serious update. :-)

Glyn said...

Thanks Steve. I've made a note in the blog.

thilina said...

this is excellent. Absolutely cool beginners guide.

MaNJuLA said...

Thank you very much Glyn, this is a excellent article for a beginner to start with OSGI.

Anonymous said...

Oh, I have problems with that tutorial. According to http://www.eclipse.org/forums/index.php/t/340093/ - How should we start org.eclipse.equinox.console bundle?

Glyn said...

I found the same problem a few minutes ago! If you use an older version of Equinox, such as 3.2.1, the tutorial works fine.

Projects

OSGi (130) Virgo (59) Eclipse (10) Equinox (9) dm Server (8) Felix (4) WebSphere (3) Aries (2) GlassFish (2) JBoss (1) Newton (1) WebLogic (1)

Blog Archive