Introduction
One of the cool specifications produced by the MicroProfile group is the Rest Client for MicroProfile available from MP release 1.3.
It builds on top of the Client API of JAX-RS and allows you to use type-safe access to your endpoints without the need to programmatic interact with the Client API.
MicroProfile compliant server implementations need to implement this specification, but nothing says we cannot expand the usage into other environments (with a proper implementation) like use in Java SE (JavaFX seems must useful here) and plain Java EE.
Atbash has created an implementation of the specification so that it can be used in these environments and will use it within Octopus framework to propagate authentication and authorization information automatically in calls to JAX-RS endpoints.
The specification
A few words about the specification itself. JAX-RS 2.x contains a client API which allows you to access any ‘Rest’ endpoint in a uniform way.
Client client = ClientBuilder.newClient(); WebTarget employeeWebTarget = client.target("http://localhost:8080/demo/data").path("employees"); employeeWebTarget.request(MediaType.APPLICATION_JSON).get(new GenericType<List<Employee>>() { });
This client API is great because we can use it to call any endpoint, not even limited to Java ones. As long as they behave in a standard way.
But things can be improved, by moving away from the programmatic way of performing these calls, into a more declarative way.
If we could define some kind of interface like this
@Path("/employees") public interface EmployeeService { @GET @Produces(MediaType.APPLICATION_JSON) List<Employee> getAll(); }
And we just could ask an implementation of this interface which performs the required steps of creating the Client, WebTarget and invoke it for us in the background. This would make it much easier for the developer and makes it much more type-safe.
Creating the implementation of that interface is what MicroProfile Rest Client is all about.
The interface defined above can then be injected in any other CDI bean (need to add the @RegisterRestClient on the interface and preferably a CDI scope like @ApplicationScoped) and by calling the method, we actually perform a call to the endpoint and retrieve the result.
@Inject @RestClient private EmployeeService employeeService; public void doSomethingWithEmployees() { .... ... employeeService.getAll(); .... }
Atbash Rest Client
The specification also allows for a programmatic retrieval of the implementation, for those scenarios where no CDI is available.
However, remember that if we are running inside a CDI container, we can always retrieve some CDI bean by using
CDI.current().select();
from within any method.
The programmatic retrieval is thus an ideal candidate to use it in other environments or frameworks like JavaFX (basically every Java SE program), Spring, Kotlin, etc …
The programmatic retrieval starts from the RestClientBuilder with the newBuilder() method.
EmployeeService employeeService = RestClientBuilder.newBuilder() .baseUrl(new URL("https://localhost:8080/server/data")) .build(EmployeeService.class);
The above retrieves also an implementation for the Employee retrieval endpoint we used earlier on in this text.
For more information about the features like Exception handling, adding custom providers and more, look at the specification document.
The Atbash Rest Client is an implementation in Java 7 which can run on Java SE. It is created mainly for the Octopus Framework to propagate the user authentication (like username) and authorization information (like permissions) to JAX-RS endpoints in a transparent, automatically way.
A ClientRequestFilter is created in Octopus which creates a JWT token (compatible with MicroProfile JWT auth specification) containing username and permissions of the current user and this Filter can then be added as a provider to the MicroProfile Rest Client to have this security information available within the header of the call.
Since the current Octopus version is still based on Java SE 7, no other existing implementation could be used (MicroProfile is Java SE 8 based). The implementation is based on the DeltaSpike Proxy features and uses any Client API compatible implementation which is available at runtime.
Compliant, not certified.
Since all the MicroProfile specifications are Java 8 based, the API is also converted to Java SE 7. Except for a few small incompatibilities, the port is 100% interchangeable.
The most notable difference is creating the builder with the newBuilder() method. In the original specification, this is a static method of an interface which is not allowed in Java 7. For that purpose, an abstract class is created, AbstractRestClientBuilder which contains the method.
Other than that, class and method names should be identical, which makes the switch from Atbash Rest Client to any other implementation using Java 8 very smooth.
The Maven dependency has following coordinates (available on Maven Central):
<dependency> <groupId>be.atbash.mp.rest-client</groupId> <artifactId>atbash-rest-client-impl</artifactId> <version>0.5</version> </dependency>
If you are running on Java 8, you can use the Apache CXF MicroProfile Rest client
<dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-rs-mp-client</artifactId> <version>3.2.4</version> </dependency>
Which also can be used in a plain Java SE environment. Other implementations like the ones within Liberty and Payara Micro aren’t useable standalone in Java SE.
Remark
This first release of Atbash Rest Client is a pre-release, meaning that not all features of the specification are completely implemented. It is just a bare minimum for the a POC within Octopus.
For example, the handling of exceptions (because the endpoint returned a status in the range 4xx or 5xx) isn’t completely covered yet.
The missing parts will be implemented or improved in a future version.
Conclusion
With the MicroProfile Rest Client specification, it becomes easy to call some JAX-RS endpoints in a type-safe way. By using a declarative method, calling some endpoint becomes as easy as calling any other method within the JVM.
And since micro-services need to be called at some point from non-micro-service code, Java SE executable implementation is very important. It makes it possible to call it from plain Java EE, Java SE, any other framework or a JVM based language.
Have fun.