GxRest/GxJRS/Responses
Contents
What are Code Exceptions
Code Exceptions model an approach to simplify and uniform the handling of exceptions (in the Java sense) within RESTful or REST-like services. Instead of creating separate classes for each exception type, the idea behind CodeExceptions is to use a single, system-wide exception class. And make it extend WebApplicationException
(from javax.ws.rs
) that in turn extends RuntimeException
.
Major advantages of CodeException are:
- abstract over the response returned by a REST resource method
- provide a single (and self-documented) point where to declare error codes and messages returned by the webapp
- reduce the class count in a project (not to mention in a system) avoiding the proliferation of custom Exception classes
- simplify the method declaration (RuntimeExceptions do not need to be declared)
- simplify the client code that manages only the error codes it is capable to handle
- remove the need to declare exceptions that sometimes aren’t going to be handled anyway.
Distribution
CodeExceptions
are available as part of the gCube eXtensions to the Rest Protocol library (to be released).
Sample Usage
The following code snippets are extracted from the new Resource Manager service.
Declare your Error Codes
The first step is to declare an enumeration of the error codes and associated messages. The enum must extend the ErrorCode
interface.
import org.gcube.core.gxrest.exceptions.ErrorCode; public enum RMCode implements ErrorCode { INVALID_METHOD__REQUEST(0, "The request is invalid."), MISSING_PARAMETER(1,"Required query parameter is missing."), MISSING_HEADER(2, "Required header is missing."), CONTEXT_ALREADY_EXIST(3, "Context already exists at the same level of the hierarchy."), CONTEXT_PARENT_DOES_NOT_EXIST(4, "Failed to validate the request. The request was not submitted to the Resource Registry."), INVALID_REQUEST_FOR_RR(5, "Failed to validate the request. The request was not submitted to the Resource Registry."), GENERIC_ERROR_FROM_RR(6, "The Resource Registry returned an error."); private int id; private String msg; private RMCode(int id, String msg) { this.id = id; this.msg = msg; } public int getId() { return this.id; } public String getMessage() { return this.msg; } }
Thrown CodeExceptions
The following method tries to create a Context given certain parameters (not shown).
import javax.ws.rs.core.Response; import org.gcube.resourcemanagement.manager.io.rs.RMCode; import org.gcube.core.gxrest.exceptions.WebCodeException; @Path("context") public class RMContext { @POST public Response create(...) { //An error condition occurs throw new WebCodeException(RMCode.CONTEXT_ALREADY_EXIST)); //Another error condition occurs throw new WebCodeException(RMCode.CONTEXT_PARENT_DOES_NOT_EXIST)); } }
Do note that WebCodeExceptions can be thrown by any object invoked withing the create method without the need of declaring them (this is because they are RuntimeExceptions too).
Receive CodeExceptions
When a client invokes a resource method that throws CodeException, it can access to the returned code(s) and message(s) as follows:
import org.gcube.core.gxrest.exceptions.SerializableErrorCode; //... Response create = target("context").queryParam(...).request() .post(Entity.entity(ISMapper.marshal(newContext), MediaType.APPLICATION_JSON + ";charset=UTF-8")); SerializableErrorCode code = create.readEntity(SerializableErrorCode.class);
The code above makes usage of the Jersey testing framework to invoke the create method.
As enums are not POJOs, they cannot be correctly serialized in the Response. Because of that, the enum value is converted and returned as a SerializableErrorCode
instance.
Access to Error Codes
Once the client fetched the SerializableErrorCode instance, there are two ways to handle the error codes:
- Use the SerializableErrorCode methods:
System.err.println(String.format("The service says %d, %s",code.getId(), code.getMessage())); switch (code.getId()) { case 1: //manage the error break; case 2: //manage the error break; default: //can't manage the other cases break; }
- Convert the SerializableErrorCode into the original enum value:
import org.gcube.core.gxrest.exceptions.CodeFinder; SerializableErrorCode code = create.readEntity(SerializableErrorCode.class); RMCode realCode = CodeFinder.findAndConvert(code, RMCode.values()); switch (realCode) { case CONTEXT_ALREADY_EXIST: //manage the error break; case CONTEXT_PARENT_DOES_NOT_EXIST: //manage the error break; default: //can't manage the other cases break; }
CodeFinder
is an utility provided by the CodeException package. This case is possible only if the enum with the ErrorCodes is available in a components shared between the Service and the client.