R-OSGi, or how to call OSGi services remotely: Part I

by kengilmer

When learning OSGi and discovering the power and simplicity of the service registry, a common thought is “Oh, I want to use this service remotely…how can I do that?”  And, well, currently the answer is you can’t!   While there is active work going into remote services in R4.2, the existing OSGi specification only defines what happens inside of a single JVM instance.  But what if you really want to remote your OSGi? One sweet solution is R-OSGi

The BUG runs the Concierge OSGi framework, and happily it was written to support R-OSGi, but other frameworks should work as well.  The HOWTO will cover installing R-OSGi on the BUG and Virtual BUG, creating a service server and client.  A forthcoming post will detail service discovery and “seemless” distributed apps.  If you don’t have or care about BUG, this HOWTO works great on regular Java on regular machines.  So, continue on to learn about R-OSGi!

1. SDK Setup

Make sure you have the latest version of the SDK.  Now download the “Remote OSGi” application from BUGnet into the SDK.  Your SDK should look something like this:

created on: 01/06/09

Now you’ll notice that there are some compiler errors.  This is because R-OSGi requires the Event Admin, an OSGi service designed for pub/sub messaging.  Let’s install the event admin application from BUGnet.  Search for and download Event Admin.  Your workspace should now look like this:

created on: 01/06/09

 

 

Now let’s start the Virtual BUG and use the console to see if our new bundles are running.  Type ‘bundles’ at the Concierge prompt.  Here is what I got:

created on: 01/06/09

Great!  Now our Virtual BUG has R-OSGi!  Let’s now install Remote and EventAdmin on the BUG.  Simply connect the BUG and download the apps.  Notice how the R-OSGi bundle does not show up in the applications folder on your BUG.  This is because it’s not designated as a “BUG Application”.  You can add the header “Bug-Bundle-Type: Application” to the manifest to make it show up.

2. A Remotable Service

Now R-OSGi is running both on the real BUG and the Virtual BUG.  Let’s create a service, a bundle that exports the service, and a bundle that consumes the service.  We’ll deploy the server to the BUG and consume it on the Virtual BUG.  To keep things simple, we’ll create a service that has one method that returns a string.  Notice that we add one property to the service to let R-OSGi know that we want to make it accessible by remote clients.

Activator.java:

package serviceproducer;

import java.util.Dictionary;
import java.util.Hashtable;

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

import serviceproducer.pub.RemoteServiceDefinition;

public class Activator implements BundleActivator {

private ServiceRegistration sr;

public void start(BundleContext context) throws Exception {
Dictionary d = new Hashtable();
d.put("service.remote.registration", Boolean.TRUE);

sr = context.registerService(RemoteServiceDefinition.class.getName(), new RemoteService(), d);
}

public void stop(BundleContext context) throws Exception {
sr.unregister();
}
}

RemoteService.java:

package serviceproducer;

import serviceproducer.pub.RemoteServiceDefinition;

public class RemoteService implements RemoteServiceDefinition {

public String getMessage() {
return System.getProperty("os.arch");
}

}

RemoteServiceDefinition.java:

package serviceproducer.pub;

public interface RemoteServiceDefinition {
String getMessage();
}

The app is here on BUGnet: http://buglabs.net/applications/ServiceProducer  Now let’s install the app on the BUG via the SDK.  After installing, type “services” at the OSGi console on the BUG.  You should see your new service registered:


(: services
...
31 serviceproducer.pub.RemoteServiceDefinition service.ranking = 0, service.remote.registration = true,

 3. A client that consumes a remote service.

Now let’s create our client bundle.  Create a new BUG application and call it ServiceConsumer.  Again to keep things simple, we will not get fancy with our consumer. 

Activator.java:

package serviceconsumer;

import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.osgi.util.tracker.ServiceTracker;

import com.buglabs.application.IServiceProvider;
import com.buglabs.application.RunnableWithServices;
import com.buglabs.application.ServiceTrackerHelper;
import serviceproducer.pub.RemoteServiceDefinition;

public class Activator implements BundleActivator, RunnableWithServices {

private ServiceTracker st;

public void start(BundleContext context) throws Exception {
st = ServiceTrackerHelper.createAndOpen(context, RemoteServiceDefinition.class.getName(), this);
}

public void stop(BundleContext context) throws Exception {
st.close();
}

public void allServicesAvailable(IServiceProvider serviceProvider) {
RemoteServiceDefinition svc = (RemoteServiceDefinition) serviceProvider.getService(RemoteServiceDefinition.class);

System.out.println("The Remote Message: " + svc.getMessage());
}

public void serviceUnavailable(IServiceProvider serviceProvider,
ServiceReference sr, Object service) {
}
}

Now you should be able to run a Virtual BUG and see this all working (locally).  In the log output you should see something like: “The Remote Message: i386”   Funny, I’m not using a 386.  Oh well.

This, as it is, will not work as remotable.  There are ways to add other things that will allow this bundle, as is now, to be able to consume a remote service.  This is the very powerful aspect of R-OSGi and will be covered in the next installment. 

For now we’ll need to simply tell R-OSGi how to get our remote service.  Here is our new activator.  Notice we are tracking the Remote OSGi Service and a NetworkChannelFactory.

Activator.java:

package serviceconsumer;

import java.io.IOException;

import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleException;
import org.osgi.framework.ServiceReference;
import org.osgi.util.tracker.ServiceTracker;

import com.buglabs.application.IServiceProvider;
import com.buglabs.application.RunnableWithServices;
import com.buglabs.application.ServiceTrackerHelper;
import serviceproducer.pub.RemoteServiceDefinition;

import ch.ethz.iks.r_osgi.RemoteOSGiException;
import ch.ethz.iks.r_osgi.RemoteOSGiService;
import ch.ethz.iks.r_osgi.RemoteServiceReference;
import ch.ethz.iks.r_osgi.URI;
import ch.ethz.iks.r_osgi.channels.NetworkChannelFactory;

public class Activator implements BundleActivator, RunnableWithServices {
private RemoteOSGiService remote;
private URI connURI;
private ServiceTracker st;

public void start(BundleContext context) throws Exception {
st = ServiceTrackerHelper.createAndOpen(context, new String [] {RemoteOSGiService.class.getName(), NetworkChannelFactory.class.getName()}, this);
}

public void stop(BundleContext context) throws Exception {
st.close();
}

public void allServicesAvailable(IServiceProvider serviceProvider) {
remote = (RemoteOSGiService) serviceProvider.getService(RemoteOSGiService.class);
connURI = new URI("r-osgi://10.10.10.10:9278");

try {
RemoteServiceReference[] svcRef = remote.getRemoteServiceReferences(connURI, RemoteServiceDefinition.class.getName(), null);

if (svcRef != null && svcRef.length > 0) {
RemoteServiceDefinition svc = (RemoteServiceDefinition) remote.getRemoteService(svcRef[0]);

System.out.println("Remote Service: " + svc.getMessage());
} else {
System.out.println("Service unavailable.");
}
} catch (RemoteOSGiException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

public void serviceUnavailable(IServiceProvider serviceProvider,
ServiceReference sr, Object service) {
remote.disconnect(connURI);
}
}

Notice I’m connecting to my BUG which is connected via USBNET on a static IP address of 10.10.10.10.   9278 is the standard R-OSGi port.  In my service tracker, I get a reference to the remote service and then get a reference to a RemoteServiceDefinition implementation. 

But wait, there is a problem!  When the Virtual BUG starts you will notice an error message:

ch.ethz.iks.r_osgi.RemoteOSGiException: No NetworkChannelFactory for r-osgi found.
at ch.ethz.iks.r_osgi.impl.RemoteOSGiServiceImpl.connect(RemoteOSGiServiceImpl.java:564)
at ch.ethz.iks.r_osgi.impl.RemoteOSGiServiceImpl.getRemoteServiceReferences(RemoteOSGiServiceImpl.java:620)
at serviceconsumer.Activator.allServicesAvailable(Activator.java:41)
at com.buglabs.application.ServiceTrackerCustomizerAdapter.doStart(ServiceTrackerCustomizerAdapter.java:69)
at com.buglabs.application.ServiceTrackerCustomizerAdapter.addingService(ServiceTrackerCustomizerAdapter.java:94)
at org.osgi.util.tracker.ServiceTracker$Tracked.track(ServiceTracker.java:815)
at org.osgi.util.tracker.ServiceTracker$Tracked.serviceChanged(ServiceTracker.java:747)
at ch.ethz.iks.concierge.framework.Framework.notifyServiceListeners(Framework.java:1227)
at ch.ethz.iks.concierge.framework.Framework$BundleContextImpl.registerService(Framework.java:1916)
at ch.ethz.iks.concierge.framework.Framework$BundleContextImpl.registerService(Framework.java:1937)
at ch.ethz.iks.r_osgi.impl.RemoteOSGiActivator.start(RemoteOSGiActivator.java:93)
at ch.ethz.iks.concierge.framework.BundleImpl.startBundle(BundleImpl.java:446)
at ch.ethz.iks.concierge.framework.Framework$SystemBundle.setLevel(Framework.java:2468)
at ch.ethz.iks.concierge.framework.Framework$SystemBundle.access$0(Framework.java:2428)
at ch.ethz.iks.concierge.framework.Framework.startup(Framework.java:485)
at ch.ethz.iks.concierge.framework.Framework.main(Framework.java:315)

What’s happening here is that our ServiceTracker is running before R-OSGi is fully loaded.  Unfortunately in the current SDK (1.2.2) there is no way to fix this, because you can’t set bundle start order.  However you can simply restart the bundle after everything has been loaded and it should work, like so:


(: stop 18
ServiceConsumer: active -> resolved

(: start 18
Remote Service: armv6l
ServiceConsumer: resolved -> active

And viola!  You’ve called a OSGi service on the BUG from a Virtual BUG.  Notice my machine is now an armv6l. 

In summary we’ve covered the basics of R-OSGi: calling a service remotely.  This, however, isn’t as cool as it could be.  First of there seems to be more code than necessary to do all this, and I have to hardcode the connections from client to server.  Don’t fret because R-OSGi has more tricks up it’s sleave to relieve these problems!  Stay tuned for the next post in the R-OSGi HOWTO!

 

Advertisements