iPojoRC – An iPOJO-based OSGi IRC Bot
I’ve been doing a fair bit of work recently in OSGi land. OSGi is a dynamic module system for Java designed to facilitate the creation, distribution and consumption of modular Java code. The idea is that anybody who has an application running on an OSGi platform implementation (Felix, Equinox) can retrieve your module (optionally stored in an OBR) and have it load in to their running system without upsetting anything. Exactly how well the hot-swapping of bundles works depends heavily on how well the application is written and how well the bundle being loaded / unloaded is written, but it can be made to work.
The Whiteboard Pattern
I’m not quite sure why it’s called a whiteboard pattern, but one of the neat things about the OSGi platform is the service registry. Service consumers and producers interact through the service registry that the platform provides, which takes care (in principle) of service lifecycle and dependency resolution. A correctly written service only becomes available to a correctly written consumer when the service’s dependencies have been met and its initialisation is complete. If the dependencies of any service are no longer satisfied (because of the disappearance of a required bundle), the service is removed from the registry and consumers are notified. Not rocket science, but saves developers a fair bit of effort and makes interoperable components easier to write.
The canonical example of the whiteboard pattern is an IRC bot whose commands are implemented as services. In this example, an IRC Bot is created that maintains its connection to the IRC server while commands are loaded and unloaded from the Bot on the fly. Here, the ServiceTracker is used to listen for the appearance and disappearance of bundles. Neat, but there’s still some associated boilerplate:
private ServiceTracker serviceTracker;
public void start(BundleContext context) throws Exception {
serviceTracker = new ServiceTracker(context,
MyTrackedService.class.getName(), null);
serviceTracker.open();
... startup code...
}
public void doStuff() {
Object[] implementors = serviceTracker.getServices();
if (implementors != null && implementors.length > 0) {
for (Object o : implementors) {
MyService myService = (MyService) o;
... do stuff...
}
}
}
public void stop(BundleContext context) throws Exception {
serviceTracker.close();
serviceTracker = null;
... shutdown code ...
}
iPOJO
iPOJO attempts to reduce this boilerplate and make service registration and listening even simpler. Under iPOJO, the boilerplate is reduced to:
@Requires private MyService[] implementors;
plus some hand waving. iPOJO takes care of making sure that the implementors array contains registered implementations of MyService – all your application has to do is iterate over them.
iPojoRC
With a view to having a play around with this stuff, I thought I’d implement the IRC Bot using iPOJO. You can find the fruits of that labour on my github account. It seems to work pretty well. I’ve so far not had a problem adding and removing commands on the fly. It’s even possible (if you’re really careful with versions) to rev parts of the IRCCommand API and have the old and new versions running side by side, but in practice it’s much easier just to mvn clean install.
Why?
Summut to do, innit. More seriously, OSGi seems to be gaining ground as a way of building and distributing Java applications. It plays very nicely with Maven and there are some quite neat bundles available that you can just drop in to your application with very little wiring. That said, one of the challenges of OSGi is dealing with packages and dependencies when you create your own bundles. Most of the tutorials recommend starting with all your code in a single bundle, with good reason – I invariably spend more time sorting out dependency and classloader issues with my bundles than I do writing the code inside them. If you’re going to start a serious OSGi project, it’s good to have experience of bundling simple things and the IRC Bot is a great example of simple code in many bundles.