Craig Walls has been professionally developing software for over 14 years (and longer than that for the pure geekiness of it). He is the author of Spring in Action (now in its second edition) and XDoclet in Action, both published by Manning and is currently writing about OSGi and Spring-DM. Craig has posted 9 posts at DZone. View Full User Profile

Launching OSGi With Pax Runner

04.30.2009
| 13364 views |
  • submit to reddit

In last week's article, we saw how to go from zero to working OSGi bundles quickly using Pax Construct. We've only scratched the surface of Pax Construct and we'll look at it some more in a future article. This week, however, we're going to uncover the magic behind Pax Construct's pax-provision script: Pax Runner.

OSGi Without Pax Runner

In the classic Christmas movie, It's a Wonderful Life, the protagonist George Bailey gets a chance to see what the world would've been like without him. Similarly, to gain an appreciation of what Pax Runner offers, we're going to start by seeing what a world without Pax Runner is like.

Let's say that you wanted to start Equinox and to install and start our hello world bundle from last week. First, you'd need to download Equinox. Assuming that you choose Equinox 3.4 (the latest release), then we'd start Equinox on the command line like this:

~/equinox% java -jar org.eclipse.osgi_3.4.0.v20080605-1900.jar 
~/equinox%

Oops! That didn't do much of anything. That's because Equinox has its console turned off by default and without any other bundles installed yet, it shuts down almost as soon as it starts. No problem--we'll just start it with the console turned on:

~/equinox% java -jar org.eclipse.osgi_3.4.0.v20080605-1900.jar -console

osgi>

Okay, this time we get the osgi> prompt, which is the Equinox console. From here we can do all sorts of things, including getting a short status of all of the bundles installed:

osgi> ss

Framework is launched.

id State Bundle
0 ACTIVE org.eclipse.osgi_3.4.0.v20080605-1900

osgi>

As you can see, there's not a lot installed. The only bundle available is the Equinox runtime itself. So that means that our next step will be to install our hello world bundle. For that, Equinox provides the install command:

osgi> install file:/Users/wallsc/Projects/sandbox/helloworld/hello-simple/target/hello-simple-1.0-SNAPSHOT.jar
Bundle id is 1

osgi>

Of course, unless you coincidentally have your path setup exactly like mine, you'll need to adjust the file: URL appropriately. Optionally, you can install the bundle using an http: URL:

osgi> http://www.habuma.com/osgi/bundles/hello-simple-1.0-SNAPSHOT.jar
Bundle id is 1

osgi>

The install command takes a URL that points to a bundle JAR file to install. The URL can either be a file: or http: URL. In case you're wondering...yes, the bundle is available at that http: URL.

For more URL options beyond file: and http:, check out Pax URL.

Now the hello world bundle is installed, but it hasn't been started. Let's start it:

osgi> start 1
Hello World!

osgi>

There's our "Hello World!" greeting, indicating that it worked. Now let's review the steps we took to get to this point:

  1. Downloaded Equinox
  2. Started Equinox (with console on)
  3. Installed a bundle
  4. Started a bundle

That wasn't so difficult. But then again, we only installed one bundle. Imagine what it may have been like to install several dozen bundles. And what would it take to try this all over again with a different version of Equinox or even a different OSGi implementation like Felix or Knopflerfish? It seems like we'd have to go through most or all of those same steps again while dealing with the slight differences between the various OSGi implementations.

And that's where Pax Runner shines.

Quick start OSGi with Pax Runner

Pax Runner, put simply, is a OSGi framework launcher. But don't let that concise description fool you--There's a lot more to Pax Runner than meets the eye. Per the Pax Runner home page, Pax Runner is great if you want to change from OSGi platform to another, you're new to OSGi and want to spend a short time checking it out, you don't want to be bothered with the setup and requirements of each OSGi platform. In short, Pax Runner makes it easy to start any OSGi platform with any selection of bundles...and then switch to another platform and bundle selection painlessly.

To get started with Pax Runner, first download it (I'm using version 0.18.0) and unzip it. On my MacBook Pro, that looks like this:

pax% unzip ~/Downloads/pax-runner-assembly-0.18.0-jdk15.zip 
Archive: /Users/wallsc/Downloads/pax-runner-assembly-0.18.0-jdk15.zip
creating: pax-runner-0.18.0/
creating: pax-runner-0.18.0/bin/
inflating: pax-runner-0.18.0/bin/pax-runner-0.18.0.jar
inflating: pax-runner-0.18.0/bin/pax-run.bat
inflating: pax-runner-0.18.0/bin/pax-run.sh
pax%

As you can see, Pax Runner comes with a JAR file, an MS-DOS batch file, and a Unix shell script. I'll be using the Unix shell script in this article, but if you're on Windows then the batch file is for you. In either event, add the bin directory to your system path and you're ready to roll with Pax Runner.

We'll start with baby steps. Let's run Pax Runner to see what we get out of the box:

~/osgi-fun% pax-run.sh 
______ ________ __ __
/ __ / / __ / / / / /
/ ___/ / __ / _\ \ _/
/ / / / / / / _\ \
/__/ /__/ /__/ /_/ /_/

Pax Runner (0.18.0) from OPS4J - http://www.ops4j.org
-----------------------------------------------------

-> Using config [classpath:META-INF/runner.properties]
-> Using only arguments from command line
-> Preparing framework [Felix 1.6.0]
-> Downloading bundles...
-> Felix 1.6.0 : 368418 bytes @ [ 601kBps ]
-> org.osgi.compendium (4.1.0) : 514214 bytes @ [ 5194kBps ]
-> org.apache.felix.shell (1.2.0) : 59114 bytes @ [ 687kBps ]
-> org.apache.felix.shell.tui.plugin (1.2.0) : 12455 bytes @ [ 1245kBps ]
-> Using execution environment [J2SE-1.6]
-> Runner has successfully finished his job!


Welcome to Felix.
=================

->

As you can see, Pax Runner start Felix 1.6.0. If you issue the ps command, you'll see that it's a fairly basic Felix runtime:

-> ps
START LEVEL 6
ID State Level Name
[ 0] [Active ] [ 0] System Bundle (1.6.0)
[ 1] [Active ] [ 1] osgi.compendium (4.1.0.build-200702212030)
[ 2] [Active ] [ 1] Apache Felix Shell Service (1.2.0)
[ 3] [Active ] [ 1] Apache Felix Shell TUI (1.2.0)
->

At this point, we could use the install command to install a bundle. But that wouldn't be any different than how we'd do it without Pax Runner. So, get out of Felix (issue the shutdown command) and then run Pax Runner like this:

~/osgi-fun% pax-run.sh file:/Users/wallsc/Projects/sandbox/helloworld/hello-simple/target/hello-simple-1.0-SNAPSHOT.jar
______ ________ __ __
/ __ / / __ / / / / /
/ ___/ / __ / _\ \ _/
/ / / / / / / _\ \
/__/ /__/ /__/ /_/ /_/

Pax Runner (0.18.0) from OPS4J - http://www.ops4j.org
-----------------------------------------------------

-> Using config [classpath:META-INF/runner.properties]
-> Using only arguments from command line
-> Scan bundles from [file:/Users/wallsc/Projects/sandbox/helloworld/hello-simple/target/hello-simple-1.0-SNAPSHOT.jar]
-> Scan bundles from [scan-bundle:file:/Users/wallsc/Projects/sandbox/helloworld/hello-simple/target/hello-simple-1.0-SNAPSHOT.jar]
-> Provision bundle [file:/Users/wallsc/Projects/sandbox/helloworld/hello-simple/target/hello-simple-1.0-SNAPSHOT.jar, at default start level, bundle will be started, bundle will be loaded from the cache]
-> Preparing framework [Felix 1.6.0]
-> Downloading bundles...
-> file:/Users/wallsc/Projects/sandbox/helloworld/hello-simple/target/hello-simple-1.0-SNAPSHOT.jar : 3677 bytes @ [ 1838kBps ]
-> Using execution environment [J2SE-1.6]
-> Runner has successfully finished his job!


Welcome to Felix.
=================

-> Hello World!

With one simple command line entry, we were able to start Felix, install our hello world bundle, and start that bundle.

Now let's try that with Equinox instead of Felix. To do that, all we need to do is add a new parameter to the command line:

~/osgi-fun% pax-run.sh <b>--platform=equinox</b> file:/Users/wallsc/Projects/sandbox/helloworld/hello-simple/target/hello-simple-1.0-SNAPSHOT.jar
______ ________ __ __
/ __ / / __ / / / / /
/ ___/ / __ / _\ \ _/
/ / / / / / / _\ \
/__/ /__/ /__/ /_/ /_/

Pax Runner (0.18.0) from OPS4J - http://www.ops4j.org
-----------------------------------------------------

-> Using config [classpath:META-INF/runner.properties]
-> Using only arguments from command line
-> Scan bundles from [file:/Users/wallsc/Projects/sandbox/helloworld/hello-simple/target/hello-simple-1.0-SNAPSHOT.jar]
-> Scan bundles from [scan-bundle:file:/Users/wallsc/Projects/sandbox/helloworld/hello-simple/target/hello-simple-1.0-SNAPSHOT.jar]
-> Provision bundle [file:/Users/wallsc/Projects/sandbox/helloworld/hello-simple/target/hello-simple-1.0-SNAPSHOT.jar, at default start level, bundle will be started, bundle will be loaded from the cache]
-> Preparing framework [Equinox 3.4.2]
-> Downloading bundles...
-> Equinox 3.4.2 (v20081215-1030) : 997240 bytes @ [ 493kBps ]
-> Eclipse utilities : 22755 bytes @ [ 948kBps ]
-> Eclipse compendium services : 63704 bytes @ [ 2275kBps ]
-> Using execution environment [J2SE-1.6]
-> Runner has successfully finished his job!


osgi> Hello World!

And just like that, Pax Runner started Equinox 3.4.2 then installed and started the hello world bundle.

Wanna try Knopflerfish?

~/osgi-fun% pax-run.sh --platform=knopflerfish file:/Users/wallsc/Projects/sandbox/helloworld/hello-simple/target/hello-simple-1.0-SNAPSHOT.jar
______ ________ __ __
/ __ / / __ / / / / /
/ ___/ / __ / _\ \ _/
/ / / / / / / _\ \
/__/ /__/ /__/ /_/ /_/

Pax Runner (0.18.0) from OPS4J - http://www.ops4j.org
-----------------------------------------------------

-> Using config [classpath:META-INF/runner.properties]
-> Using only arguments from command line
-> Scan bundles from [file:/Users/wallsc/Projects/sandbox/helloworld/hello-simple/target/hello-simple-1.0-SNAPSHOT.jar]
-> Scan bundles from [scan-bundle:file:/Users/wallsc/Projects/sandbox/helloworld/hello-simple/target/hello-simple-1.0-SNAPSHOT.jar]
-> Provision bundle [file:/Users/wallsc/Projects/sandbox/helloworld/hello-simple/target/hello-simple-1.0-SNAPSHOT.jar, at default start level, bundle will be started, bundle will be loaded from the cache]
-> Preparing framework [Knopflerfish 2.2.0]
-> Downloading bundles...
-> Knopflerfish 2.2.0 : 335103 bytes @ [ 246kBps ]
-> org.osgi.compendium : 689150 bytes @ [ 1534kBps ]
-> org.knopflerfish.bundle.console : 36334 bytes @ [ 46kBps ]
-> org.knopflerfish.bundle.consoletty : 6170 bytes @ [ 1028kBps ]
-> org.knopflerfish.bundle.frameworkcommands : 26090 bytes @ [ 147kBps ]
-> Using execution environment [J2SE-1.6]
-> Runner has successfully finished his job!

Knopflerfish OSGi framework, version 4.0.12
Copyright 2003-2008 Knopflerfish. All Rights Reserved.

See http://www.knopflerfish.org for more information.
Loading xargs url file:knopflerfish/config.ini
Installed and started: file:bundles/osgi.compendium_4.0.1.jar (id#1)
Installed and started: file:bundles/org.knopflerfish.bundle.console_2.0.1.jar (id#2)
Installed and started: file:bundles/org.knopflerfish.bundle.consoletty-IMPL_2.0.0.jar (id#3)
Installed and started: file:bundles/org.knopflerfish.bundle.frameworkcommands-IMPL_2.0.5.jar (id#4)
Installed and started: file:bundles/com.habuma.osgi.helloworld.hello-simple_1.0.0.SNAPSHOT.jar (id#5)
> Hello World!
Framework launched

>

How about Concierge?

~/osgi-fun% pax-run.sh --platform=concierge file:/Users/wallsc/Projects/sandbox/helloworld/hello-simple/target/hello-simple-1.0-SNAPSHOT.jar
______ ________ __ __
/ __ / / __ / / / / /
/ ___/ / __ / _\ \ _/
/ / / / / / / _\ \
/__/ /__/ /__/ /_/ /_/

Pax Runner (0.18.0) from OPS4J - http://www.ops4j.org
-----------------------------------------------------

-> Using config [classpath:META-INF/runner.properties]
-> Using only arguments from command line
-> Scan bundles from [file:/Users/wallsc/Projects/sandbox/helloworld/hello-simple/target/hello-simple-1.0-SNAPSHOT.jar]
-> Scan bundles from [scan-bundle:file:/Users/wallsc/Projects/sandbox/helloworld/hello-simple/target/hello-simple-1.0-SNAPSHOT.jar]
-> Provision bundle [file:/Users/wallsc/Projects/sandbox/helloworld/hello-simple/target/hello-simple-1.0-SNAPSHOT.jar, at default start level, bundle will be started, bundle will be loaded from the cache]
-> Preparing framework [Concierge 1.0.0]
-> Downloading bundles...
-> Concierge 1.0 (RC2) : 86089 bytes @ [ 87kBps ]
-> ch.ethz.iks.concierge.service.tracker : 8635 bytes @ [ 1233kBps ]
-> ch.ethz.iks.concierge.shell : 16007 bytes @ [ 1000kBps ]
-> Using execution environment [J2SE-1.6]
-> Runner has successfully finished his job!

---------------------------------------------------------
Concierge OSGi 1.0_RC1 on Mac OS X 10.5.6 starting ...
---------------------------------------------------------
INSTALLING file:bundles/1296071065_1.0.0.RC2.jar
INSTALLING file:bundles/1441349545_1.0.0.RC2.jar
INSTALLING file:bundles/com.habuma.osgi.helloworld.hello-simple_1.0.0.SNAPSHOT.jar
STARTING file:bundles/1296071065_1.0.0.RC2.jar
STARTING file:bundles/1441349545_1.0.0.RC2.jar

Concierge> STARTING file:bundles/com.habuma.osgi.helloworld.hello-simple_1.0.0.SNAPSHOT.jar
Hello World!
---------------------------------------------------------
Framework started in 0.042 seconds.
---------------------------------------------------------


Concierge>

As you can see, switching between OSGi platforms is a piece of cake with Pax Runner. Switching between versions of a platform is just as easy using the --version parameter. Let's say that we wanted to try this out with an older version of Equinox:

~/osgi-fun% pax-run.sh --platform=equinox --version=3.2.1 file:/Users/wallsc/Projects/sandbox/helloworld/hello-simple/target/hello-simple-1.0-SNAPSHOT.jar
______ ________ __ __
/ __ / / __ / / / / /
/ ___/ / __ / _\ \ _/
/ / / / / / / _\ \
/__/ /__/ /__/ /_/ /_/

Pax Runner (0.18.0) from OPS4J - http://www.ops4j.org
-----------------------------------------------------

-> Using config [classpath:META-INF/runner.properties]
-> Using only arguments from command line
-> Scan bundles from [file:/Users/wallsc/Projects/sandbox/helloworld/hello-simple/target/hello-simple-1.0-SNAPSHOT.jar]
-> Scan bundles from [scan-bundle:file:/Users/wallsc/Projects/sandbox/helloworld/hello-simple/target/hello-simple-1.0-SNAPSHOT.jar]
-> Provision bundle [file:/Users/wallsc/Projects/sandbox/helloworld/hello-simple/target/hello-simple-1.0-SNAPSHOT.jar, at default start level, bundle will be started, bundle will be loaded from the cache]
-> Preparing framework [Equinox 3.2.1]
-> Downloading bundles...
-> Equinox 3.2.1 (R32x_v20060717) : 847974 bytes @ [ 438kBps ]
-> Eclipse utilities : 15016 bytes @ [ 1001kBps ]
-> Eclipse compendium services : 51712 bytes @ [ 1149kBps ]
-> Using execution environment [J2SE-1.6]
-> Runner has successfully finished his job!


osgi> Hello World!

Provisioning bundles from the URLs on the command line is only one option that Pax Runner offers. Altogether, there are 8 ways that Pax Runner can be provisioned:

  • From a URL
  • From a text file
  • From a Maven POM file
  • From a ZIP file
  • From the file system
  • From OBR (OSGi Bundle Repository)
  • From Apache ServiceMix features
  • Composite provisioning

I'm not going to go into detail on all of these provisioning options. But let's have a look at a few of the ones I use most often. We've already seen how to provision from a URL, so let's see how to provision from the file system, from a ZIP file, and from a text file.

Provisioning from the file system

Rather than explicitly listing all of the bundles that we want provisioned on the command line, we can point Pax Runner at a directory in the file system and have it load all bundles from that directory.

For example, let's create a directory called mybundles and copy our hello world bundle into the directory:

~/osgi-fun% mkdir mybundles
~/osgi-fun% cp ~/Projects/sandbox/helloworld/hello-simple/target/hello-simple-1.0-SNAPSHOT.jar mybundles
~/osgi-fun%

Now we can start Pax Runner like this:

~/osgi-fun% pax-run.sh mybundles
______ ________ __ __
/ __ / / __ / / / / /
/ ___/ / __ / _\ \ _/
/ / / / / / / _\ \
/__/ /__/ /__/ /_/ /_/

Pax Runner (0.18.0) from OPS4J - http://www.ops4j.org
-----------------------------------------------------

-> Using config [classpath:META-INF/runner.properties]
-> Using only arguments from command line
-> Scan bundles from [scan-dir:mybundles]
-> Provision bundle [file:/Users/wallsc/osgi-fun/mybundles/hello-simple-1.0-SNAPSHOT.jar, at default start level, bundle will be started, bundle will be loaded from the cache]
-> Preparing framework [Felix 1.6.0]
-> Downloading bundles...
-> Using execution environment [J2SE-1.6]
-> Runner has successfully finished his job!


Welcome to Felix.
=================

-> Hello World!

->

Starting Pax Runner this way tells it to look in the mybundles directory and to install/start all bundles it finds in there. In this case, that's only our hello world bundle. But if that directory were chock full of bundles, Pax Runner would install and start every one of them.

Provisioning from a ZIP file

Provisioning from a ZIP file isn't much different than provisioning from a directory. The main difference is that all of the bundles are packaged into a single ZIP file. This makes it convenient to distribute a selection of bundles for easy installation into an OSGi platform.

To try it out, let's create a ZIP file containing the hello world bundle:

~/osgi-fun% zip mybundles.zip hello-simple-1.0-SNAPSHOT.jar 
adding: hello-simple-1.0-SNAPSHOT.jar (deflated 43%)
~/osgi-fun%

Now we can kick off Pax Runner like this:

~/osgi-fun% pax-run.sh mybundles.zip
______ ________ __ __
/ __ / / __ / / / / /
/ ___/ / __ / _\ \ _/
/ / / / / / / _\ \
/__/ /__/ /__/ /_/ /_/

Pax Runner (0.18.0) from OPS4J - http://www.ops4j.org
-----------------------------------------------------

-> Using config [classpath:META-INF/runner.properties]
-> Using only arguments from command line
-> Scan bundles from [mybundles.zip]
-> Scan bundles from [scan-dir:file:/Users/wallsc/osgi-fun/mybundles.zip]
-> Provision bundle [jar:file:/Users/wallsc/osgi-fun/mybundles.zip!/hello-simple-1.0-SNAPSHOT.jar, at default start level, bundle will be started, bundle will be loaded from the cache]
-> Preparing framework [Felix 1.6.0]
-> Downloading bundles...
-> jar:file:/Users/wallsc/osgi-fun/mybundles.zip!/hello-simple-1.0-SNAPSHOT.jar : 3677 bytes @ [ 306kBps ]
-> Using execution environment [J2SE-1.6]
-> Runner has successfully finished his job!


Welcome to Felix.
=================

-> Hello World!

->

Again, the mybundles.zip file contains only a single bundle. But like directory scanning, Pax Runner's ZIP file scanning can pull in and start as many bundles as are contained in the ZIP file.

Provisioning from a text file

So far we've started Pax Runner by specifying a URL to a bundle JAR, a directory, and a ZIP file. We can also start it by giving it a flat text file containing a list of URLs to install bundles from. For example let's create a file named mybundles.txt and put a reference to our hello world bundle in it:

file:/Users/wallsc/Projects/sandbox/helloworld/hello-simple/target/hello-simple-1.0-SNAPSHOT.jar

Then, let's start Pax Runner, giving it this text file:

~/osgi-fun% pax-run.sh mybundles.txt 
______ ________ __ __
/ __ / / __ / / / / /
/ ___/ / __ / _\ \ _/
/ / / / / / / _\ \
/__/ /__/ /__/ /_/ /_/

Pax Runner (0.18.0) from OPS4J - http://www.ops4j.org
-----------------------------------------------------

-> Using config [classpath:META-INF/runner.properties]
-> Using only arguments from command line
-> Scan bundles from [mybundles.txt]
-> Scan bundles from [scan-file:file:/Users/wallsc/osgi-fun/mybundles.txt]
-> Provision bundle [file:/Users/wallsc/Projects/sandbox/helloworld/hello-simple/target/hello-simple-1.0-SNAPSHOT.jar, at default start level, bundle will be started, bundle will be loaded from the cache]
-> Preparing framework [Felix 1.6.0]
-> Downloading bundles...
-> Using execution environment [J2SE-1.6]
-> Runner has successfully finished his job!


Welcome to Felix.
=================

-> Hello World!

->

Note that although the text file contained a file: URL, it could've just as easily have been an http: URL. And, thanks to Pax URL, it can also be a Maven URL, referencing an artifact ID, group ID, and version of the bundle in a Maven repository:

mvn:com.habuma.osgi.helloworld/hello-simple/1.0-SNAPSHOT

As with the other options, we can list multiple bundles in the text file to be provisioned. For example, a more interesting OSGi bundle-based application might include several bundles in its provisioning text file:


To recap, this week we saw how to use Pax Runner to fire up an OSGi platform with one or more bundles already installed and started. We've kept it simple so far, installing a single bundle. But as I've mentioned, adding more bundles to the mix is a simple matter of adding entries to a provisioning file, dropping the bundles in a directory, or packaging them in a ZIP file.

We're just getting started with Pax Runner. I assure you that you'll see more Pax Runner goodness in future blog entries.

In fact, next week I plan to show you how to use profiles, a Pax Runner feature that is based on composite provisioning. You'll see how profiles makes it easy to install several dozen bundles all at once by referring to them by a single logical name. While we're at it, we'll also tinker with Distributed OSGi, a new feature of the upcoming OSGi R4.2 specification. See ya then!

From http://www.jroller.com/habuma

Published at DZone with permission of its author, Craig Walls.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)