A few months a go I was reading the website of the springframework. One article subject supprised me. I had never heard of it, OSGi. Curious enough to want to know what it was about I opened the article and started reading. The more I read the more I was certain, this is going to be the next big thing. Then in December I attended The SpringExperience in Miami and of course there was one presentation I had to attend. It turned out I was not the only one interested in spring-OSGi. Adrian Colyer gave a very interesting presentation and almost the complete interface21 team was there. They all think OSGi is going to be the next big thing. If you do a google on spring osgi you will get a lot of hits. Of course none of these is as technical as mine 🙂

If you want to read more about the event look at this blog.

Now a few weeks later I had finally found some time to experiment, and of course I want to share what I have learned. This blog item is a step by step approach of creating your own spring-osgi bundle. Later I will add some more complicated bundles with references, listeners and maybe a webserver.

So, how do we start? Well first download all code and install it using maven. Hmm, maybe I should tell you first that you need to install maven 2 and subversion. So, please install subversion and maven2. Then checkout all sources:

svn checkout https://svn.sourceforge.net/svnroot/springframework/spring-osgi/trunk spring-osgi

Than step into the root folder and do a mvn install. You can ofcourse have a look at the samples that come with spring-osgi. Please go to the spring-osgi website and see the tutorials there. We arfe going to create a new bundle using the maven archetype.

If you want to have a look at the complete source code, do a checkout from the following google code website or browse it online

svn checkout http://gridshore.googlecode.com/svn/trunk/SpringOsgiSample gridshore

Using the archetype

mvn archetype:create \
  -DarchetypeGroupId=org.springframework.osgi \
  -DarchetypeArtifactId=spring-osgi-bundle-archetype \
  -DarchetypeVersion=1.0-SNAPSHOT \
  -DgroupId=nl.gridshore.samples.springosgi \
  -DartifactId=bookreview-service \
  -Dversion=1.0-SNAPSHOT \
  -DremoteRepositories=http://static.springframework.org/maven2-snapshots

Have a look at the generated pom.xml file and check mine. There are some things I altered that are important.
– I added the scope – test to the dependencies that did not have it
– I added the following dependencies : jcl104-over-slf4j.osgi, slf4j-log4j-full and log4j.osgi
– removed the dependecy for commons-logging
Check the pom.xml file of the provided samples by spring-osgi, that is where I got it from.

The archetype also provides a .project and .classpath file for eclipse. I had problems when I did a mvn eclipse:clean eclipse:eclipse. Some lines are not added that were in place when the files were provided by the archetype.

    <buildCommand>
      <name>org.eclipse.pde.ManifestBuilder</name>
    </buildCommand>
    <buildCommand>
      <name>org.eclipse.pde.SchemaBuilder</name>
    </buildCommand>
    <nature>org.eclipse.pde.PluginNature</nature>

Not sure whether these are absolutely necessary. I do know a bundle within eclipse is a plugin, therefore you need the plugin nature.

Start coding
The framework of the project is in place and should compile (mvn install). Now it is time to start creating the bundle. We’ll start by creating the BookReviewService, the unit test and the implementation of this service. This is pure java code, check the sources if you like. Now add a bean that we can expose as a service to the spring context file. There are two context files out of the box. You can find them in the src/main/resources/META-INF/spring folder.
bundle-context.xml : contains the normal spring configuration
bundle-context-osgi.xml : contains all beans that use the osgi namespace

We have created the spring bean myBookReviewService:

<bean name="myBookReviewService" 
    class="nl.gridshore.samples.springosgi.impl.BookReviewServiceImpl" />

Then we expose this bean as a special osgi service

<osgi:service id="bookReviewServiceOSGi" ref="myBookReviewService"
    interface="nl.gridshore.samples.springosgi.BookReviewService" />

And now we are done, no not really. First we need to create an integration test. We use the special super class AbstractDependencyInjectionSpringContextTests. This test uses the spring config file to configure and start an application context. From the context we obtain our bean and test it to see if it works.
Alter the manifest file
Next step is to alter the manifest file, you can find this file in the META-INF folder at the same level of the src folder. You need to add an export package and the Bundle-classpath

Bundle-Version: 1.0
Bundle-SymbolicName: nl.gridshore.samples.springosgi.bookreviewservice
Bundle-Name: nl.gridshore.samples.springosgi.bookreview-service
Export-Package: nl.gridshore.samples.springosgi
Bundle-ClassPath: .,
 target/classes/

Now we are ready to package our bundle. By using one of the tutorials from the spring-osgi website you can use eclipse/equinox to install and start this bundle. There is however not much to see. Wouldn’t it be nice to be able to test within an exsiting osgi container whether your hard labor was wurth it? You can, the next steps will describe how you can create an in container test.

Creating the in container test
Again the framework provides a number of super classes that makes life a lot easier. I am not going to explain in detail what these classes are doing. Still I think one thing is important to know. Your test projects is being transformed into a bundle at runtime with a reference to the bundle under test. But lets start with the creation of the normal jar file that will contain our test.

mvn archetype:create \
  -DgroupId=nl.gridshore.samples.springosgi \
  -DartifactId=bookreview-service-integration-test \
  -Dversion=1.0-SNAPSHOT \

Alter the pom, it should use the parent and have a lot of dependencies. There are two I would like to mention is special:
bookreview-service : this is our own project that we are gonna test
org.springframework.osgi.test : utilit classes for our own test class

The manifest file for the bundle that is created on the fly comes from the test/resources directory and looks like this.

Manifest-Version: 1.0
Bundle-Name: simple-service-integration-tests
Bundle-SymbolicName: nl.gridshore.samples.springosgi.test
Bundle-Activator: org.springframework.osgi.test.JUnitTestActivator
Import-Package: junit.framework,
 org.osgi.framework;specification-version="1.3.0",
 org.springframework.core.io;specification-version="2.1.0",
 org.springframework.osgi.test,
 org.springframework.osgi.test.runner,
 nl.gridshore.samples.springosgi

Now we can create the test, use the ConfigurableBundleCreatorTests as a super class and implement the method to locate your MANIFEST.MF file. You also must implement a method that defines all the dependencies that can be obtained from your local maven repository. Finally you need to add your test methods.

public void testOSGiStartedOk() {
   BundleContext bundleContext = getBundleContext();
   assertNotNull(bundleContext);
}

This test checks if the context for the created bundle can be obtained.

public void testSimpleServiceExported() {
  waitOnContextCreation("nl.gridshore.samples.springosgi.bookreviewservice");
  BundleContext context = getBundleContext();
  ServiceReference ref = context.getServiceReference(BookReviewService.class.getName());
  assertNotNull("Service Reference is null", ref);
  try {
    BookReviewService bookReviewService = (BookReviewService) context.getService(ref);
    assertNotNull("Cannot find the service", bookReviewService);
    List bookreviews = bookReviewService.findBookReviewByKeyword("habits"); 
    assertEquals(1, bookreviews.size());
  } finally {
    context.ungetService(ref);
  }
}

This test first checks if the osgi service can be obtained from the bundle context. Then the obtained service is used and the result is checked. In my case we are looking for a book and if all is well we should be able to find exactly one.

Now do a mvn install on the highest level and you should have a tested spring osgi module

Conclusion
That is about it, I hope this will help some of you, do not hesitate to ask questions if something is not clear. You can find a lot of information on the spring osgi website, there is a google group and google lists about thousands of hits.

Spring osgi – an evaluation using maven 2 and the special spring osgi archetype