Notice: This Wiki is now read only and edits are no longer possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.
ECF/Asynchronous Remote Services
Synchronous Remote Services
Whether a remote or local OSGi service, a service is invoked by calling a method on the service interface. For example, consider a simple IHello service interface:
package org.eclipse.ecf.examples.remoteservices.hello; public interface IHello { public String hello(String from); }
Once the consumer gets a valid service instance (i.e. through ServiceTracker, injection via declarative services (DS) or other injection framework, BundleContext.getServiceReference or other methods), it can actually invoke by calling the service interface method:
String response = helloService.hello("slewis");
With remote services, the helloService instance will typically be a proxy. When invoked/called, the proxy will marshall any arguments (i.e. 'slewis' String in this case), communicate with the remote service host via some protocol, the host will execute the associated code, and if there is a result, it will be unmarshalled and the result value returned to the caller thread. As per typical java call-return semantics, the thread that calls 'hello' will block until this entire process is complete.
Asynchronous Remote Services
ECF has added the ability for the service host to declare asynchronous access to a remote method, so that the consumer can be guaranteed that invoking/calling the service will not block. For example, it's possible for a consumer to make a non-blocking call to the IHello service like this
Future<String> future = helloAsyncService.helloAsync("slewis");
With ECF asynchronous proxies (AP), the consumer thread that calls helloAsyncService.helloAsync is guaranteed not to block, and the returned Future>String< can be used to subsequently access the String result (or exception/failure).
With ECF's AP support, neither the consumer nor the host have to implement this asynchronous behavior. The proxy is automatically and dynamically constructed by the ECF remote services implementation on the consumer.
You might ask: How is the asynchronous proxy defined? i.e. where does the helloAsyncService come from?
The answer is that it is defined in a new/second service interface...called the asynchronous service interface. For example, here is how to declare an asynchronous service interface for the IHello service:
public interface IHelloAsync { public Future<String> helloAsync(String from); }
Notice that this declaration resembles the IHello service interface declaration, but differs from it in several important ways:
- The name of the asynchronous service interface is IHelloAsync rather than IHello
- The method name(s) is/are helloAsync rather than hello
- The return value is Future<String> (or with Java 8 CompletableFuture<String> see below) rather than String
With ECF remote services, on the consumer when a IHello proxy is created, the proxy will also automatically implement the IHelloAsync interface, and the consumer is able to use either the synchronous IHello service interface or the IHelloAsync asynchronous service interface. So, for example
IHello helloService = ...get service via DS/injections, ServiceTracker or otherServiceTracker, or other... if (helloService instanceof IHelloAsync) { IHelloAsync helloServiceAsync = (IHelloAsync) helloService; // call it asynchronously...no blocking Future<String> future = helloAsync("slewis"); // do other things String result = future.get(); // do something with result... }
or
IHelloAsync helloAsyncService = ...get service via DS/injections, ServiceTracker or other // call it asynchronously...no blocking Future<String> future = helloAsync("slewis"); // do other things String result = future.get(); // do something with result...
Having both the synchronous (IHello) and asynchronous (IHelloAsync) service types gives the consumer flexibility in determining how a given remote invocation will occur (i.e. synchronously/blocking or asynchronously/non-blocking). Synchronous, asynchronous, or both invocation methods may be used as appropriate for the consumer's use case(s) for the service.
Java 8 CompletableFuture
Java 8 introduces a new type of Future called java.util.concurrent.CompletableFuture. With ECF 3.8.1/Luna remote services, it's now possible for asynchronous service interfaces to return a CompletableFuture:
package my.package; import java.util.concurrent.CompletableFuture; public interface IHelloAsync { public CompletableFuture<String> helloAsync(String from); }
with CompletableFuture it's not necessary for the consumer to call Future.get (and possibly block) directly...but rather you can write succinct, simple, and guaranteed to be non-blocking calls such as:
IHelloAsync helloAsync = ...get remote service via DS/injections, ServiceTracker or other CompletableFuture<String> cf = helloAsync("slewis"); cf.thenAccept((response) -> System.out.println("response to slewis helloAsync was: " + response));
The use of CompletableFuture has some very nice properties for service design, as well as some nice guarantees for the service consumer. For remote service designers, they may declare and implement normal OSGi remote services (IHello) without having to concern themselves with the consumer's invocation style (synchronous or asynchronous).
Remote service consumers can be assured that there will be no blocking in their code (via the guarantees of CompletableFuture). They may also use lambda to succinctly express arbitrary eventual execution...guaranteed to be done asynchronously when the response becomes available.
Note that with ECF remote services there is no need for either the host implementation or the consumer to implement the asynchronous service interface. The dynamically constructed proxy will automatically have/expose the asynchronous service interface to the consumer, and this proxy will provide the underlying implementation.
Note another advantage...for both the service designer and consumer...is that there are no extra class dependencies in the synchronous or asynchronous service interfaces (e.g. IHello and IHelloAsync). That is, there are no dependencies on OSGi, OSGi Remote Services, or ECF classes...meaning that if they wish to use/reuse these services in other runtime contexts (e.g. outside of OSGi, OSGi Remote Services, and/or ECF) in the service interfaces or in any client code. The only dependencies are to other service types and java.util.concurrent.CompletableFuture or java.util.concurrent.Future.
Eclipse Communication Framework |
API |
API Documentation • Javadoc • Providers |
Development |
Development Guidelines • Integrators Guide |