Advanced Remote Services

For reasons of saving service provider's resources or to have client-dependent code parts, services can register smart proxies. These are abstract classes that implement the service interface and can have additional methods. The service consuming platform uses smart proxies during proxy bundle generation and leaves all implemented (i.e. not abstract) methods untouched. Abstract methods are implemented with remote method invocations.

It is e.g. possible to have a method that returns an attribute of the local framework or that uses a local service instead of redirecting the invocation to the service provider. Implemented methods can themselves use every abstract or implemented method of the abstract class. R-OSGi injects all classes into the bundle that are references within the service interface (i.e., as method parameter) or within the code of the smart proxy class, if they were not imported by the original bundle. For more sophisticated applications, it is possible to inject additional classes into the proxy bundle. This is also useful, for instance, if methods have interfaces as formal parameter and the developer wants to provide an implementation of the interface (which is not part of the execution environment). By this, it can be avoided that the bundle makes too much assumptions about the presence of certain implementation of interfaces.

Smart proxy example

ServiceInterface:

			
package ch.ethz.iks.r_osgi.sample.api;

public interface ServiceInterface {

	public String echoService(final String message, final Integer count);

	public String reverseService(final String message);

	public boolean equalsRemote(Object other);

	public void printRemote(int i, float f);

	public void zero();

	public void local();

}
ServiceImpl:
			
package ch.ethz.iks.r_osgi.sample.service;

import ch.ethz.iks.r_osgi.sample.api.ServiceInterface;

/**
 * just a simple sample service plays around with strings
 */
public final class ServiceImpl implements ServiceInterface {

	public String echoService(final String message, final Integer count) {
		StringBuffer buffer = new StringBuffer();
		final int c = count.intValue();
		for (int i = 0; i < c; i++) {
			buffer.append(message);
			if (i < c - 1) {
				buffer.append(" | ");
			}
		}
		return buffer.toString();
	}

	public String reverseService(String message) {
		return new StringBuffer().append(message).reverse().toString();
	}

	public void local() {
		System.out.println("Server: local called");
		throw new RuntimeException("Local cannot be called remotely");
	}

	public void zero() {
		System.out.println("Server: zero called.");
	}

	public boolean equalsRemote(Object other) {
		return equals(other);
	}

	public void printRemote(int i, float f) {
		System.out.println("i is " + i);
		System.out.println("f is " + f);
	}
}

SmartService:
			
package ch.ethz.iks.r_osgi.sample.service;

import ch.ethz.iks.r_osgi.sample.api.ServiceInterface;

public abstract class SmartService implements ServiceInterface {

	public abstract String echoService(String message, Integer count);

	public abstract String reverseService(String message);

	public abstract boolean equalsRemote(Object other);

	public void local() {
		System.out.println("Local invocation");
		zero();
	}

	public abstract void printRemote(int i, float f);

	public abstract void zero();

	public boolean equals(Object obj) {
		System.out.println("checking for equality");
		if (!obj.equals(this)) {
			return equalsRemote(obj);
		} else {
			return true;
		}
	}
}

Registration by whiteboard pattern:
(...)

Dictionary properties = new Hashtable();
properties.put(RemoteOSGiService.R_OSGi_REGISTRATION, Boolean.TRUE);
properties.put(RemoteOSGiService.SMART_PROXY, SmartService.class.getName());

context.registerService(ServiceInterface.class.getName(), new ServiceImpl(), properties);

(...)

If you want to interact with other services in your smart proxy, you can let your smart proxy class implement
package ch.ethz.iks.r_osgi;

public interface SmartProxy {

	/**
	 * This method is called when the smart proxy is started. Since it passes
	 * the bundle context to the smart proxy, it can be used to retrieve other
	 * services, etc.
	 * 
	 * @param context
	 *            the bundle context of the proxy bundle.
	 */
	public void started(final BundleContext context);

	/**
	 * This method is called when the smart proxy is stopped.
	 * 
	 * @param context
	 *            the bundle context.
	 */
	public void stopped(final BundleContext context);

}
and get a BundleContext instance through the lifecycle methods that are called when the smart proxy is started or stopped.

Handling of existing services

This way of registering services for remote access is still a bit invasive. It requires to alter the part of the code which registers the service (typically, the BundleActivator). To deal with existing bundles, there is an alternative way of registering services for remote access through an external surrogate service:

public class Activator implements BundleActivator, SurrogateRegistration {

	public void start(BundleContext context) throws Exception {

		final Hashtable properties = new Hashtable();
		properties.put(RemoteOSGiService.R_OSGi_REGISTRATION,
				Boolean.TRUE);
		properties.put(RemoteOSGiService.SMART_PROXY, SmartService.class
				.getName());		
				
		final ServiceReference legacyService = context.getServiceReference(ServiceInterface.class.getName());		
		properties.put(SurrogateRegistration.SERVICE_REFERENCE, legacyService);
		
		context.registerService(SurrogateRegistration.class.getName(), this, properties);

	}
	
...

}
	
Thus, the existing service (in the above example, it is a ServiceImpl registered as ServiceInterface service) remains unchanged and the registration is performed through a surrogate that can reside in a different bundle serving as adapter between the legacy application and the remote world.