OSGI White-Board pattern with a sample

White-Board pattern is an important implementation scenario in OSGI programming. I’m writing this blog post because it is hard to find a proper blog post regarding the white-board pattern with an sample. Even some of you might implemented this pattern without knowing this is what means “White-board pattern”. To get more idea about the post it is necessary to have basic knowledge about the OSGI.

Why we need a White-Board pattern?

The whiteboard pattern was invented because the registration of listeners and reacting to their events became a pain in the ass with many (dynamic) dependencies. Especially, because in a component framework you cannot be sure who will be around, which means having to handle all kinds of optionality if you want to be in the driver seat. The original whitepaper is a bit outdated but it shows different examples why, if you analyze what you have to do, the whiteboard is almost always simpler and more flexible. One of the few cases of having your lunch and eating it too … — By Peter Kriens (OSGI pioneer and Guru) From blog by pradeep

I am going to give the information regarding the White-Board pattern with a sample based on a scenario that i faced.

Scenario : There is a OSGI bundle called Transport-Adaptor-Core which contains the details about the Transport Adaptors ( ex: jms, email, local & etc). Each transport adaptor is a separate OSGI bundle, These bundles are implemented separately. Here the issue how these Transport-Adaptor bundle gives their information to the Transport-Adaptor-Core bundle. For this there should be a listener in the core which is listening for different transport adaptor implementations, then when a new transport adaptor implementation found Transport-Adaptor-Core bundle can get those implementation info.

Above scenario can be implemented with two methods. before going into those methods, we’ll see how we can register newly implemented transport adaptor implementation.

/**
*
@scr.component name="CassandraTransportAdaptorService.component" immediate="true"
*/

public class CassandraTransportAdaptorServiceDS {

protected void activate(ComponentContext context) {

try {
TransportAdaptorFactory cassandraTransportAdaptorFactory = new CassandraTransportAdaptorFactory();
context.getBundleContext().registerService(TransportAdaptorFactory.class.getName(), cassandraTransportAdaptorFactory, null);
log.info("Successfully deployed the cassandra transport service");
} catch (RuntimeException e) {
log.error("Can not create transport service ", e);
}
}
}

Here we have implemented a TransportAdaptorFactory object and registered it in the bundle context.

Now we’ll see how we can implement the listener using Service Tracker (Service Listener)

/**
*
@scr.component name="Transport.Adaptor.Tracker.component" immediate="true"
*/

public class TransportAdaptorServiceTracker implements Runnable {

private static final Log log = LogFactory.getLog(TransportAdaptorServiceTracker.class);
Thread thread;
ServiceTracker tracker;

protected void activate(ComponentContext context) {

try {
tracker = new ServiceTracker(context.getBundleContext(), TransportAdaptorFactory.class.getName(),
null );
tracker.open();
thread = new Thread( this, "DisplayManager Whiteboard" );
thread.start();
log.info("Transport Adaptor Tracker started");
} catch (RuntimeException e) {
log.error("Can not create transport service ", e);
}
}

@Override
public void run() {
Thread current = Thread.currentThread();
int n = 0;
while ( current == thread ) {
Object [] providers = tracker.getServices();
if ( providers != null && providers.length > 0 ) {
if ( n >= providers.length )
n = 0;

TransportAdaptorFactory transportAdaptorFactory = (TransportAdaptorFactory) providers[n++];
((CarbonTransportAdaptorService)TransportAdaptorServiceValueHolder.getCarbonTransportAdaptorService()).registerTransportAdaptor(transportAdaptorFactory.getTransportAdaptor());
}

try { wait( 5000 ); } catch( InterruptedException e ) {}
}
}
}

More details about this implementation and White-Board scenario can be found here. But this approach is not efficient on my view.

Listener implementation with declarative services.

/**
*
@scr.component name="TransportAdaptorServiceTracker.component" immediate="true"
*
@scr.reference name="transport.adaptor.tracker.service"
* interface="org.wso2.carbon.transport.adaptor.core.TransportAdaptorFactory" cardinality="0..n"
* policy="dynamic" bind="setTransportAdaptorService" unbind="unSetTransportAdaptorService"
*/

public class TransportAdaptorServiceTracker {

/**
* initialize the Transport Adaptor Manager core service here.
*
*
@param context
*/
protected void activate(ComponentContext context) {

try {

log.info("Successfully deployed the transport adaptor tracker service");
} catch (RuntimeException e) {
log.error("Can not create the transport manager service ", e);
}
}

protected void setTransportAdaptorService(TransportAdaptorFactory transportAdaptorFactory) {
((CarbonTransportAdaptorService)TransportAdaptorServiceValueHolder.getCarbonTransportAdaptorService()).registerTransportAdaptor(transportAdaptorFactory.getTransportAdaptor());
}

protected void unSetTransportAdaptorService(TransportAdaptorFactory transportAdaptorFactory) {

}
}

Declarative services is the most easiest and efficient way to implement the the White-Board pattern. Here

/**
*
@scr.component name="TransportAdaptorServiceTracker.component" immediate="true"
*
@scr.reference name="transport.adaptor.tracker.service"
* interface="org.wso2.carbon.transport.adaptor.core.TransportAdaptorFactory" cardinality="0..n"
* policy="dynamic" bind="setTransportAdaptorService" unbind="unSetTransportAdaptorService"
*/

Above annotation declares (that is why it is called declarative services) that there can be 0 to n instances of “TransportAdaptorFactory”. “setTransportAdaptorService” method is triggered when it is found a new implementation of the “TransportAdaptorFactory”. “unSetTransportAdaptorService” is called when removing a “TransportAdaptorFactory” implementation.

More details about the White-Board pattern can be found in below links.

[1] http://pradeepfernando.blogspot.com/2013/02/patterns-in-osgi-programming-white.html

[2] http://www.osgi.org/javadoc/r4v43/core/org/osgi/framework/ServiceListener.html

[3] http://blog.osgi.org/

Acknowledgement : Azeez & Sameera

– THE END –

Originally published at mohanadarshan.wordpress.com on June 23, 2013.

Senior Tech Lead, Speaker @ WSO2. Closely works on Stream Processing and Integration