Using the Whiteboard Pattern with RAP

Using the Whiteboard Pattern with RAP

When adopting the Whiteboard Pattern for one of our Eclipse RAP based web applications, we stumbled upon a problem concerning the session scope of events. Here’s how we solved it.

The Whiteboard Pattern in OSGi development is an alternative to the event listener or Observer pattern commonly used in Java programming. In an OSGi environment, the Whiteboard Pattern is widely considered to be the better choice since it uses OSGi’s powerful service platform to handle the error-prone registration and deregistration of listeners, and fits better into the component-based approach.

When using Eclipse RAP as a web application framework, OSGi provides the component model under the hood. Hence, the Whiteboard Pattern can be used in RAP development as well.

However, it has to be handled carefully due to the multi-user environment of a web application. When a listener is registered using the Whiteboard pattern, i.e. as an OSGi service, it will be notified whenever an event happens anywhere in the application, even in another session! This may cause pretty funny effects (imagine you click a button in your browser session, and the dialog you want to open is also opened in the browser of another user who’s currently logged into the same application) – but this is hardly what you want in your application.

Under normal circumstances you would like your listeners to respond only to events originating from the same session. Fortunately, the OSGi service framework has an elegant solution for this: service properties. A service can be registered with a set of properties, and the consumer can only access the services matching a given property filter.

To make sure a listener receives events only from within its session, the listener should be registered as service, with the ID of the current session as a property:

private void registerListener() {
   // The implementation of the listener interface
   IMyListener myListenerImpl = new MyListenerImpl();
   // Define the RWT session ID as a property
   Hashtable properties = new Hashtable();
   properties.put("RAP_SESSION_ID", RWT.getSessionStore().getId()));
   // Register the listener implementation as a service with the property
   bundleContext.registerService(IMyListener.class, myListenerImpl, properties);
}

Please keep in mind that you’ll need to deregister the service on session expiry – otherwise, you will produce a memory leak.

The code firing the events must only request the services whose properties match the ID of the current RWT session. This can be achieved by specifying an LDAP-style filter string:

private void fireEvent() {
   // LDAP-style filter string, allowing only services with the given property
   String filter = "(RAP_SESSION_ID=" + RWT.getSessionStore().getId() + ")";
   // Retrieve only service references which are registered with the same RWT session ID
   Collection> serviceReferences =
      bundleContext.getServiceReferences(IMyListener.class, filter);
   for (ServiceReference reference : serviceReferences) {
      IMyListener listener = bundleContext.getService(reference);
      listener.handleEvent();
   }
}

(Note: of course ServiceTrackers and/or Declarative Services could and should be used here.)

If you don’t want to create your own listener interfaces and choose to use OSGi’s generic EventAdmin service in your Eclipse RAP application, this filter approach can also be applied.

Is anyone else using the Whiteboard Pattern in Eclipse development? I’m interested in hearing your opinions.