Wednesday 2 December 2009

Hello Tatooine

So in my last couple of posts I've been showing the power of Nimble. You will have noticed that it is primarily a console environment. As such you may be wondering how you can provide your own commands to execute in the Nimble shell - Posh (Paremus OSGi Shell).

Posh is an implementation of the command line interface specified in RFC-147 from the OSGi alliance. If you are familiar with OSGi development you will know that to date every framework implementation has defined it's own particular API for providing command line utilities within an OSGi runtime. This has meant that there is significant duplication of effort when writing commands to work in the various environments.

To this end the alliance proposed RFC 147 in order to provide a common standard that different frameworks could implement such that a command that worked in one framework could work unchanged in another. The initial implementation of RFC 147 was developed primarily by Peter Kriens and donated to the Felix project earlier this year. Since then there have been a number of maintenance releases and it has been included as a component of the Felix Karaf container and the Nimble container from Paremus.

This gives you some background, so now the standard thing for me to do would be to write a trivial hello world application. But that's no fun, so instead of conforming to the norm I thought it would be more interesting to port the Starwars Asciimation work to run in OSGi as an RFC 147 command line interface.




Yep this is very probably the geekiest post you will ever see... :)

I hasten to add that I did not undertake the core task of this myself, instead I took the liberty of contacting Simon Jansen (the author of Asciimation) to ask his permission to "borrow" the ascii text from his player. This he was very kind to do.

The first thing we need to do to define our cli is define a class that implements the core functionality as shown below:

package org.chronologicalthought;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.io.Reader;
import java.net.URL;

public class Starwars {
public void starwars() throws IOException, InterruptedException {
play(67);
}

public void starwars(int frameLength) throws IOException, InterruptedException {
URL res = Starwars.class.getResource("/starwars.txt");
if (res == null)
throw new IllegalStateException("Missing resource");
InputStream in = res.openStream();
try {
InputStreamReader reader = new InputStreamReader(new BufferedInputStream(in));
render(reader, System.out, frameLength);
} finally {
in.close();
}
}

private void render(Reader reader, PrintStream out, int frameLength) {
// ...
}
}

Here the command provides two methods, play and play(int) and prints the individual frames from the "starwars.txt" file embedded in our bundle to System.out.

Wait a minute you might be thinking. Where's the API to the CLI? Well this is one of the neat things about RFC 147 you don't need to write your code to any API. The specification provides a clever utility in the form of a ThreadIO service that multiplexes the references to System.in, System.out, and System.err so the command can interact with the user. It also calls methods on the class reflectively so there is no need to implement a defined interface. Simply declare a method and Posh will attempt to convert arguements supplied from the command line to match the method signature.

The next step is to define an activator that publishes our cli class to the OSGi bundle context.

package org.chronologicalthought;

import java.util.Hashtable;
import org.osgi.service.command.CommandProcessor;

import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;

public class Activator implements BundleActivator {

public void start(BundleContext ctx) throws Exception {
Hashtable props = new Hashtable();
props.put(CommandProcessor.COMMAND_SCOPE, "ct");
props.put(CommandProcessor.COMMAND_FUNCTION, new String[] { "starwars" });
ctx.registerService(Starwars.class.getName(), new Starwars(), props);
}

public void stop(BundleContext ctx) throws Exception {
}
}

This activator publishes the Starwars class with two attributes:

  • CommandProcessor.COMMAND_SCOPE - a unique namespace for our command
  • CommandProcessor.COMMAND_FUNCTION - the names of the methods to expose as commands in the cli

The code is available from here for those who want to take a look around:

Now the final stage is to load our command within nimble. Since my cli has such a trivial set of dependencies I will defer the usage of the full nimble resolution to another blog post. So instead I will use a set of trivial commands which I've defined here. So finally let the show commence:

$ svn co http://chronological-thought.googlecode.com/svn/trunk/starwars
$ cd starwars
$ ant
$ posh
Paremus Nimble 30-day license, expires Wed Dec 30 23:59:59 GMT 2009.
________________________________________
Welcome to Paremus Nimble!
Type 'help' for help.
[feynman.local.0]% source http://chronological-thought.googlecode.com/svn/trunk/nimble-examples/basic-commands.osh
[feynman.local.0]% installAndStart file:build/lib/org.chronologicalthought.starwars.jar
[feynman.local.0]% starwars





WWW.ASCIIMATION.CO.NZ


presents




The final piece of Nimble functionality that it would be fun to demonstrate is stopping the movie. Simply hit Ctrl-C. The nimble shell then sends a Thread.interrupt to the currently running command. For those who want to see the movie to the end and don't want to wait try running:

% starwars 20

To set the frame length as 20 milliseconds.

Enjoy the show.

Laters.

Tuesday 1 December 2009

And for my next trick

Just for fun and to demonstrate the power of the Posh (sh)ell environment I decided to knock together the following trivial script to do a "traditional" OSGi bundle file install from a directory:

// create a temporary array for storing ids
array = new java.util.ArrayList;

// iterate over the files passed
// in as arguement 1 to this script
each (glob $1/*) {

// use the BundleContext.installBundle
// method to install each bundle
id=osgi:installBundle $it;

// store the bundle id for start later
$array add $id;
};

// iterate over our installed bundles
each ($array) {
// use the BundleContext.start method
//to start it
osgi:start $it;
};

To try this out for yourself or to find out more about Nimble you look here once installed you can run the above script using the following command:

posh -k http://chronological-thought.googlecode.com/svn/trunk/nimble-examples/file-install.osh <your bundles dir>

Where you should replace <your bundles dir> with a path to a directory on your local file system that contains bundles.

Hmmm what to blog next...ponders...

Laters,

Nimble OSGi

So I just sent a rather cryptic twitter message with the instructions:

posh -kc "repos -l springdm;add org.springframework.osgi.samples.simplewebapp@active"

I figure it's probably worth a short note to explain what this is doing given the narrowband aspect of twitter communications.

This command is running an instance of the posh (sh)ell which ships with Nimble. There are two switch parameters parsed to the shell:

-c: Tells posh to execute the command passed in from the unix shell in the posh (sh)ell environment
-k: Tells posh to remain running after the command has completed and open a tty session for user input

Now we come to the actual commands:

repos -l springdm: tells posh to load the spring dm repository index into the nimble resolver

add org.springframework.osgi.samples.simplewebapp@active: tells nimble to resolve all dependencies for the spring simplewebapp from it's configured repositories.

The interesting thing about nimble resolution is that it doesn't just figure out the bundles that need to be installed. It also figures out what state these bundles should be in. If you look at the bundles in the nimble container using the command lsb you will see that not only are all the bundles installed but certain key bundles have also been activated:

lsb
*nimble/com.paremus.util.cmds-1.0.4.jar 00:00 59Kb
0 ACTIVE org.eclipse.osgi:3.5.1.R35x_v20090827
1 ACTIVE com.paremus.posh.runtime:1.0.4
2 ACTIVE com.paremus.posh.shell:1.0.4
3 RESOLVED com.paremus.util.types:1.0.4
4 ACTIVE com.paremus.nimble.core:1.0.4
5 ACTIVE com.paremus.nimble.repos:1.0.4
6 ACTIVE com.paremus.nimble.cli:1.0.4
7 RESOLVED javax.servlet:2.5.0.v200806031605
8 RESOLVED com.springsource.slf4j.api:1.5.6
9 RESOLVED com.springsource.slf4j.nop:1.5.6
10 RESOLVED com.springsource.net.sf.cglib:2.1.3
11 RESOLVED com.springsource.edu.emory.mathcs.backport:3.1.0
12 RESOLVED org.springframework.osgi.log4j.osgi:1.2.15.SNAPSHOT
13 RESOLVED com.springsource.org.aopalliance:1.0.0
14 RESOLVED org.springframework.osgi.jsp-api.osgi:2.0.0.SNAPSHOT
15 RESOLVED com.springsource.slf4j.org.apache.commons.logging:1.5.6
16 RESOLVED osgi.cmpn:4.2.0.200908310645
17 RESOLVED org.mortbay.jetty.util:6.1.9
18 RESOLVED org.springframework.osgi.jstl.osgi:1.1.2.SNAPSHOT
19 RESOLVED org.springframework.core:2.5.6.A
20 RESOLVED org.springframework.osgi.commons-el.osgi:1.0.0.SNAPSHOT
21 RESOLVED org.mortbay.jetty.server:6.1.9
22 ACTIVE org.springframework.osgi.samples.simplewebapp:0.0.0
23 RESOLVED org.springframework.beans:2.5.6.A
24 RESOLVED org.springframework.osgi.io:1.2.0
25 RESOLVED org.springframework.osgi.jasper.osgi:5.5.23.SNAPSHOT
26 RESOLVED org.springframework.aop:2.5.6.A
27 RESOLVED org.springframework.osgi.catalina.osgi:5.5.23.SNAPSHOT
28 RESOLVED org.springframework.context:2.5.6.A
29 ACTIVE org.springframework.osgi.catalina.start.osgi:1.0.0
30 RESOLVED org.springframework.osgi.core:1.2.0
31 RESOLVED org.springframework.web:2.5.6.A
32 RESOLVED org.springframework.osgi.web:1.2.0
33 ACTIVE org.springframework.osgi.web.extender:1.2.0
34 ACTIVE com.paremus.posh.readline:1.0.4
35 ACTIVE com.paremus.util.cmds:1.0.4

This listing also demonstates another key feature of nimble. Typing lsb resulted in the following log line:

*nimble/com.paremus.util.cmds-1.0.4.jar                           00:00    59Kb

This demonstrates that the nimble container resolved the lsb command from its repository index and installed it on the fly. In fact if you look at the Nimble download it is only 55K in size. All of the extra functionality is automatically downloaded based on information provided via the nimble index files and traversing package and service level dependencies!

To complete this blog post you can browse the simple web app running from nimble by opening:

http://localhost:8080/simple-web-app/

Nimble is available for download here.