A shell-less OSGi shell
Today from my tweetstream I read from @njbartlett about a new OSGi enterprise initiative. My interest heightened when I saw a line in the press release about a bash-like shell called Posh. Very clever name! I took a peek at the Paremus documentation on the shell. Very full featured and comes the closest yet I think to a real shell environment for OSGi. This seems like a good time to talk about my experiements with a shell-less OSGi shell.
Early on at Bug when I started to get serious about OSGi, I found that using the shells included with Equinox and Concierge helped a great deal in understanding runtime dynamics and structure. The shell is invaluable in how it can shed light into how the system is running, how services and packages are associated, and of course being able to change the state and debug dependency issues. I ended up writing my own shell just so I could Have It My Way, which was also a good learning exercise. But, there are problems in general with OSGi shell systems no matter good they get. They are isolated from the operating system; walled gardens. Additionally while they often support a subset of real shell commands and constructs, they are cut off from a real shell session, making utility limited. Conversely, the more full featured a shell environment is, the more frustrating it is that it’s not the native shell. And of course each shell environment has it’s own commands, behaviors, and quirks. So, on BUG I wanted to get rid of the OSGi shell, and trade it for something more natural for a Linux environment. I like the “everything is a file” philosophy and have seen it put to great and simple use in Linux with sysfs. I needed to come up with a system that does the three things that OSGi shells do:
- Inspect the state of a running OSGi system.
- Install/start/stop/remove bundles from running system.
- Make system changes (runlevel, shutdown, etc.)
Using the Linux init system as found on my Ubuntu Linux dev machine as inspiration, I created a mock-up filesystem layout like this:
/installed /runlevel-1 (...n) /status /control
The idea being that “/installed” would be like /etc/init.d, holding all the programs or OSGi bundles that would be installed in this particular OSGi instance. And then, similiarly to /etc/rc3.d, /runlevel-x would have symlinks to bundles living in /installed that should be set to run when the framework runlevel is set appropriately. To stop a bundle I simply remove the symlink from the runlevel directory. Also, unlike the rc init system, the filesystem would be periodically scanned for changes, so if a new bundle goes into installed, the running OSGi instance is just updated.
Next, with inspiration from the Linux sysfs filesystem, using out-only pipes, I created files (such as /status/services) that would return OSGi state information to the caller. So from my (real) shell I could say “cat /status/bundle/23/headers” and it would return me the MANIFEST.MF contents, essentially, of bundle ID 23. This worked pretty well and I was able to quickly make use of this, grepping for services, packages, installing things in specific places. All of this being done from a native shell environment, using my native tools that I’m familiar with and having access to things like scp, cron jobs, etc..
For a control mechanism, I created some write-only pipes that would let me make state changes to the running system. For example to “bounce” a bundle: “echo 1 > /control/bundle/12/bounce” would restart bundle 23. Shutting the framework down is simply: “echo 1> /control/shutdown”.
Lastly taking cues from bnd, I made the bundle have multiple entry points, creating a simple OSGi installer for when the bundle is called from a Java context. This let me quickly setup a new framework layout quickly. For now it only works for Felix.
So, with this implemented I can now remove my OSGi shell and treat it just like another subsystem. The prototype implementation has some problems. It lacks an extension mechanism to allow other bundles to contribute files and behavior. The pipe mechanism requires a thread per-file. And there are times when the symlink behavior is awkward, like when a bundle fails to start (current policy: delete the symlink). And of course if you’re using Windows your probably out of luck.
The prototype implementation is named “osgifs” and source is available at svn://bugcamp.net/bug/trunk/com.buglabs.osgi.filesystem and the binary is at http://bugcommunity.com/downloads/files/osgifs.jar . To get started simply copy the jar to an empty directory, run “java -jar osgifs.jar”. This will download felix and setup the initial directories. Run the start script and from that point osgifs will represent the runtime system somewhat like a filesystem.
What’s next? For efficiency it may be better to rely on INotify instead of filesystem polling, and eventually switch to FUSE and make the entire thing virtual. Other ideas include a nested-framework mechanism and transparently OSGi-ifing dumb jars. At this point though I’m mainly curious if anyone has written something like this before, and if there is any other feedback?