Skip to main content

Notice: this Wiki will be going read only early in 2024 and edits will no longer be possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

Difference between revisions of "ECF/Asynchronous Remote Services"

< ECF
Line 59: Line 59:
 
Notice that this async interface declaration resembles the IHello service interface declaration, but differs from it in a couple of ways:
 
Notice that this async interface declaration resembles the IHello service interface declaration, but differs from it in a couple of ways:
  
<ol><li>The name is <b>IHelloAsync</b> rather than <b>IHello'''</a>
+
<ol><li>The name is <b>IHelloAsync</b> rather than <b>IHello</b>
 
<li>It extends <b>IAsyncRemoteServiceProxy</b></li>
 
<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>
Line 88: Line 88:
  
 
<ol>
 
<ol>
<li>Class name must be <b>[service interface classname]Async</b>Example:  <b>IHello -> IHelloAsync</b></li>
+
<li>Class name must be <b>[service interface classname]Async</b>
<li>The class must extend [http://www.eclipse.org/ecf/org.eclipse.ecf.docs/api/org/eclipse/ecf/remoteservice/IAsyncRemoteServiceProxy.html IAsyncRemoteServiceProxy].  Example:  <b>IHelloAsync extends IAsyncRemoteServiceProxy</b></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>Example:  <b>hello -> helloAsync</b></li>
+
<li>Name the method <b>[methodName]Async</b>
<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. Example:  <b>hello(String) -> helloAsync(String,IAsyncCallback)</b></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   
 +
<ul><li>Example:  <b>hello(String) -> helloAsync(String,IAsyncCallback)</b></li></ul></li>
 
<li>The return value, should be <b>void</b></li>
 
<li>The return value, should be <b>void</b></li>
 
</ul>
 
</ul>
Line 174: Line 178:
 
So the rules shown above can be amended
 
So the rules shown above can be amended
  
#Class name must be '''<service interface classname>Async'''.
+
<ol>
#The class must extend [http://www.eclipse.org/ecf/org.eclipse.ecf.docs/api/org/eclipse/ecf/remoteservice/IAsyncRemoteServiceProxy.html IAsyncRemoteServiceProxy].   
+
<li>Class name must be <b>[service interface classname]Async</b>
#For any methods that should be exposed to the consumer via the asynchronous proxy
+
<ul><li>Example:  <b>IHello -> IHelloAsync</b></li></ul></li>
##Name the method '''<methodName>Async'''.  
+
<li>The class must extend [http://www.eclipse.org/ecf/org.eclipse.ecf.docs/api/org/eclipse/ecf/remoteservice/IAsyncRemoteServiceProxy.html IAsyncRemoteServiceProxy].   
If using callback
+
<ul><li>Example:  <b>IHelloAsync extends IAsyncRemoteServiceProxy</b></li></ul></li>
##Add [http://www.eclipse.org/ecf/org.eclipse.ecf.docs/api/org/eclipse/ecf/remoteservice/IAsyncCallback.html IAsyncCallback] as the '''last''' argumentExample:  '''hello(String) -> helloAsync(String,IAsyncCallback)'''
+
<li>For any methods that should be exposed to the consumer via the asynchronous proxy
##The return value, should be '''void'''.
+
<ul>
If using future
+
<li>Name the method <b>[methodName]Async</b>
##The return value should be IFuture.
+
<ul><li>Example: <b>hello -> helloAsync</b></li></ul></li>
 +
<ul>
 +
<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  
 +
<ul><li>Example:  <b>hello(String) -> helloAsync(String,IAsyncCallback)</b></li></ul></li>
 +
<li>The return value, should be <b>void</b></li>
 +
</ol>
 +
</li>
 +
<li>For future results
 +
<ol>
 +
<li>The return value should be IFuture
 +
<ul><li>Example:  <b>hello(String) -> helloAsync(String,IAsyncCallback)</b></li></ul></li>
 +
</ol>
 +
</li>
 +
</ol>

Revision as of 19:40, 15 April 2010

ECF's Remote Services API, which is used to implement OSGi 4.2 remote services, has had the ability for consumers/clients to use asynchronous/non-block remote method calls for at least the last 2 years.

For background on the discussion about asynchronous remote services invocation in general and possible future standardization by the OSGI standards organization see Peter Kriens blog entry, and Scott Lewis' blog entry.

Normal/Synchronous Proxies

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 void hello(String from);
 
}

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':

helloService.hello("slewis");

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.

Asynchronous Proxies

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

helloAsyncService.helloAsync("slewis", new IAsyncCallback() {
    public void onSuccess(Object result) {
        // Do something with result
    }
    public void onFailure(Throwable t) {
        // Deal with network failure in appropriate manner
    }
});

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.

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.

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

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

public interface IHelloAsync extends IAsyncRemoteServiceProxy {
 
	public void helloAsync(String from, IAsyncCallback callback);
}

Notice that this async interface declaration resembles the IHello service interface declaration, but differs from it in a couple of ways:

  1. The name is IHelloAsync rather than IHello
  2. It extends IAsyncRemoteServiceProxy
  3. The method name is helloAsync rather than hello
  4. The method arguments are String and IAsyncCallback rather than just String

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

IHello helloService = ...get service reference via declarative services/injection, or ServiceTracker, or other...
if (helloService instanceof IHelloAsync) {
   IHelloAsync helloServiceAsync = (IHelloAsync) helloService;
   // call it asynchronously
   helloAsyncService.helloAsync("slewis", new IAsyncCallback() {
       public void onSuccess(Object result) {
           // Do something with result
       }
       public void onFailure(Throwable t) {
           // Deal with network failure in appropriate manner
       }
   });
}

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 proxy, or simply cast to the asynchronous proxy. Or both invocation methods can be used as desired.

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:

  1. Class name must be [service interface classname]Async
    • Example: IHello -> IHelloAsync
  2. The class must extend IAsyncRemoteServiceProxy.
    • Example: IHelloAsync extends IAsyncRemoteServiceProxy
  3. For any methods that should be exposed to the consumer via the asynchronous proxy
    • Name the method [methodName]Async
      • Example: hello -> helloAsync
    • Add IAsyncCallback as the last argument
      • Example: hello(String) -> helloAsync(String,IAsyncCallback)
    • The return value, should be void

Example

Original Service Interface
package org.eclipse.ecf.examples.remoteservices.hello;
 
public interface IHello {
 
	public void hello(String from);
 
}
Asynchronous Proxy
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 callback);
 
}

A Second Example

Original Service Interface
package my.package;
 
public interface IFoo {
 
	public String bar(URI uri, String something);
}
Asynchronous Proxy
package my.package;
import org.eclipse.ecf.remoteservice.IAsyncCallback;
import org.eclipse.ecf.remoteservice.IAsyncRemoteServiceProxy;
 
public interface IHelloAsync extends IAsyncRemoteServiceProxy {
 
	public void barAsync(URI uri, String something, IAsyncCallback callback);
}

Futures

IAsyncCallback is one approach to achieving asynchronous invocation of remote services. Another that is also supported by ECF 3.3 remote services is 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.

// This will not block/return immediately
IFuture future = fooServiceAsync.barAsync(uri,something);
// ...do other local computation here...
// get result
String result = (String) future.get();
// ...

ECF 3.3 remote service supports asynchronous access via either 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

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 callback);
    // async via future
    public IFuture helloAsync(String from);
 
}

So the rules shown above can be amended

  1. Class name must be [service interface classname]Async
    • Example: IHello -> IHelloAsync
  2. The class must extend IAsyncRemoteServiceProxy.
    • Example: IHelloAsync extends IAsyncRemoteServiceProxy
  3. For any methods that should be exposed to the consumer via the asynchronous proxy
    • Name the method [methodName]Async
      • Example: hello -> helloAsync
      • For asynchronous callback
        1. Add IAsyncCallback as the last argument
          • Example: hello(String) -> helloAsync(String,IAsyncCallback)
        2. The return value, should be void
      • For future results
        1. The return value should be IFuture
          • Example: hello(String) -> helloAsync(String,IAsyncCallback)
      • </ol>

Copyright © Eclipse Foundation, Inc. All Rights Reserved.