Using OSGi services to single-source an RCP and RAP Application

Probably one of RAP’s best known features is its single-sourcing capabilities. Some time ago we created a guide on Single-Sourcing RCP and RAP applications. The guide recommended a technique where a facade and fragments were used to invoke the RCP or RAP implementation during runtime. With this post I want to show you how to achieve the same the OSGi way.

For single-sourcing a RAP or RCP application, its straightforward to use the power of OSGi because it’s included in both platforms out-of-the-box. OSGi has a central concept called services which are simply POJOs and are used to allow communication between modules. And, you can register or resolve services at any time in your code.

The basic Idea behind using OSGi services for single-sourcing is as follows. We need to extract all the things that vary into separate, platform specific bundles. To use the different implementations, we have to create an interface in a “common” bundle that contains the methods we want to use. The platform specific bundles have to register an implementation of this interface as a service. In the “common” bundle we can reference the registered services and use them to get the platform specific stuff. More specifically, we register a RAP implementation in a “rap” bundle and an RCP implementation in an “rcp” bundle. The only thing we need to do then is to start the right bundles on the associated platform to get the right service.

With this solution we can extract the platform specific stuff into separate bundles and we don’t have to rely on fragments. So enough talking. Let’s look at some code. I decided to create a very simple single-sourcing interface which can be used to get the WidgetUtil.CUSTOM_VARIANT constant value. This constant exists only in RAP so, it’s a good example for showing how to handle the differences.

public interface ISingleSourcingService {
  String getCustomVariantString();
}

I have created three bundles: one that contains the application which is entitled “com.eclipsesource.app” and one for each platform. These are called “com.eclipsesource.app.rap” and “com.eclipsesource.app.rcp”. Both platform bundles implement the interface.
RAP:

  @Override
  public String getCustomVariantString() {
    return WidgetUtil.CUSTOM_VARIANT;
  }

RCP:

  @Override
  public String getCustomVariantString() {
    // There are no custom variants in RCP
    return "";
  }

And both bundles are registering the service in their Activator during the start method call.

  public void start( BundleContext bundleContext ) throws Exception {
    Activator.context = bundleContext;
    registration = context.registerService( ISingleSourcingService.class.getName(),
                                            new RAPSingelSourcingService(),
                                            null);
  }

When it comes to using the service in the common “app” bundle we can use a ServiceTracker to get the service implementation (you can also use DS).

  private ISingleSourcingService getSingleSourcingService() {
    // We use a tracker to get the service. We also can use DS to get it.
    Bundle bundle = FrameworkUtil.getBundle( getClass() );
    BundleContext context = bundle.getBundleContext();
    ServiceTracker tracker
      = new ServiceTracker( context,
                            ISingleSourcingService.class.getName(),
                            null );
    tracker.open();
    ISingleSourcingService service = tracker.getService();
    tracker.close();
    return service;
  }

The only thing we have to do now is to create two launch configurations which contain the associated platform-specific bundle.

That’s it! You can find the sources in this github repository. Have fun using OSGi services to single-source applications.

 

5 Responses to “Using OSGi services to single-source an RCP and RAP Application”

  1. Ralf Zahn says:

    Which solution whould you recommend? Using fragments or using OSGi services?

    IMHO, developers of RAP based applications that are not familiar with OSGi should know the differences of fragments and OSGi services:
    – Fragments and host bundle share the same class loader and have access to any internals of each other. (OSGi service implementations do not share the classloader with the bundle that defines the service interface.)
    – OSGi services mostly have the same life cycle as the bundle that contains the implementation. To get an instance of the ISingleSourcingService (in your example), you have to ensure that the implementation bundle is started.
    – I would not recomment to open and close a ServiceTracker each time a service instance is requested. The ServiceTracker should be opened and closed within an Activator.
    – Using Equinox Declarative Services requires to have the org.eclipse.equinox.ds bundle started. Because of the Extender Pattern, this bundle is not resolved and started automatically.

  2. Hi Ralf,
    thanks for your comments. I would say you are absolutely right with all your points. I would say both pattern (fragments and services) has their pros and cons. E.g. with services you can simply reuse the the implementation in more than just one host bundle. I opened and closed the tracker with every service call to encapsulate the framework specific stuff in one method to avoid confusion for new OSGi users.

    JFYI: A DS post will follow up ;)

  3. Cole Markham says:

    Nice approach.

    - One thing I would question is returning an empty string for the RCP case. It seems like that might cause problems if something else is using the Widget.setData.

    - Also it seems that it would be better to actually have the service do the work for you, just have a method like setCustomVariant(Widget, String). I guess it’s just a matter of preference.

  4. Hi Cole, sure there are many things that can be optimized. This was just a demo to show how to handle the differences via services. Of course it would be much better to let the service do the work. Thanks for your feedback.

    Cheers Holger

5 responses so far

Written by . Published in Categories: Planet Eclipse, Planet OSGi

Published:
Jun 20th, 2011
Follow:

Twitter Google+ GitHub Facebook