Contact me!

I'd love to hear from you.

mailto :
kevin dot webber (gmail)

inperson :
I live downtown Toronto, and can often be found in a coffee shop illuminated by the glow of my Macbook.

You’ve probably heard of both OSGi and Apache Wicket. If you haven’t, holy doodle… you’re missing out on a wonderful world of Java goodness! I’m not kidding. Wicket and OSGi are two very exciting technologies.

In this tutorial we’re going to set up an OSGi container, create a sample OSGi bundle that uses the Wicket framework, and deploy the bundle to our container.

Hello, OSGi

As modular as your code is at compile and build time using super-creative archiving and package-naming cleverness, all your objects wind up in the same bowl of runtime soup. It’s kind of like taking the time to cook a wonderful meal, only to throw it in a blender before serving it. Sure, creating modular Java applications is possible without OSGi by creating a physical or logical separation between components; packaging classes in different EARs/WARs, and/or the tried-and-true “common artifact” bundle. Sounds great, but the logical separation achieved by the common artefact strategy dissolves at runtime. Creating a true physical separation via distributed communications (WS, REST, etc) cleanly separates entities at runtime, but comes with a performance penalty. Also, the latter strategy only works for webapps; embedded or desktop apps can’t make use of distributed communications. Imagine if all the Eclipse plugins you have installed needed to communicate via WS* or REST?

All we really need is a way to modularize our runtime environment in the same way we do with our build artifacts.

Enter the dragon: OSGi.

Cleanly separated bundles. Hidden internal classes. Explicitly defined inter-bundle dependencies. I’m so happy! It’s like plain old Java, but zen. I hear WebSphere, JBoss, et al. are built on top of OSGi containers now. How about I go ahead, create an OSGi bundle, deploy it to my most favourite-ist enterprise app server, and celebrate with some Doritos and Cherry Coke!

Wait… there’s a catch.

At the present time, OSGi doesn’t have the tooling and app server support as of now to make it a reasonable solution for most enterprise web development projects. There, I said it. :p

One of the interesting facts about the current generation of app servers is that while most vendors have spent a huge amount of effort and resources refactoring their offerings and building them on OSGi containers, the developers using said app servers cannot take advantage of all this internal vendor OSGi goodness. At the time of this writing, the only enterprise app server ready to gobble up your OSGi bundles is the SpringSource dm Server. It supports both complete OSGi bundle support (RFC-124) and also OSGi web bundles (RFC-66). The folks at IBM and Oracle (BEA) haven’t bothered to pass on all the OSGi goodness to mere mortals like myself who actually develop and deploy functional web applications for a living.

So… if you work at a massively-massive corporation like I do, you’re probably using an app server from one of the big-boys (WebSphere, Weblogic, or something equally enterprise-y). Introducing a new app server to your production environment isn’t going to happen for technological reasons. Unless you’re lucky and have more flexibility than most of us (or work at a small company), I recommend looking at OSGi in terms of long-term strategic planning rather than a short-term tactical decision. So, why am I spending time with OSGi then? As soon as the major app server vendors start to fully support OSGi, the shift towards this technology will be fast and totally rational (in my humble opinion). In other words…

OSGi is fantastic!

The lack of server support is the only thing holding back the OSGi adoption rate. I highly recommend learning and using OSGi for specific projects; just be careful not to commit “design-by-buzzword” and clobber together a half-baked OSGi solution into your enterprise ecosystem.

With that said, building an OSGi-fied “Hello World” Wicket app seems like a good first step towards OSGi nirvana!

Word of warning: Most of this tutorial relies on the magic provided by Spring DM. My understanding is that as of version 2.0, Spring DM will deprecate support for web bundles and Tomcat / Jetty integration in favour of supporting official OSGi standards. Basically, this means that Spring DM will expect your OSGi container to provide these services (RFC-66 specifically). Make sure to use Spring DM 1.2.x or above, but not 2.x.

Apache Wicket

Wicket makes it cool to be a Java developer again, seriously! Apache Wicket is simply the most fun and productive UI framework I’ve ever worked with. Yes, that’s a strong statement, but it’s true! I wish Wicket happened a decade ago. Until now Java developers have been burdened with overly complex view technologies (like JSF) that almost guaranteed the success of lightweight frameworks like RoR and Grails.

Beyond the cool factor, Wicket finally gives us Java-heads an opportunity to take a deep breath and harness the epic (and underutilized) powers of our tasty Java + OOA/D knowledge… and we can wield these powers on the UI now. Holy doodle!

Tutorial (On with the show!)

Before we go much further, I assume you have a working-level knowledge of both OSGi and Wicket and are looking to wire them together without using any magic. If you’re completely new to OSGi, I recommend reading this article.

And don’t forget to check out the Wicket site if you’re new to Wicket.

Step 1: Get your OSGi container Tomcat-ified

We’re going to make a few assumptions. The first is that you’re going to use Pax Construct rather than create OSGi bindings and configuration files by hand, the second is that you’re going to use Apache Felix as your OSGi container, and the third is that you’re going to use Tomcat as your Java/JSP container.

Pax Construct is like the Yoda of OSGi development. It simply does stuff. Epic stuff. Learn it, know it, love it. There is also a project called Pax Wicket that you may want to check out. It accomplishes a different goal that what I’m going for, which is simply trying to decide if A) Wicket would even work in an OSGi container, and B) If it was worth the effort to explore deeper. The answer is yes, and yes!

Step 2: Creating a new OSGi project

Okay, so enough with the preamble. Let’s get rolling.

  • Download and install the latest version of Pax Construct from here. At the time of writing, the latest version is 1.4.
  • Make sure you have Maven installed and configured properly.
  • Execute the following at your command line to create your OSGi project:
    pax-create-project -g org.itypeincaps -a osgilywicket -v 1.0-SNAPSHOT
  • Add Java 1.5 support to your Maven POM. Edit the pom.xml file in osgilywicket root and add:
    <plugin>
       <groupId>org.apache.maven.plugins</groupId>
       <artifactId>maven-compiler-plugin</artifactId>
       <configuration>
         <source>1.5</source>
         <target>1.5</target>
       </configuration>
    </plugin>

Step 3: Add the SpringSource OSGi bundle repository

A significant number — the majority — of dependencies are not distributed as proper OSGi bundles. However, SpringSource has a repository with a number of OSGi bundles.

Check to see if the version of your dependency exists in the SpringSource repository first. For instance, if you want to use version 1.4.3 of Wicket, check SpringSource first. If it exists, fantastic. If it doesn’t, you’ll need to use Pax Construct to wrap the non-OSGi JAR obtained from the original source. Before we worry about wrapping JARs, we need to make a few changes to our Maven build file to locate the proper repositories.

  • Cut and paste (or use the pax-add-repository command) to add the following repositories to the pom.xml file in your main project folder:
    <repositories>
      <repository>
        <id>com.springsource.repository.bundles.external</id>
        <name>SpringSource Enterprise Bundle Repository - External Bundle Releases</name>
        <url>http://repository.springsource.com/maven/bundles/external</url>
      </repository>
      <repository>
        <id>com.springsource.repository.bundles.release</id>
        <name>SpringSource Enterprise Bundle Repository - SpringSource Bundle Releases</name>
        <url>http://repository.springsource.com/maven/bundles/release</url>
      </repository>
      <repository>
        <id>spring-osgi</id>
        <url>http://s3.amazonaws.com/maven.springframework.org/osgi</url>
      </repository>
    </repositories>
  • Execute the following four commands at the command line:
    pax-import-bundle -g org.apache.commons -a com.springsource.org.apache.commons.logging -v 1.1.1
    pax-import-bundle -g org.springframework.osgi -a catalina.osgi -v 6.0.16-SNAPSHOT
    pax-import-bundle -g org.springframework.osgi -a catalina.start.osgi -v 1.0.0
    pax-import-bundle -g org.springframework.osgi -a servlet-api.osgi -v 2.5-SNAPSHOT
  • Now you should have a functioning Tomcat instance running in your OSGi container. To test:
    mvn pax:provision
  • Point a browser at http://localhost:8080 and you should get a blank page. If you do, you’re ready to move forward. The blank page is good. If you get anything other than a blank page, uh-oh! Game over? Not quite, inspect your console output.

Step 4: Wicket up that container!

If you take a look in the SpringSource OSGi repository, you’ll notice a version of Wicket exists. Unfortunately, it’s old. Real old. 1.3.3 to be exact. So the real question is how do you bundle the latest version of Wicket so it works properly in OSGi-land?

We need to build our own OSGi-fied Wicket bundle, of course!

We’ll use Pax Construct to help us get off on the right foot. pax-wrap-jar is a handy feature that allows us to wrap a regular JAR as an OSGi JAR by properly creating an OSGi manifest. Luckily, Pax Construct does this for us (or at the very least gets us off on the right foot).

  • Wrap the latest version of Wicket (which is 1.4.3 at the moment of this writing):
    pax-wrap-jar -g org.apache.wicket -a wicket -v 1.4.3
  • We’ll need to make some changes though. The manifest that pax-wrap-jar creates is virtually empty and not quite what we’ll need for Wicket to properly work as a bundle. maven-bundle-plugin to the rescue! If you want to learn more read this article. Otherwise, let’s proceed to wrapping a JAR.

Step 5: Wrapping a JAR

Most of the time executing pax-wrap-jar is sufficient for getting a regular JAR ready to be dropped into your OSGi container. But I always like to understand the library I’m using, and most importantly figure out what packages it needs to import and export. It’s possible Wicket will work fine without the next steps, but I recommend doing this simply as a learning exercise.

Instead of starting from scratch, we’ll take a look at the manifest in the OSGi-fied Wicket JAR at SpringSource.

The next three steps are optional. If you want to fast-track, skip them and proceed to editing your pom.xml.

  • Download the binary version and extract it.
  • Take a look at the manifest, it will give us a very good idea about what imports need to be done. Don’t worry about exports, because the maven-bundle-plugin will help us take care of that.
  • If you try copying the list of imports as-is, it will fail. So we’ll need to add sun.misc and sun.reflect to the imports. We’ll make these optional, because the sun.* packages are only available on Sun JDKs, and I like to keep things vendor agnostic whenever possible.

If you hack around on your own, you’ll probably wind up with a maven-build-plugin configuration like below. This will go inside the pom.xml created inside the wrapped JAR folder.

  • When you wrapped the jar, it created a new folder called org.apache.wicket in your OSGi project root. This folder contains a pom.xml file.
  • Copy the plugin config below to the pom.xml file:
    <build>
      <plugins>
        <plugin>
          <groupId>org.apache.felix</groupId>
          <artifactId>maven-bundle-plugin</artifactId>
          <version>1.4.0</version>
          <extensions>true</extensions>
          <configuration>
            <instructions>
              <Bundle-SymbolicName>${pom.groupId}.${pom.artifactId}
              </Bundle-SymbolicName>
              <Bundle-Name>${pom.name}</Bundle-Name>
              <Bundle-Version>${pom.version}</Bundle-Version>
              <Bundle-ClassPath>.</Bundle-ClassPath>
              <Import-Package>sun.misc;resolution:=optional,
                sun.reflect;resolution:=optional,
                javax.crypto,javax.crypto.spec,javax.imageio,
                javax.portlet;resolution:=optional,javax.servlet;version="[2.5.0,
                3.0.0)", javax.servlet.http;version="[2.5.0, 3.0.0)",
                javax.swing.event,javax.swing.text,javax.swing.tree,
                javax.xml.parsers,javax.xml.transform,javax.xml.transform.stream,
                junit.framework;version="[3.8.2, 4.0.0)";resolution:=optional,
                org.slf4j;version="[1.5.0, 2.0.0)",org.w3c.dom,org.xml.sax
              </Import-Package>
            </instructions>
          </configuration>
        </plugin>
      </plugins>
    </build>
  • Try an mvn install from the wrapped JAR folder (org.apache.wicket). If everything goes smoothly, execute an mvn pax:provision from the project root folder. Ensure the bundle starts up successfully and the Wicket bundle is active.

We’ll need to resolve some runtime issues before going much further. If you fire up your OSGi container, it looks like slf4j is going to be an issue, so let’s add it. If you take a look at the import package instructions, it appears that Wicket depends on 1.5.0. Lucky, there’s an already OSGi-fied version at SpringSource.

  • Execute the following command:
    pax-import-bundle -g org.slf4j -a com.springsource.slf4j.jcl -v 1.5.0 -- -DimportTransitive -DwidenScope
  • It’s very important that you don’t import two different slf4j bindings at the same time, otherwise you’ll receive a nasty StackOverflowError surprise. Check out the slf4j manual for more info.
  • Now if you run mvn pax:provision, everything should start up nicely.
  • We also need to make sure Tomcat can handle JSP, as Wicket requires this. Execute the following two statements on the command line:
    pax-import-bundle -g org.springframework.osgi -a jasper.osgi -v 6.0.16-SNAPSHOT
    pax-import-bundle -g org.mortbay.jetty -a jsp-api-2.1 -v 6.1.14

Step 6: Install Spring DM

If we just try to go ahead and fire up our webapp without Spring DM, nothing very exciting will happen. We need to install the Spring DM web extender; that’s where the real magic happens. To make a long story short, the Spring DM web extender checks for WAR bundles deployed to your OSGi container and automatically deploys them to embedded Tomcat or Jetty instance. It’s the glue that ties your WAR bundle to your JSP/Servlet container in the world of OSGi. An OSGi standard does exist so in the future containers will take care of this themselves, but for now, Apache Felix does not support this automatically. Just remember that it doesn’t matter if your app does not using the Spring framework, you still need Spring DM.

  • First, install the Spring DM Web Extender with the Maven switch to pull in all dependencies. We’ll also install the Spring Web bundle.
    pax-import-bundle -g org.springframework.osgi -a spring-osgi-web-extender -v 1.2.0 -- -DimportTransitive -DwidenScope
    pax-import-bundle -g javax.el -a com.springsource.javax.el -v 1.0.0
    pax-import-bundle -g org.springframework -a org.springframework.web -v 2.5.6.A

Step 7: Creating our Hello World bundle!

Now we’re going to create a new WAR bundle. We’ll create our Hello World OSGi bundle using Pax Construct. But we’ll need to modify the automatically generated Maven build settings in order to properly package our OSGi bundle as a WAR file instead of the default JAR file. Luckily, there is a Maven plugin available that makes this task fairly trivial.

  • Execute the following command to create a new Wicket Hello World project:
    pax-create-bundle -p osgilywicket.web -n wicket-helloworld.war -g org.itypeincaps -v 1.0-SNAPSHOT
  • Spring DM used to rely on the project name ending with .war in order for it to pick up and deploy it properly. You may be able to name it something else, but I can’t guarantee this will work. If anyone can verify if this is no longer the case, please leave a comment. :)
  • Next we’ll make the necessary modifications for WAR packaging. The main thing to understand is how to package your bundle as a war file. If you’d like more details about this, check out this article.
  • Add the following to the pom.xml file in the your Hello World bundle root folder:
    <build>
      <plugins>
        <plugin>
          <groupId>org.apache.felix</groupId>
          <artifactId>maven-bundle-plugin</artifactId>
          <executions>
            <execution>
              <id>bundle-manifest</id>
              <phase>process-classes</phase>
              <goals>
                <goal>manifest</goal>
              </goals>
            </execution>
          </executions>
          <configuration>
            <supportedProjectTypes>
              <supportedProjectType>jar</supportedProjectType>
              <supportedProjectType>bundle</supportedProjectType>
              <supportedProjectType>war</supportedProjectType>
            </supportedProjectTypes>
            <instructions>
              <Bundle-SymbolicName>${bundle.symbolicName}</Bundle-SymbolicName>
              <Bundle-Version>${pom.version}</Bundle-Version>
              <Export-Package>${bundle.namespace}.*;version="${pom.version}"</Export-Package>
              <_include>-osgi.bnd</_include>
            </instructions>
          </configuration>
        </plugin>
        <plugin>
          <artifactId>maven-war-plugin</artifactId>
          <configuration>
            <archive>
              <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestfile>
            </archive>
          </configuration>
        </plugin>
      </plugins>
    </build>
  • Change the packaging type from bundle to war in pom.xml:
    <packaging>war</packaging>
  • Make Wicket available to the webapp by adding this to the pom.xml:
    <dependency>
      <groupId>org.apache.wicket</groupId>
      <artifactId>wicket</artifactId>
      <version>1.4.3</version>
      <scope>provided</scope>
    </dependency>
  • Create the following web.xml under src/main/webapp/WEB-INF:
    <?xml version="1.0" encoding="ISO-8859-1"?>
      <web-app xsi:schemalocation="http://java.sun.com/xml/ns/j2ee
        http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" version="2.4"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns="http://java.sun.com/xml/ns/j2ee">
     
      <display-name>HelloWorld</display-name>
     
      <context-param>
        <param-name>contextClass</param-name>
        <param-value>org.springframework.osgi.web.context.support.OsgiBundleXmlWebApplicationContext</param-value>
      </context-param>
     
      <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
      </listener>
     
      <filter>
        <filter-name>wicket.wicketTest</filter-name>
        <filter-class>org.apache.wicket.protocol.http.WicketFilter</filter-class>
        <init-param>
          <param-name>applicationClassName</param-name>
          <param-value>org.itypeincaps.osgilywicket.WicketApplication</param-value>
        </init-param>
      </filter>
     
      <filter-mapping>
        <filter-name>wicket.wicketTest</filter-name>
        <url-pattern>/*</url-pattern>
      </filter-mapping>
     
    </web-app>
  • Create the following applicationContext.xml under src/main/webapp/WEB-INF. For some reason I was having a hell of a time with XSD validation (more on this later), so I grabbed this from one of Matt Riable’s excellent demos as a sanity check just to make sure I wasn’t losing my mind. ;)
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xsi:schemalocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
        http://www.springframework.org/schema/osgi
        http://www.springframework.org/schema/osgi/spring-osgi.xsd" xmlns:osgi="http://www.springframework.org/schema/osgi" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans">
     
        <!--osgi:reference id="jobsBPS"
            interface="org.appfuse.bps.api.MyBPS" timeout="10000" cardinality="0..1"/-->
     
    </beans>
  • Edit osgi.bnd to import all necessary packages. In order for our XML config files to pass XSD validation we need to make the factory package available:
    Bundle-ClassPath: .,WEB-INF/classes
    Import-Package: *,org.springframework.web.context,\
    org.springframework.osgi.web.context.support,\
    org.springframework.beans.factory.xml
    Web-ContextPath: osgi
  • Optional step. Take note of line 4 above. For those of us behind a proxy or without a connection to the internet, a strange anomaly exists with Spring that makes it impossible for the applicationContext schema to validate. This package may not be required for everyone, but I’m going to put it here for the heck of it. Plus, I really dislike the idea of relying out external resources simply to fire up my app, so this ensures we keep all validation dependencies local. Just keep in mind as you build your app, you should include the package names of your XSD files required by any Spring XML configuration files. If you don’t, your container will connect to the net during XSD validation, which may significantly increase the amount of time required during app startup. Keep in mind that for those of us behind a proxy, your OSGi container will need to be properly configured to support your proxy settings. For those using Apache Felix, take a look at the Apache Felix usage documentation.
  • Optional step. As above, an error exists when trying to validate schemas with no web connection available. Those of us behind a proxy are the most likely to notice the problem. The workaround is to extract spring.handlers and spring.schemas and place them in the src/main/webapp/META-INF folder of your bundle. This is a particularly distasteful kludge, so hopefully the issue is resolved and this step is unnecessary by the time you’re reading this. :)

Now we need to create the standard Wicket stuff in order for our page to actually do anything. This step will seem fairly trivial compared to everything we’ve done so far. We’ll create three new files in the source folder of our project using the package name org.itypeincaps.osgilywicket. We’ll need our main Wicket application file (WicketApplication.java), and our component Java/HTML pair (Index.java and Index.html).

  • WicketApplication.java
    package org.itypeincaps.osgilywicket;
     
    import org.apache.wicket.Page;
    import org.apache.wicket.protocol.http.WebApplication;
     
    public class WicketApplication extends WebApplication {
     
        @Override
        public Class<? extends Page> getHomePage() {
            return Index.class;
        }
    }
  • Index.java
    package org.itypeincaps.osgilywicket;
     
    import org.apache.wicket.markup.html.WebPage;
    import org.apache.wicket.markup.html.basic.Label;
     
    public class Index extends WebPage {
        /**
         * Constructor
         */
        public Index()
        {
            add(new Label("message", "Hello OSGi-land!"));
        }
    }
  • Index.html
    <html>
        <head>
            <title>Wickety OSGi!</title>
        </head>
        <body>
            <p wicket:id="message">replace me</p>
        </body>
    </html>

The moment of truth has arrived. Execute an mvn install in your bundle folder, execute another mvn install in your OSGi (root) project folder, execute an mvn pax:provision to fire up your container, and see if it works. Fingers crossed!

Step 8: Fire it up!

It looks like we’re finished! Go ahead and launch your OSGi container, open your browser, and point to http://localhost:8080/osgi. If you see Hello World!, that means our great success journey has come to a successful conclusion. If not, download the full working example, peer review at your convenience, and let me know if I forget a step somewhere along the way.

I used TextMate as my editor when writing this tutorial. If you’d like to use Eclipse, execute the following command to create your project files. Then you can simply import your Hello World bundle as an existing project.

mvn pax:eclipse -DdownloadSources=true -DdownloadJavadocs=true

Final thoughts on OSGi and Wicket

If you made it this far, congratulations. :) You may also be wondering if this “stack” is ready for prime-time, enterprise, mission critical production applications. The short answer is no. The long answer is not quite yet.

OSGi does a number of things amazingly. I won’t go into the benefits of OSGi, because I assume if you got this far you already know what they are. :p The problem isn’t with OSGi as a technology, the problem is with the adoption rate of vendors. If the big commercial app server vendors — mainly IBM and Oracle (BEA) — would provide world-class OSGi support in their products for developers, I think a lot of us would jump all over OSGi. As you may have figured out from this tutorial, putting together a functional OSGi stack with your favourite UI framework is a bit of a hassle. And we’ve only tackled a development stack. Putting something like this into production would be a disaster without some major securing and planning for load + availability. The other option of embedding OSGi inside your webapp (which is the complete opposite of what we did here) is also simply too kludge-y for most organizations to greenlight.

With all of that being said, I wouldn’t hesitate to use a Wicket/Spring/OSGi stack for complex internal applications. (For trivial internal applications, OSGi is overkill; use PHP or RoR or Grails or… something else!)

Moving into 2010, dynamic Java is going to play a major role in new enterprise development. Even though we have to do a lot more work than we should to get our environment ready, the benefits of OSGi are enormous, and the configuration tedium will slowly die down as tooling and server support become more mature.

Categories: OSGi, Spring DM, Wicket.

Share this with friends:

retweet digg this delicious stumbleupon reddit
blog comments powered by Disqus