I'm a software developer from Toronto, Canada.
I strive to deliver applications that exceed expectations.
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.
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.
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!
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.
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!
Okay, so enough with the preamble. Let’s get rolling.
pax-create-project -g org.itypeincaps -a osgilywicket -v 1.0-SNAPSHOT
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>
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.
<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>
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
mvn pax:provision
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).
pax-wrap-jar -g org.apache.wicket -a wicket -v 1.4.3
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.
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.
<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>
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.
pax-import-bundle -g org.slf4j -a com.springsource.slf4j.jcl -v 1.5.0 -- -DimportTransitive -DwidenScope
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
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.
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
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.
pax-create-bundle -p osgilywicket.web -n wicket-helloworld.war -g org.itypeincaps -v 1.0-SNAPSHOT
<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>
<packaging>war</packaging>
<dependency> <groupId>org.apache.wicket</groupId> <artifactId>wicket</artifactId> <version>1.4.3</version> <scope>provided</scope> </dependency>
<?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>
<?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>
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
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).
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; } }
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!")); } }
<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!
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
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.