My current area of interest is in exploring how modular user interfaces, and Eclipse RCP in particular, can help us leverage the benefits of microservices. Modular UIs offer a lot of possibilities in this area, and I’ll have much more to say about this in the coming months.
As a starting point, however, we need the technical infrastructure to integrate with back-end microservices. In this post I’ll demonstrate how an Eclipse RCP client can easily consume a service exposed as a REST endpoint. I’ve also made an repository available on GitHub that shows the implementation in more detail. This example has been kept as simple as possible to clearly show the moving parts. You should be able to clone this repository and get things running very quickly.
SpaceX REST API
When demonstrating this approach, it’s helpful to have a stable and interesting REST API to consume. Luckily SpaceX provides a set of well-documented services that describe various domains – launches, missions, rockets and many others. In this example, we’ll be consuming one of these, the Launch Service.
Setting up the environment
This approach relies on a set of Eclipse frameworks that combine to make it very simple to consume REST services using JAX-RS. The stack of these frameworks looks like this:
These frameworks allow us to create a Java interface annotated with JAX-RS, and then expose a proxy to that interface as an OSGi service. Both CXF and Jersey implementations are provided, and Jersey is used in this example. Jackson is used by both providers to handle data binding between the service and the Java types.
There is a Git repository for the JAX-RS providers. If you would like to consume the pre-built features in an Eclipse target definition file or Tycho build, you can use this p2 repository location:
https://raw.githubusercontent.com/ECF/JaxRSProviders/master/build
But if you’re trying to set this up for the first time, I highly recommend using the target definition I’ve included in the git repository.
Describing and discovering the service
One way to describe a service is to use an Endpoint Description (EDEF) file. An EDEF file describes how to connect to an endpoint and also how to map the service to Java types using JAX-RS. The two most important attributes described by an EDEF file are the URI of the endpoint and the Java type that will represent the service.
The EDEF file is discovered at runtime by including a Remote-Service entry in your OSGi manifest.
Remote-Service: OSGI-INF/LaunchService_EDEF.xml
At runtime, the EDEF file is read and a proxy for the Java interface is registered as an OSGi service. That service can then be called without knowing or caring that a REST service is working behind the scenes.
Implementing the service
The EDEF file specifies a Java interface annotated using JAX-RS.
@Path("/launches")
public interface LaunchService {
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("/")
public List<Launch> getLaunches();
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("/latest")
public Launch getLatestLaunch();
}
We also need to represent the launch data returned from the service. Jackson annotations have been used here to help with object mapping, but it’s possible to have a completely unannotated POJO as well.
@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
public class Launch {
private String flightNumber;
private String missionName;
public String getFlightNumber() {
return flightNumber;
}
public String getMissionName() {
return missionName;
}
}
At this point, running the code would result in a LaunchService instance available as an OSGi service. This service could be consumed using regular OSGi mechanisms, such as a Service Tracker or Declarative Services. While this service could be used in any OSGi environment, I’ll now describe how to access this service from an Eclipse RCP application.
Integrating with Eclipse RCP
Note: A previous version of this section suggested using a Declarative Services facade to access the JAX-RS service. I’ve updated this to reflect what I think is a better approach.
While an Eclipse RCP client can consume OSGi services using a Service Tracker or Declarative Services, we also have the ability to inject services directly into our UI components.
To inject a service creating using the JAX-RS provider, we need to do two things:
- Inject the service using both the @Inject and @Service annotations. The @Service annotation fixes some class-loading issues that occur with the normal injection mechanism.
- Configure the plug-in containing the UI component to be activated when one of it’s classes is requested. This can be done on the Overview page of the Manifest Editor.
At this point, the injected service can be used inside of the Eclipse RCP UI component to request SpaceX launch data.
public class LaunchPart {
@Inject
@Service
private LaunchService launchService;
@PostConstruct
public void createComposite(Composite parent) {
List<Launch> launches = launchService.getLaunches();
// Display data
}
}
In the part, I’m retrieving the launch number and name, and then displaying that data in a table. The running application currently looks like this.
Wrapping up
Very little code is required to consume REST services from an Eclipse RCP client. And once this infrastructure is set up, it’s possible to start using the power of a modular user interface to leverage microservice architectures across the entire stack.
In the coming months I’ll be blogging about specific issues related to REST and Eclipse RCP, including configuration (supporting multiple environments, etc.) and security (including getting access to HTTP requests/responses). I’ll also be continuing to focus on the bigger picture of how to best use Eclipse RCP in a microservices world.
For now, though, the best place to start would be to clone the git repository and run the code. It shouldn’t take longer than 10 minutes to get things running.
https://github.com/modular-mind/spacex-client
Finally, I’d like to thank Scott Lewis and the Eclipse Communication Framework team for doing the work that makes this all possible. If this all seems a bit too easy, remember all the effort that went into making it possible.