Eclipse RCP and REST – Making Asynchronous Calls

In my last blog post I described how to access REST services from an Eclipse RCP application using the ECF Remote Services JAX-RS Jersey Client Provider. It turns out that with a few minor changes we can also access these REST services asynchronously.

I’ll demonstrate here how to modify the SpaceX Launch Service example to use asynchronous calls.

Adding the osgi.async intent

OSGi R7 introduced the Asynchronous Service Specification which applies to services generally. It also introduced a way for remote services to support asynchronous calls.

While there are parts of the specification that manage asynchronous behavior on both the client and server, my main concern is allowing a client to make an asynchronous REST call without knowing anything about the server implementation. Especially when using microservice architectures, the less we know about the server the better.

In the end, all we need to do is add one property to our endpoint description, which is managed by the EDEF XML file. We’ll be adding the osgi.async intent, like this:

<property name="service.intents" value-type="String">
    <array>
        <value>osgi.async</value>
    </array>
</property>

Note that this property can be added whether or not you want to immediately support asynchronous calls. Once the intent has been added, your JAX-RS interfaces can support a mixture of synchronous and asynchronous calls.

Adding an asynchronous method to the JAX-RS interface

On the JAX-RS interface, the only change we need to make is to add a method that returns one of four types:

  • Future
  • CompletableFuture
  • CompletableStage
  • Promise (OSGi specific)

When the ECF JAX-RS Jersey Client finds one of these return types, it will automatically wrap the REST call with the appropriate asynchronous type and return that to the caller. So for our SpaceX Launch Service, we could modify the interface to look like this.

@Path("/launches")
public interface LaunchService {

	@GET
	@Produces(MediaType.APPLICATION_JSON)
	@Path("/")
	public List<Launch> getLaunches();

        /* new asynchronous method */

	@GET
	@Produces(MediaType.APPLICATION_JSON)
	@Path("/")
	public CompletableFuture<List<Launch>> getLaunchesAsync();
}

Note that OSGi Promises specification is especially useful when running on JVMs that do not support CompletableFuture. In my code, I prefer to use native Java types whenever possible.

Integrating with Eclipse RCP

In the SpaceX example, the Eclipse RCP client accesses the Launch Service using dependency injection. We can make the asynchronous request by calling the method returning a CompletableFuture and managing the callback.

public class LaunchPart {

     @Inject
     @Service
     private LaunchService launchService;

     @PostConstruct
     public void createComposite(Composite parent) {

          CompletableFuture<List<Launch>> = launchService.getLaunchesAsync();

          launchesFuture.thenAccept((launches) -> {
               /* process launch data here */
          });
     }
}

Wrapping up

With a few small changes, we can easily create JAX-RS interfaces that mix synchronous and asynchronous behavior. The example code on GitHub has been updated to demonstrate how this works.

https://github.com/modular-mind/spacex-client