$cd ~

Wednesday, February 4, 2009

JLine UI for Apache Felix

Apache Felix by default provides a very simple text shell UI. Unfortunately, it is not very convenient to use. For example, it doesn't support command history. JLineTUI is a replacement, which make uses of the wondeful JLine library to provide helpful features like command history etc.

Source Code: JLineTUIActivator.java

package us.timetrack.felix.shell;

import org.apache.felix.shell.ShellService;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.util.tracker.ServiceTracker;

public class JLineTUIActivator implements BundleActivator {
private BundleContext _ctx = null;
private ServiceTracker _tracker = null;
private Thread _thread = null;
private ShellTUIRunnable _runnable = null;

@Override
public void start(BundleContext ctx) throws Exception {
this._ctx = ctx;
_tracker = new ServiceTracker(_ctx, ShellService.class.getName(), null);
_tracker.open();
_runnable = new ShellTUIRunnable(_tracker);
_thread = new Thread(_runnable);
_thread.start();
}

@Override
public void stop(BundleContext ctx) throws Exception {
if(_runnable!=null) _runnable.stop();
_thread.join();
_tracker.close();
}
}


Source Code: ShellTUIRunnable.java

package us.timetrack.felix.shell;

import java.io.IOException;
import java.io.PrintStream;

import jline.ConsoleReader;

import org.apache.felix.shell.ShellService;
import org.osgi.util.tracker.ServiceTracker;

class ShellTUIRunnable implements Runnable {
private static final String PROMPT="$";
private ServiceTracker _tracker = null;
private volatile boolean _stop = false;
private PrintStream _out = null;

ShellTUIRunnable(ServiceTracker tracker){
this._tracker = tracker;
this._out = System.out;
}

void stop(){
_stop = true;
}

@Override
public void run() {
ConsoleReader reader = openConsoleReader();
if( reader==null ) return;
while(!_stop){
dispatchCommand(readCommandLine(reader));
}
}

private void dispatchCommand(String command) {
if(command==null) return;
command = command.trim();
if(command.isEmpty()) return;
ShellService service = (ShellService)_tracker.getService();
try {
service.executeCommand(command, _out, System.err);
} catch (Exception e) {
System.err.println("Shell JLine UI: "+e.getMessage());
}
}

private String readCommandLine(ConsoleReader reader) {
String command = null;
try{
command = reader.readLine(PROMPT);
}catch(IOException exception){
stop();
System.err.println("cannot accept command" + exception.getMessage());
}
return command;
}

private ConsoleReader openConsoleReader() {
ConsoleReader reader = null;
try{
reader = new ConsoleReader();
reader.setBellEnabled(false);
}catch(IOException exception){
System.err.println("cannot open the console to read: " + exception.getMessage());
}
return reader;
}
}

Tuesday, January 27, 2009

Event Publishing in OSGi

In Java, a common pattern to publish events is the Observer pattern, which requires the event source to maintain a list of interested listeners. An alternative is to use the PubSub model, where event source and listeners don't need to be aware of the existence of each other. To build a PubSub application, JMS is one choice, but if your application is built upon OSGi, you can take advantage of the EventAdmin and EventHandler services which are part of the OSGi specification.

The steps to create event publisher and event subscriber in OSGi is simple. First, we need to create EventHandler services and register them with OSGi. Registration parameters define the events that interest the EventHandler. List 1 is a sample event listener:

public class SampleListener implements EventHandler, BundleActivator{
@Override
public void start(BundleContext ctx) throws Exception {
Properties props = new Properties();
/* interested in Foo/Bar events */
props.setProperty(EventConstants.EVENT_TOPIC, "Foo/Bar");
ctx.registerService(EventHandler.class.getName(), new Listener1(), props);
}

@Override
public void handleEvent(Event event) {
/*code to process the event*/
...
}

...
}


EventAdmin is the second service that is necessary. Fortunately, it is part of OSGi. So to make use of it, we need to do nothing except that we may need to manually install the EventAdmin bundle into your OSGi environment. But to make the use of this service easier, an EventAdmin service tracker would be helpful:

public class EventAdminTracker extends ServiceTracker {
public EventAdminTracker(BundleContext context) {
super(context, EventAdmin.class.getName(), null);
}

public void postEvent(Event e){
EventAdmin ad = (EventAdmin) this.getService();
if(ad!=null){
ad.postEvents(e);
}
}
}


The last component is the event source. It is no different from regular event sources in Java.

public class EventSource implements BundleActivator {
private EventAdminTracker eat;

@Override
public void start(BundleContext ctx) throws Exception {
eat = new EventAdminTracker(ctx);
eat.open();
createSampleEvent();
}

@Override
public void stop(BundleContext ctx) throws Exception {
eat.close();
}

private void createSampleEvent{
Event e = new Event("Foor/Bar", null);
/* create a Foo/Bar event */
eat.postEvent(e);
}
}


The above is all we need to do to publish events in OSGi. As you can see, it is very easy.

Blog Archive