Tuesday, May 01, 2018

ECF Photon supports OSGi Async Remote Services

In a previous post, I indicated that ECF Photon/3.14.0 will support the recently-approved OSGi R7 specification.   What does this support provide for  developers?

Support osgi.async remote service intent

The OSGi R7 Remote Services specification has been enhanced with remote service intents.  Remote Service Intents allow service authors to specify requirements on the underlying distribution system in a standardized way.   Standardization of service behavior guarantees the same runtime behavior across distribution providers and implementations.

The osgi.async intent allows the service interface to use return types such as Java8's CompletableFuture or OSGi's Promise.   With a supporting distribution provider, the proxy will automatically implement the asynchronous/non-blocking behavior for the service consumer.

For example, consider a service interface:
public interface Hello {
    CompletableFuture<String> hello(String greetingMessage);
}
When an implementation of this service is registered and exported as a remote service with the osgi.async intent:
@Component(property = { "service.exported.interfaces=*", "service.intents=osgi.async" })
public class HelloImpl implements Hello {
    public CompletableFuture<String> hello(String greetingMessage) {
          CompletableFuture<String> future = new CompletableFuture<String>();
          future.complete("Hi.  This a response to the greeting: "+greetingMessage);
          return future;
    }
}
Then when a Hello service consumer (on same or other process) discovers, imports and then remote service is injected by DS:
@Component(immediate=true)
public class HelloConsumer {

    @Reference
    private Hello helloService;

    @Activate
    void activate() throws Exception {
        // Call helloService.hello remote service without blocking
        helloService.hello("hi there").whenComplete((result,exception) -> {
            if (exception != null)
                exception.printStackTrace(exception);
            else
                System.out.println("hello service responds: " + result);
        });
    }
}
The injected helloService instance (a distribution-provider-constructed proxy) will automatically implement the asynchronous remote call.   Since the proxy is constructed by the distribution provider, there is no need for the consumer to implement anything other than calling the 'hello' method and handling the response via the Java8-provided whenComplete method.   Java8's CompletionStage, Future, and OSGi's Promise are also supported return types.  (Only the return type is used to identify asynchronous remote methods, any method name can be used).  For example: the following signature is also supported as an async remote service:
public interface Hello {
    org.osgi.util.promise.Promise<String> hello(String greetingMessage);
}

Further, OSGi R7 Remote Services supports a timeout property:
@Component(property = { "service.exported.interfaces=*", "service.intents=osgi.async", "osgi.basic.timeout=20000" })
public class HelloImpl implements Hello {
    public CompletableFuture<String> hello(String greetingMessage) {
          CompletableFuture<String> future = new CompletableFuture<String>();
          future.complete("Hi.  This a response to the greeting: "+greetingMessage);
          return future;
    }
}
With ECF's RSA implementation and distribution providers, this timeout will be honored by the underlying distribution system. That is, if the remote implementation does not return within 20000ms, then the returned CompletableFuture will complete with a TimeoutException.

Async Remote Services make it very easy for service developers to define, implement, and consume loosely-coupled and dynamic asynchronous remote services.   It also makes asynchronous remote service contracts transport independent, allowing the swapping of distribution providers or creating/using custom providers without changes to the service contract.

For the documented example code, see here