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?

24 comments:

  1. 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.

    ReplyDelete
  2. 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.

    ReplyDelete
  3. 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!

    ReplyDelete
  4. Anonymous12:11 PM

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

    ReplyDelete
  5. Anonymous11:13 PM

    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

    ReplyDelete
  6. 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.

    ReplyDelete
  7. Anonymous2:31 PM

    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:..... ???

    ReplyDelete
  8. 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!

    ReplyDelete
  9. Anonymous12:10 PM

    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

    ReplyDelete
  10. Anonymous12:35 PM

    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?

    ReplyDelete
  11. 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.

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

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

    ReplyDelete
  14. >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.

    ReplyDelete
  15. 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. :-)

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

    ReplyDelete
  17. Anonymous9:12 AM

    this is excellent. Absolutely cool beginners guide.

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

    ReplyDelete
  19. Anonymous1:01 PM

    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?

    ReplyDelete
  20. 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.

    ReplyDelete
  21. Hi,

    Thanks for the tutorial, still a beginner in OSGi.

    I'm having problems with step 6.

    When I run the terminal command:

    java -jar org.eclipse.osgi_3.2.1.R32x_v20060919.jar -console

    The output is:

    Exception in thread "main" java.lang.NullPointerException
    at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:186)
    at org.eclipse.core.runtime.adaptor.EclipseStarter.main(EclipseStarter.java:150)


    Have I missed out something?What am I doing wrong?


    ReplyDelete
  22. I'm afraid I stopped working on OSGi quite a few years ago. I just tried it and got the same exception as you. I tried again with org.eclipse.osgi_3.12.100.v20180210-1608.jar and that hung. Sorry. I suggest you contact the Equinox community at http://www.eclipse.org/equinox/resources.php.

    ReplyDelete