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 "Tutorial: Building your first Asynchronous OSGi Remote Service"

(Define the Asynchronous Remote Service)
Line 88: Line 88:
  
 
public Long getCurrentTime();
 
public Long getCurrentTime();
 
 
}
 
}
 
</source>
 
</source>
Line 102: Line 101:
  
 
public interface ITimeServiceAsync {
 
public interface ITimeServiceAsync {
/**
+
 
* Get current time using Java 8 {@link CompletableFuture}.
+
*
+
* @return CompletableFuture<Long> The future value time in milliseconds since Jan 1, 1970. Will not
+
*        return <code>null</code>.
+
*/
+
 
public CompletableFuture<Long> getCurrentTimeAsync();
 
public CompletableFuture<Long> getCurrentTimeAsync();
 
}
 
}
 
</source>
 
</source>
  
Note the following about this service interface:
+
Note the following about this interface:
  
 
#The name of the asynchronous remote service interface is '''ITimeServiceAsync''', which is the same as '''ITimeService''' + '''Async'''.  In general, this async remote service interface must be named '''OriginalServiceInterfaceNameAsync'''.
 
#The name of the asynchronous remote service interface is '''ITimeServiceAsync''', which is the same as '''ITimeService''' + '''Async'''.  In general, this async remote service interface must be named '''OriginalServiceInterfaceNameAsync'''.

Revision as of 16:57, 17 April 2014


Introduction

In a previous tutorial, we went through how to define, implement, and execute a simple OSGi Remote Service using ECF's Remote Service implementation. By definition, Remote Services require communication between processes/frameworks since local OSGi services are based upon synchronous invocation of a service method (i.e. the calling thread blocks while the service method is being invoked, and only continues after the service method returns). With Remote Services, this means that the calling thread can block as the remote method invocation is sent over a network, handled by the remote service, and then the response (method return value) is returned over the network. As well, the remote method invocation may fail outright...because of a network partition, or the remote service host failure. The handling of such failures may block the calling thread for a significant length of time.

To support asynchronous (non-blocking) remote service invocation, ECF's implementation of OSGi Remote Services ECF has created Asynchronous Remote Services. This provides developers of Remote Services the option to expose non-blocking/asynchronous remote method invocation to consumers of their remote service, without any additional programming. This tutorial will step through the process of defining and using an asynchronous remote service.

Define the Asynchronous Remote Service

In the previous tutorial a simple time service was defined:

package com.mycorp.examples.timeservice;
 
public interface ITimeService {
 
	public Long getCurrentTime();
}

As described above, when the consumer calls/invokes the getCurrentTime() method, without any implementation error it's possible for this method to block for a relatively long time...and/or throw a runtime exception...because the communication failed between the consumer and the remote service host.

ECF uniquely supports the creation of an Asynchronous Remote Service interface...that will automatically be available to clients of Remote Services. For example, here is the asynchronous service interface for the ITimeService given above:

package com.mycorp.examples.timeservice;
 
import java.util.concurrent.CompletableFuture;
 
public interface ITimeServiceAsync {
 
	public CompletableFuture<Long> getCurrentTimeAsync();
}

Note the following about this interface:

  1. The name of the asynchronous remote service interface is ITimeServiceAsync, which is the same as ITimeService + Async. In general, this async remote service interface must be named OriginalServiceInterfaceNameAsync.
  2. The name of the method is getCurrentTimeAsync, which is the same oas getCurrentTime and Async. Like the name of the service interface, in general the async method must be named OriginalServiceMethodNameAsync
  3. The return value is of type Future<Long>'. This uses the Java Concurrent API Future class along with the original type returned by the getCurrentTime method (Long) as the generic qualifier for the Future class. In general, the pattern here must be Future<OriginalType>. Note that the Void type can be used for methods that return void...e.g. Future<Void>.
  4. The asynchronous service interface must extend com.mycorp.examples.timeservice.IAsyncRemoteServiceProxy. The IAsyncRemoteServiceProxy interface is a marker interface (no methods are declared) used at runtime to distinguish async service interfaces from normal/synchronous service interfaces, and associate them with one another. Note: The OSGi Remote Service specification does not currently have any support for async remote service invocation, although there is now RFC-206 for introducing asynchronous access to OSGi services more generally. As this specification develops, ECF's implementation will continue to support all OSGi specifications, including RFC-206.
  5. The contract for this asynchronous remote service is that getCurrentTimeAsync will not block, and immediately return a non-null instance of Future<Long>. At the discretion of the caller, the Future<Long> instance may be queried about the availability of the result/completion of the call, and then able to access the underlying result via the methods exposed by the Future class.

This single asynchronous service interface is all that is needed for the remote service host to expose non-blocking access to the ITimeService.getCurrentTime() method to consumers. No actual implementation is necessary, as it's provided automatically by the ECF Remote Service implementation.

Consumer: Discover and Use the Asynchronous Remote Service

To invoke methods on a remote service, the consumer must first discover the service. Asynchronous Remote Service discovery works exactly the same as normal remote service discovery and so for details of that process see the previous tutorial.

In the previous tutorial, upon discovery, the ITimeService instance is injected in the consumer via the bindTimeService method:

package com.mycorp.examples.timeservice.consumer.ds;
 
import com.mycorp.examples.timeservice.ITimeService;
 
public class TimeServiceComponent {
 
	void bindTimeService(ITimeService timeService) {
		System.out.println("Discovered ITimeService via DS");
		// Call the service and print out result!
		System.out.println("Current time is: " + timeService.getCurrentTime());  // Call the ITimeService remote service
	}
}

All that is necessary to access the ITimeServiceAsync methods is to query the injected timeService instance for the desired asynchronous remote service interface. For example:

void bindTimeService(ITimeService timeService) {
    if (timeService instanceof ITimeServiceAsync) {
        ITimeServiceAsync asyncTimeService = (ITimeServiceAsync) timeService;
        System.out.println("Discovered ITimeServiceAsync via DS");
        // Call the asynchronous remote service.  Unlike the synchronous getTimeService(),
        // this method will not block
        Future<Long> currentTimeFuture = asyncTimeService.getCurrentTimeAsync();
        // potentially do other operations here...
        System.out.println("Current time is: " + currentTimeFuture.get());  
    }
}

With ECF's implementation of remote services, if an ITimeService proxy is created for the consumer, and an asynchronous service interface (i.e. ITimeServiceAsync) is present then the proxy will implement the asynchronous service interface. The actual implementation is provided directly by ECF Remote Services, and neither the host nor the consumer must do anything further for the asynchronous remote service to be usable by consumers. All that's necessary is to declare the asynchronous remote service (ITimeServiceAsync) in the same package as the original (ITimeService), and export a host instance of the remote service as described by the previous tutorial.

Using Java8 CompletableFuture for Asynchronous Remote Services

In Java 8, in addition to lambdas, streams and other features, a new type of Future was introduced, called CompletableFuture. Unlike java.util.concurrent.Future, CompletableFuture can be used in a way guaranteed not to block, whereas calling Future.get() can block...if the underlying result does not yet exist.

In ECF 3.8.1/Luna support for CompletableFuture has been added. As shown above, async proxy interfaces can return either java.util.concurrent.Future as above...or java8's java.util.concurrent.CompletableFuture. For example, ITimeServiceAsync can be defined to return CompletableFuture rather than Future:

package com.mycorp.examples.timeservice;
 
import java.util.concurrent.CompletableFuture;
 
public interface ITimeServiceAsync {
	/**
	 * Get current time using Java 8 {@link CompletableFuture}.
	 * 
	 * @return CompletableFuture<Long> The future value time in milliseconds since Jan 1, 1970. Will not
	 *         return <code>null</code>.
	 */
	public CompletableFuture<Long> getCurrentTimeAsync();
}



Background and Related Articles

Asynchronous Proxies for Remote Services

Getting Started with ECF's OSGi Remote Services Implementation

OSGi Remote Services and ECF

Static File-based Discovery of Remote Service Endpoints

Download ECF Remote Services/RSA Implementation

How to Add Remote Services/RSA to Your Target Platform

Back to the top