The Java ecosystem makes it easy to implement REST services. Using Java EE, Jakarta EE, JAX-RS, and JSON-B, the source code of a REST service looks like a plain Java program, enhanced with a few easy-to-understand annotations.
With the MicroProfile REST client, the same has become true for implementing the invoking side.
You don’t have to manually translate between the Java domain objects and their on-the-wire representation, since this is done automatically by JSON-B (as opposed to JSON-P).
However, there are several common use cases that still require additional boilerplate code if you want to remain on this level of abstraction. Jareto is a small Java library that provides useful features in an easy-to-use way, for both server- and client-side development.
For mapping Java exceptions: On the server side, Jareto helps with serialization to HTTP wire data (JSON); these JSON exceptions can also be parsed by non-Java clients and are customizable and allow transport of structured data. On the client side, Jareto helps with client deserialization from HTTP wire data (JSON).
For transporting HTTP metadata: Jareto helps with HTTP status codes and headers.
Depending on your requirements, you can use the server-side part of Jareto, or the client-side part, or both.
Jareto, created by my company, SVC, is available on GitHub under the Apache License Version 2.0 License. There, you can also find a demo project with a sample web application and JUnit tests.
Jareto setup
Jareto’s features are implemented using JAX-RS providers. To prevent accidental activation of certain providers that you don’t need or want (with future extensions in mind), Jareto politely abstains from autoregistration of providers. Likewise, if you want to use Jareto in a REST client, you must register the Jareto provider classes during client construction.
Mapping Java exceptions
Without additional measures, throwing a runtime or checked exception from your service usually results in a stack trace being returned to the invoker. Apart from the security implications, this prevents clients from identifying and handling exceptional cases in an automated way, so this is usually not what you want.
JAX-RS specifies an unchecked WebApplicationException that allows you to customize both the HTTP response status and the returned payload. However, you would still have to
Provide the payload using a lower-level JAX-RS response
Take care of unexpected exceptions such as NullPointerException (by means of a global exception handler)
Jareto provides both a checked AppException and an unchecked AppRuntimeException that accept the following data during construction:
- Error message
- Error code
- Error detail code (optional)
- HTTP status code (optional; defaults to 500)
In theory, transporting exception data would also be possible via HTTP headers (instead of JSON inside the HTTP response body). Even though this alternative might seem appealing at first glance, it does not scale to advanced use cases where exceptions will contain more-complex, structured data. In this respect, Jareto’s exception handling resembles that of GraphQL, which is also capable of returning arbitrary error data inside the response payload.
By contrast, a MicroProfile REST client equipped with Jareto can catch and handle these exceptions.
Transporting HTTP metadata
Although a MicroProfile REST client greatly simplifies invocations by (re-)using the service’s Java interface, it does not offer any direct access to HTTP request headers or to HTTP response status and headers. To gain access to these, you would have to write additional ResponseExceptionMapper and ClientRequestFilter code.
The client-side API is deliberately designed to be usable also in standalone Java environments without Contexts and Dependency Injection (CDI). This way, running Java-based system tests against your REST service is quick and easy, as it does not require any CDI setup.
On the server side, simple access to the HTTP headers does not require anything other than HttpServletRequest and HttpServletResponse from Jakarta EE.
Alternatively, reading HTTP request headers is also possible by using the JAX-RS annotation HeaderParam.
In case you would like to add a static HTTP header to your responses, you can do so by using Jareto’s @Header annotation at the class or method level of your REST service interface or implementation.