Work Around Solution For: MicroProfile Fault Tolerance Annotations Not Applied to Rest Client Interface

Photo of Rudy De Busscher by Rudy De Busscher

 

This blog describes the issue we currently have within the Payara Platform products when you try to combine the MicroProfile Rest Client specification and MicroProfile Fault Tolerance and provides a solution.

The MicroProfile Fault Tolerance annotations are currently not recognized when you use them on a MicroProfile Rest Client interface definition.

Let us quickly introduce both MicroProfile specifications.

MicroProfile Rest Client

MicroProfile Rest Client provides a type-safe approach to invoke RESTful services over HTTP. Calling an endpoint with the JAX-RS client functionality requires that you write several statements which resemble the low-level mechanics of the protocol.

Client client = ClientBuilder.newClient();
WebTarget webTarget = client.target("http://<host>:<port>/path/to/other/endpoint");
Response response = webTarget.request(MediaType.APPLICATION_JSON).get();

With MicroProfile Rest Client, you can use some high-level constructs, and calling a REST endpoint appears the same as calling any other service. The only difference, of course, is that it effectively uses a remote call.

You start by defining an interface which describes the endpoint:

@RegisterRestClient
@ApplicationScoped
public interface SomeService {

@GET
@Path("/remote/{name}")
String getValue(@PathParam("name") String name)

}

The important factor here is that you annotate the interface with @RegisterRestClient so that a proxy class will be created and can be used later on as a CDI bean.

The return type and parameter of the method, like path parameter or query parameters, need to match just as the HTTP method, GET in this case with the remote endpoint definition.

The proxy can be used in the following way (within any CDI bean or JAX-RS bean):

@Inject
@RestClient
private SomeService service;

public String doSomethingUseful() {
//...
String remoteValue = service.getValue("test");
//...
return ...

}

MicroProfile Fault Tolerance

Within a microservice architecture, it is important to use some fault tolerance strategies. Remote calls can occasionally fail or the remote service might take too long to respond. Retry policies, bulkheads, and circuit breakers are popular concepts to cope with this.

The MicroProfile Fault Tolerance specification defines these concepts as a series of annotations which acts as a CDI interceptor.

@Retry
public String doSomethingUseful() {

//...
return ...

}

The above CDI method will be retried in case there is a failure (exception) thrown within the method. The retry parameters, like the number of retries and time between retries, are used to determine how the retries are executed. And of course, it must make sense to retry the method and that we can expect a successful execution when we re-execute the method again. For this purpose, the @Retry annotation can be configured for example with a list of Exceptions when it should perform the retry.

The Issue

According to the MicroProfile Rest Client specification, the Fault Tolerance annotations must be taken into account. This is an extract from the specification text.

MP Rest Client implementations must ensure that MP Fault Tolerance annotations on client interfaces are honored. In general, these annotations are treated as CDI interceptor bindings.

So the following code example is valid and should result in a retry of the remote call in case it fails.

@RegisterRestClient
@ApplicationScoped
public interface SomeService {

@GET
@Path("/remote/{name}")
@Retry
String getValue(@PathParam("name") String name)

}

However, there are no TCK tests to verify this within MicroProfile as there is no real top-level project verifying all the inter specification behavior. And we also failed to implement this requirement correctly in the current version of the Payara Platform products.

The result is that when the remote call fails when using the Rest Client functionality, the call is not retried and the failure is propagated through the call stack of the client caller method.

Current Workaround

Until we have properly fixed this issue, you can use the following workaround. Since the @Retry annotation work as expected on a regular CDI bean, you can encapsulate the reference to the Rest Client proxy within a CDI method that is properly annotated.

@ApplicationScoped
public class RemoteService {

@Inject
@RestClient
private SomeService someService;

@Retry
public String somethingUseful(String name) {
return someService.getValue(name);
}

}

Since we have the @Retry annotation on the RemoteService.somethingUseful() method, the call to the remote service through the MicroProfile Rest Client functionality will be retried now.

Conclusion

The implementation of the MicroProfile Rest Client specification within the Payara Platform doesn't take into account the Fault Tolerance annotations that are defined on an interface definition for the moment. The workaround is to encapsulate the calls with MP Rest Client into a CDI bean when you need to combine them until this issue is solved.

Comments