Skip to main content
Jump to: navigation, search

Difference between revisions of "ECF/Asynchronous Remote Services"

< ECF
Line 1: Line 1:
 
[[ECF]]'s Remote Services API, which is used to implement OSGi remote services and remote service admin specifications, has the ability for consumers/clients to use asynchronous/non-block remote method calls.  
 
[[ECF]]'s Remote Services API, which is used to implement OSGi remote services and remote service admin specifications, has the ability for consumers/clients to use asynchronous/non-block remote method calls.  
  
===Normal/Synchronous Services===
+
===Normal/Synchronous OSGi (Remote) Services===
  
 
When an OSGi service is actually invoked today, this is done by the caller calling a method on the service.  For example, consider a simple 'hello' service:
 
When an OSGi service is actually invoked today, this is done by the caller calling a method on the service.  For example, consider a simple 'hello' service:
Line 14: Line 14:
 
</source>
 
</source>
  
Once a consumer of this service gets a valid service reference (i.e. through ServiceTracker, declarative services, getServiceReference or however), it can actually invoke/use the service via a method call on 'hello':
+
Once a consumer of this service gets a valid service reference (i.e. through ServiceTracker, injection via declarative services (DS) or other injection framework, BundleContext.getServiceReference or others), it can actually invoke/use the service via a method call on 'hello':
  
 
<source lang="java">
 
<source lang="java">
Line 22: Line 22:
 
</source>
 
</source>
  
With OSGi remote services, this 'helloService' may actually be a proxy.  If invoked as above, under the covers, a proxy will marshall the method arguments (i.e. 'slewis' String in this case), and then communicate with the remote service host via some network (i.e. via some protocol).  If there is a result of the call, then it will then be unmarshalled and return to the caller thread.  With '''synchronous''' invocation the thread that calls 'hello' will block until this entire process is complete.
+
With OSGi remote services, this 'helloService' may actually be a proxy.  If invoked as above a proxy will marshall the method arguments (i.e. 'slewis' String in this case), and communicate with the remote service host via some network (i.e. via some protocol).  If there is a result of the call, then it will then be unmarshalled and returned to the caller thread.  With '''synchronous''' invocation the thread that calls 'hello' will block until this entire process is complete.
  
===Asynchronous Services===
+
===Asynchronous Remote Services===
  
ECF has just added the ability to declare asynchronous access to a remote method, so that the consumer can ge guaranteed that calling the service will not block indefinitely.  So, for example, it's now possible to make a call to the hello service like this
+
ECF has added the ability to declare asynchronous access to a remote method, so that the consumer can ge guaranteed that calling the service will not block.  So, for example, it's now possible to make a non-blocking call to the hello service like this
  
 
<source lang="java">
 
<source lang="java">
  
helloAsyncService.helloAsync("slewis", new IAsyncCallback<String>() {
+
Future future = helloAsyncService.helloAsync("slewis");
    public void onSuccess(String result) {
+
 
        // Do something with result
+
    }
+
    public void onFailure(Throwable t) {
+
        // Deal with network failure in appropriate manner
+
    }
+
});
+
 
</source>
 
</source>
  
With an ECF created asynchronous proxy, the consumer thread that calls helloAsyncService.helloAsync is guaranteed '''not''' to block, and the appropriate IAsyncCallback method will be executed when the remote call completes (with either success or failure). The callback is called (by an arbitrary ECF thread) sometime '''after''' the helloAsync method completes. 
+
With an ECF created asynchronous proxy, the consumer thread that calls helloAsyncService.helloAsync is guaranteed '''not''' to block, and the returned Future
 +
can be used to access the value at some later point.
  
A nice thing is that neither the consumer nor the host have to actually implement the asynchronous proxy.  The proxy is automatically constructed by the ECF remote services implementation when accessed by a consumer.
+
With ECF's asynchronous proxies, neither the consumer nor the host have to actually implement the asynchronous proxy behavior.  The proxy is automatically constructed by the ECF remote services implementation when accessed by a consumer.
  
 
At this point, you might ask:  But how is the asynchronous proxy defined?  i.e. where does the '''helloAsyncService''' come from?
 
At this point, you might ask:  But how is the asynchronous proxy defined?  i.e. where does the '''helloAsyncService''' come from?
Line 49: Line 44:
  
 
<source lang="java">
 
<source lang="java">
public interface IHelloAsync extends IAsyncRemoteServiceProxy {
+
public interface IHelloAsync {
  
public void helloAsync(String from, IAsyncCallback<String> callback);
+
public Future<String> helloAsync(String from);
 
}
 
}
 
</source>
 
</source>
  
Notice that this asynchronous service interface declaration resembles the IHello service interface declaration, but differs from it in a couple of ways:
+
Notice that this asynchronous service interface declaration resembles the IHello service interface declaration, but differs from it in several specific ways:
  
 
<ol><li>The name is <b>IHelloAsync</b> rather than <b>IHello</b>
 
<ol><li>The name is <b>IHelloAsync</b> rather than <b>IHello</b>
<li>It extends <b>IAsyncRemoteServiceProxy</b></li>
 
 
<li>The method name is <b>helloAsync</b> rather than <b>hello</b></li>
 
<li>The method name is <b>helloAsync</b> rather than <b>hello</b></li>
<li>The method arguments are String and IAsyncCallback rather than just String</li>
+
<li>The return value is Future<String></li>
 
</ol>
 
</ol>
  
With ECF 3.3 remote services, when a proxy is created, '''iff''' an interface class with the name '''[fq service interface name]Async''' can be loaded and it extends IAsyncRemoteServiceProxy then the proxy will implement that interface.  So, for our hello example the proxy service reference will implement both the '''IHello''' and '''IHelloAsync''' methods, and the consumer can use '''either''' of the methods declared.  So, for example
+
With ECF remote services, when a IHello proxy is created, the proxy will also implement the IHelloAsync interface.  So, for example
  
 
<source lang="java">
 
<source lang="java">
IHello helloService = ...get service reference via declarative services/injection, or ServiceTracker, or other...
+
IHello helloService = ...get service via DS/injections, ServiceTracker or otherServiceTracker, or other...
 
if (helloService instanceof IHelloAsync) {
 
if (helloService instanceof IHelloAsync) {
 
   IHelloAsync helloServiceAsync = (IHelloAsync) helloService;
 
   IHelloAsync helloServiceAsync = (IHelloAsync) helloService;
 
   // call it asynchronously
 
   // call it asynchronously
   helloAsyncService.helloAsync("slewis", new IAsyncCallback<String>() {
+
   Future<String> future = helloAsync("slewis");
      public void onSuccess(String result) {
+
  // do other things
          // Do something with result
+
  String result = future.get();
      }
+
  // do something with result...
      public void onFailure(Throwable t) {
+
          // Deal with network failure in appropriate manner
+
      }
+
  });
+
 
}
 
}
 
</source>
 
</source>
  
This gives the consumer maximum flexibility in determining how a given invocation will occur (i.e. synchronously or asynchronously.  The caller can use the synchronous service proxy, or simply cast to the asynchronous service proxy.  Or both invocation methods can be used as desired.
+
or
  
All that's required to get this automatic creation of an asynchronous proxy is for the service interface creator to declare an *Async class using these rules:
+
<source lang="java">
 +
IHelloAsync helloAsyncService = ...get service via DS/injections, ServiceTracker or other
 +
  // call it asynchronously
 +
  Future<String> future = helloAsync("slewis");
 +
  // do other things
 +
  String result = future.get();
 +
  // do something with result...
 +
</source>
 +
 
 +
This gives the consumer maximum flexibility in determining how a given invocation will occur (i.e. synchronously or asynchronously.  The caller can use the synchronous service proxy, or the asynchronous service proxy.  Or both invocation methods may be used.
 +
 
 +
All that's required is for the service interface to declare an *Async service type class using these rules:
  
 
<ol>
 
<ol>
 
<li>Class name must be <b>[service interface classname]Async</b>
 
<li>Class name must be <b>[service interface classname]Async</b>
 
<ul><li>Example:  <b>IHello -> IHelloAsync</b></li></ul></li>
 
<ul><li>Example:  <b>IHello -> IHelloAsync</b></li></ul></li>
<li>The class must extend [http://www.eclipse.org/ecf/org.eclipse.ecf.docs/api/org/eclipse/ecf/remoteservice/IAsyncRemoteServiceProxy.html IAsyncRemoteServiceProxy]. 
 
<ul><li>Example:  <b>IHelloAsync extends IAsyncRemoteServiceProxy</b></li></ul></li>
 
 
<li>For any methods that should be exposed to the consumer via the asynchronous proxy
 
<li>For any methods that should be exposed to the consumer via the asynchronous proxy
 
<ul>
 
<ul>
 
<li>Name the method <b>[methodName]Async</b>
 
<li>Name the method <b>[methodName]Async</b>
 
<ul><li>Example:  <b>hello -> helloAsync</b></li></ul></li>
 
<ul><li>Example:  <b>hello -> helloAsync</b></li></ul></li>
<li>Add [http://www.eclipse.org/ecf/org.eclipse.ecf.docs/api/org/eclipse/ecf/remoteservice/IAsyncCallback.html IAsyncCallback] as the <b>last</b> argument 
+
<li>The return value should always be of type <b>java.util.concurrent.Future</b> with the actual return type via the Future generic type...e.g. Future<String></li>
<ul><li>Example:  <b>hello(String) -> helloAsync(String,IAsyncCallback)</b></li></ul></li>
+
<li>The return value should always be <b>void</b>, as there is not any synchronous result...the asynchronous call result is given in '''onSuccess''' callback</li>
+
 
</ul>
 
</ul>
 
</li>
 
</li>
 
</ol>
 
</ol>
  
====Example====
+
====Another Example====
=====Original Service Interface=====
+
<source lang="java">package org.eclipse.ecf.examples.remoteservices.hello;
+
 
+
public interface IHello {
+
 
+
public String hello(String from);
+
+
}
+
</source>
+
=====Asynchronous Service Interface=====
+
<source lang="java">package org.eclipse.ecf.examples.remoteservices.hello;
+
import org.eclipse.ecf.remoteservice.IAsyncCallback;
+
import org.eclipse.ecf.remoteservice.IAsyncRemoteServiceProxy;
+
 
+
public interface IHelloAsync extends IAsyncRemoteServiceProxy {
+
 
+
public void helloAsync(String from, IAsyncCallback<String> callback);
+
+
}
+
</source>
+
 
+
====A Second Example====
+
 
=====Original Service Interface=====
 
=====Original Service Interface=====
 
<source lang="java">package my.package;
 
<source lang="java">package my.package;
Line 134: Line 109:
 
=====Asynchronous Service Interface=====
 
=====Asynchronous Service Interface=====
 
<source lang="java">package my.package;
 
<source lang="java">package my.package;
import org.eclipse.ecf.remoteservice.IAsyncCallback;
 
import org.eclipse.ecf.remoteservice.IAsyncRemoteServiceProxy;
 
import org.eclipse.equinox.concurrent.future.IFuture;
 
 
public interface IFooAsync extends IAsyncRemoteServiceProxy {
 
 
    public void barAsync(URI uri, String something, IAsyncCallback callback);
 
    public IFuture barAsync(URI uri, String something);
 
}
 
</source>
 
 
===Future Results===
 
 
[http://www.eclipse.org/ecf/org.eclipse.ecf.docs/api/org/eclipse/ecf/remoteservice/IAsyncCallback.html IAsyncCallback] is one approach to achieving asynchronous invocation of remote services.  Another that is also supported by ECF 3.3 remote services is [http://en.wikipedia.org/wiki/Futures_and_promises futures].  Future objects allow the consumer to perform operations '''in between''' initiating a remote call and receiving a result. 
 
So, for example, here is a use of a future result to initiate a remote call, perform some additional local operations and then get a result from the future.
 
 
<source lang="java">
 
// This will not block/return immediately
 
IFuture future = fooServiceAsync.barAsync(uri,something);
 
// ...do other local computation here...
 
// get result
 
String result = (String) future.get();
 
// ...
 
</source>
 
 
ECF 3.3 remote service supports asynchronous access via either [http://www.eclipse.org/ecf/org.eclipse.ecf.docs/api/org/eclipse/ecf/remoteservice/IAsyncCallback.html IAsyncCallback] or futures, or both.  To expose the '''IHello.hello''' method for asynchronous access via both callback and future approaches the '''IHelloAsync''' class would be declared like this
 
 
<source lang="java">package org.eclipse.ecf.examples.remoteservices.hello;
 
import org.eclipse.ecf.remoteservice.IAsyncCallback;
 
import org.eclipse.ecf.remoteservice.IAsyncRemoteServiceProxy;
 
import org.eclipse.equinox.concurrent.future.IFuture;
 
 
public interface IHelloAsync extends IAsyncRemoteServiceProxy {
 
 
    // async via callback
 
    public void helloAsync(String from, IAsyncCallback<String> callback);
 
    // async via future
 
    public IFuture helloAsync(String from);
 
 
}
 
</source>
 
 
So the rules shown above can be amended
 
 
<ol>
 
  <li>Class name must be <b>[service interface classname]Async</b></li>
 
  <li>The class must extend [http://www.eclipse.org/ecf/org.eclipse.ecf.docs/api/org/eclipse/ecf/remoteservice/IAsyncRemoteServiceProxy.html IAsyncRemoteServiceProxy]</li>
 
  <li>For any methods that should be exposed to the consumer via the asynchronous proxy
 
    <ul>
 
      <li>Name the method <b>[methodName]Async</b>
 
        <li>For asynchronous callback
 
          <ol>
 
            <li>Add [http://www.eclipse.org/ecf/org.eclipse.ecf.docs/api/org/eclipse/ecf/remoteservice/IAsyncCallback.html IAsyncCallback] as the <b>last</b> argument</li>
 
            <li>The return type should be <b>void</b></li>
 
          </ol>
 
        </li>
 
        <li>For future results
 
          <ol>
 
            <li>The return type should be <b>org.eclipse.equinox.concurrent.future.IFuture</b>
 
              <ul>
 
                <li>Example:  <b>String hello(String) -> IFuture helloAsync(String)</b></li>
 
              </ul>
 
            </li>
 
          </ol>
 
        </li>
 
      </li>
 
    </ul>
 
  </li>
 
</ol>
 
 
===Future from Concurrent API===
 
In addition to the org.eclipse.equinox.concurrent.future.IFuture described above, ECF's asynchronous proxies now work with the java.util.concurrent.Future.  For example, an asynchronous proxy can be declared:
 
 
<source lang="java">package org.eclipse.ecf.examples.remoteservices.hello;
 
import org.eclipse.ecf.remoteservice.IAsyncRemoteServiceProxy;
 
 
import java.util.concurrent.Future;
 
import java.util.concurrent.Future;
  
public interface IHelloAsync extends IAsyncRemoteServiceProxy {
+
public interface IFooAsync {
  
    // async via concurrent future
+
     public Future<String> barAsync(URI uri, String something);
     public Future<String> helloAsync(String from);
+
+
 
}
 
}
 
</source>
 
</source>
 
As described above, ECF's RSA implementation will, by default, create a proxy that includes the IHelloAsync interfaces in the set of interfaces exposed by the proxy to consumers.  When/consumers call helloAsync("who") a non-null java.util.concurrent.Future instance will be returned and may be used to asynchronously get any results.
 
 
===Full Example with Source===
 
 
A full, working example of the hello world remote service, including the IHello service declaration, the IHelloAsync asynchronous service declaration, as well as the server host impl, consumer, and code showing the use of both synchronous proxy asynchronous proxies (both callback and future-based) is available here: [[Getting Started with ECF's OSGi Remote Services Implementation]]
 
  
 
==Notes==
 
==Notes==

Revision as of 17:58, 8 April 2014

ECF's Remote Services API, which is used to implement OSGi remote services and remote service admin specifications, has the ability for consumers/clients to use asynchronous/non-block remote method calls.

Normal/Synchronous OSGi (Remote) Services

When an OSGi service is actually invoked today, this is done by the caller calling a method on the service. For example, consider a simple 'hello' service:

package org.eclipse.ecf.examples.remoteservices.hello;
 
public interface IHello {
 
	public String hello(String from);
 
}

Once a consumer of this service gets a valid service reference (i.e. through ServiceTracker, injection via declarative services (DS) or other injection framework, BundleContext.getServiceReference or others), it can actually invoke/use the service via a method call on 'hello':

String response = helloService.hello("slewis");

With OSGi remote services, this 'helloService' may actually be a proxy. If invoked as above a proxy will marshall the method arguments (i.e. 'slewis' String in this case), and communicate with the remote service host via some network (i.e. via some protocol). If there is a result of the call, then it will then be unmarshalled and returned to the caller thread. With synchronous invocation the thread that calls 'hello' will block until this entire process is complete.

Asynchronous Remote Services

ECF has added the ability to declare asynchronous access to a remote method, so that the consumer can ge guaranteed that calling the service will not block. So, for example, it's now possible to make a non-blocking call to the hello service like this

Future future = helloAsyncService.helloAsync("slewis");

With an ECF created asynchronous proxy, the consumer thread that calls helloAsyncService.helloAsync is guaranteed not to block, and the returned Future can be used to access the value at some later point.

With ECF's asynchronous proxies, neither the consumer nor the host have to actually implement the asynchronous proxy behavior. The proxy is automatically constructed by the ECF remote services implementation when accessed by a consumer.

At this point, you might ask: But how is the asynchronous proxy defined? i.e. where does the helloAsyncService come from?

The answer to this is that it is defined in a new/second service interface...called the asynchronous service interface that is related to the IHello service interface:

public interface IHelloAsync {
 
	public Future<String> helloAsync(String from);
}

Notice that this asynchronous service interface declaration resembles the IHello service interface declaration, but differs from it in several specific ways:

  1. The name is IHelloAsync rather than IHello
  2. The method name is helloAsync rather than hello
  3. The return value is Future<String>

With ECF remote services, when a IHello proxy is created, the proxy will also implement the IHelloAsync 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
   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
   Future<String> future = helloAsync("slewis");
   // do other things
   String result = future.get();
   // do something with result...

This gives the consumer maximum flexibility in determining how a given invocation will occur (i.e. synchronously or asynchronously. The caller can use the synchronous service proxy, or the asynchronous service proxy. Or both invocation methods may be used.

All that's required is for the service interface to declare an *Async service type class using these rules:

  1. Class name must be [service interface classname]Async
    • Example: IHello -> IHelloAsync
  2. For any methods that should be exposed to the consumer via the asynchronous proxy
    • Name the method [methodName]Async
      • Example: hello -> helloAsync
    • The return value should always be of type java.util.concurrent.Future with the actual return type via the Future generic type...e.g. Future<String>

Another Example

Original Service Interface
package my.package;
 
public interface IFoo {
 
	public String bar(URI uri, String something);
}
Asynchronous Service Interface
package my.package;
import java.util.concurrent.Future;
 
public interface IFooAsync {
 
    public Future<String> barAsync(URI uri, String something);
}

Notes


Eclipse Communication Framework
API
API DocumentationJavadocProviders
Development
Development GuidelinesIntegrators Guide

Back to the top