How To Get Resource Method Information With ResourceInfo In Jakarta REST

Photo of Luqman Saeed by Luqman Saeed

Oftentimes in a Jakarta REST application, you may need to access some metadata about a given resource method matched by the Jakarta REST runtime, outside of a resource class. This could be so as to dynamically alter the client's request based on some custom business requirement, or for informative purposes. For example, in a Jakarta REST component like an exception mapper, you might want to get the currently matched resource method and get its HTTP method. 

The Jakarta REST API has the ResourceInfo interface for just such a situation. This interface is a container managed component that you can inject into other Jakarta REST components with the jakarta.ws.rs.core.Context annotation. The interface has two methods, getResourceMethod and getResourceClass that return a java.lang.reflect.Method and Class<?>, respectively. 

With the getResourceMethod, you can get metadata about the currently matched resource method. It's important to note that the methods on the ResourceInfo object can return null if they are invoked in a situation where the Jakarta REST runtime has not yet matched a given request to a resource method.

The code snippet below shows the use of the ResourceInfo in an exception mapper to get the matched HTTP method.

@Provider
public class ValidationExceptionMapper implements ExceptionMapper<ValidationException> {
    @Context
    UriInfo uriInfo;

    @Context
    ResourceInfo resourceInfo;

    @Override
    public Response toResponse(final ValidationException exception) {
        var httpMethods = List.of(POST.class, GET.class, PUT.class, DELETE.class, PATCH.class, OPTIONS.class, HEAD.class);

        Annotation[] declaredAnnotations = resourceInfo.getResourceMethod().getDeclaredAnnotations();

        String httpMethod = Arrays.stream(declaredAnnotations)
                .filter(d -> httpMethods.contains(d.annotationType())).findFirst()
                .map(a -> a.annotationType().getSimpleName()).orElse("");

        final var jsonObject = Json.createObjectBuilder()
                .add("host", uriInfo.getAbsolutePath().getHost())
                .add("resource", uriInfo.getAbsolutePath().getPath())
                .add("resourceMethod", httpMethod)
                .add("title", "Validation Errors");

        final var jsonArray = Json.createArrayBuilder();

        JsonObjectBuilder errorObject = Json.createObjectBuilder()
                .add("message", "A validation exception occurred")
                .add("reason", exception.getLocalizedMessage());
        jsonArray.add(errorObject);
        JsonObject errorJsonEntity = jsonObject.add("errors", jsonArray.build()).build();

        return Response.status(Response.Status.BAD_REQUEST).entity(errorJsonEntity).build();
    }
}

The ConstraintViolationExceptionMapper injects both the ResourceInfo and UriInfo components with the @Context annotation. The toResponse method gets the matched resource method, then gets an array of declared annotations with the getDeclaredAnnotations method. This array is then streamed and filtered for an annotation that matches one of any in the pre-declared list of annotations in the httpMethods list. Using this construct, rather than, for instance relying on the index of the methods in the array makes our code portable across vendors. It also ensures it works the same for future releases of the specification. 

The rest of the method creates JSON objects to return meaningful messages to the client. A typical call to a resource method in the application that triggers a ConstraintViolationException will resource in a JSON message such as shown below.

{
  "host": "localhost",
  "resource": "/jee-mongo/api/employee/HR-05",
  "resourceMethod": "POST",
  "title": "Validation Errors",
  "errors": [
    {
      "message": "A validation exception occurred",
      "reason": "No department found for department code HR-05"
    }
  ]
}

Notice the resourceMethod element. This is what we got from using the ResourceInfo object. Of course there are different scenarios where you will need to know the HTTP method, as stated above. But in all cases, the ResourceInfo is your go to object for getting such information. A small caveat, with the release of Jakarta EE 10, is that the @Context annotation will be deprecated and eventually removed in favor of the more ubiquitous Jakarta CDI @Inject. This is part of the long term goal of aligning the platform and making the CDI the core "glue" that binds all the various parts together. But until then, you can use the @Context annotation to get an instance of the ResourceInfo component in your Jakarta REST artefacts. 

Found this useful?

Try some of our Jakarta EE guides:

Comments