Blog

- December 21, 2015

Restlet Framework provides several approaches to handle exceptions on both client and server sides. You can choose to use the Restlet Framework API itself or use a higher level approach based on custom exceptions and / or annotated exceptions.

On the server side

Restlet Framework provides several approaches to handle exceptions on the server side. They correspond to different needs and allow you to add flexibility when generating the corresponding response.

Basic approach for exceptions

Restlet Framework allows you to throw two kinds of exceptions within the annotated methods of server resources:

  • The ResourceException exception itself corresponds to any exception that can occur within a server resource.
  • User-defined exceptions (both checked and unchecked). In the case of checked exceptions, we need to add a throws clause in the method signatures. The Restlet Framework engine will then catch them and wrap them within a ResourceException one.

That said, Restlet Framework provides an indirection level to deduce the response status code and content when an exception is thrown within a server resource. As a matter of fact, the ResourceException exception is generic and mandates to specify a status code when creating it. A good approach is to implement user-defined exceptions describing the potential errors.

Then comes the status service of Restlet Framework. The element is responsible for creating the response content when an exception is thrown. The default implementation returns the description that comes along with a particular status code as text.

Restlet Framework allows the service developer to override it and provides its own implementation. Registering a custom status service can simply be done within the Restlet Framework application class using the setStatusService() method, as described below:

public class MyApplication extends Application {
    (...)

    public MyApplication() {
        (...)
        setStatusService(new MyCustomStatusService());
    }
}

A custom status service needs to extend the StatusService class and override the following methods:

  • toStatus() method to determine the response status code from the thrown exception
  • toRepresentation() method to build the corresponding representation content

The code below describes the skeleton of a custom status service:

public class MyCustomStatusService extends StatusService {
    @Override
    public Representation toRepresentation(Status status, Request request,
                               Response response) {
        Throwable t = status.getThrowable();
        (...)
        Representation result = (...)
        return result;
    }

    @Override
    public Status toStatus(Throwable t, Request request,
                               Response response) {
        Status status = (...)
        return status;
    }
}

Notice that overriding the toStatus() method isn’t required anymore since the status code associated with a user-defined exception can be defined in the exception class itself with an annotation.

Within the toStatus() method, we simply need to check which exception was thrown and return the corresponding status code. The code below describes an implementation of this:

@Override
public Status toStatus(Throwable t, Request request,
                           Response response) {
    if (t instanceof MyValidationException) {
        return Status.CLIENT_ERROR_UNPROCESSABLE_ENTITY;
    } else if ((...)) {
        (...)
    }
    return Status.CLIENT_ERROR_BAD_REQUEST;
}

Implementing the toRepresentation() method can be a bit more complex. In fact, we generally need to check if the client needs to receive back a structured representation or a user-friendly message (in HTML, for example).

So we need first to implement a utility method that checks this:

private boolean isHtmlContentRequested(Request request) {
    // Get accept media types for the client
    ClientInfo clientInfo = request.getClientInfo();
    List<Preference> mediaTypes
                        = clientInfo.getAcceptedMediaTypes();
    for (Preference mediaType : mediaTypes) {
        // Check if the media type is HTML
        if (MediaType.TEXT_HTML.equals(mediaType.getMetadata())) {
            return true;
        }
    }
    return false;
}

Based on this method, the skeleton of the toRepresentation() method will be the following:

@Override
public Representation toRepresentation(
                 Status status, Request request, Response response) {
    // According to the preferred media type
    if (isHtmlContentRequested(request)) {
        // return HTML representation
        return toHtmlRepresentation(status, request, response);
    } else {
        // return structured representation of the error
        return toStructuredRepresentation(status, request, response);
    }
}

We don’t provide the content of the toHtmlRepresentation() method since there are different ways to implement it (based on FileRepresentation class or template engines, and so on). We will focus now on the implementation of the toStructuredRepresentation() method that aims at defining a data representation for errors.

This method will create a bean from the exception content and then leverage the converter service to convert this bean into a representation. The snippet below describes a sample implementation:

private Representation toStructuredRepresentation(
                   Status status, Request request, Response response) {
    Representation result = null;

    Throwable ex = status.getThrowable();
    Object o = getBeanFromException(ex);

    if (o != null) {
        List variants
                    = ConverterUtils.getVariants(o.getClass(), null);
        if (!variants.contains(VARIANT_HTML)) {
            variants.add(VARIANT_HTML);
        }
        Variant variant = getConnegService().getPreferredVariant(
                                     variants, request, getMetadataService());
        try {
            result = getConverterService().toRepresentation(o, variant);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return result;
    }

    return super.toRepresentation(status, request, response);
}

Restlet Framework also provides a way to manage error representations in a more flexible way without having to provide a custom status service.

Using annotated exceptions

Restlet Framework supports the concept of annotated exceptions along with its classical annotated interfaces. This feature lets us serialize such user-defined exceptions as beans within the returned response for a particular error.

For example, if you want to map a request that returns a contact into a Contact bean, you can use something like that:

public interface MyService {
    @Post
    Contact addContact(Contact contact);
}

You can use custom exceptions in this context and define them in the throws clause of methods, as described below:

public interface ContactResource {
    @Post
    Contact addContact(Contact contact) throws MyValidationException;
}

This exception can use the @Status annotation:

@Status(value = 400, serialize = true)
public class MyValidationException extends RuntimeException {
    public ServiceValidationException(String message, Exception e) {
        super(message, e);
    }
}

This exception can be thrown on the server side and serialized in the response as a bean (using its fields) thanks to the converter feature of Restlet Framework. Note that we can add user fields to the custom exception.

For example, here, we could have a JSON content like this:

{
    "message": "my validation message"
}

Now let’s tackle how to handle exceptions on the client side with Restlet Framework.

On the client side

Like for the server side, we can use these two approaches to handle exceptions and errors on the client side.

Basic approach

By default, Restlet Framework throws a ResourceException when a status code other than 2xx is received in a response. We need to catch this exception to detect that an error occurs.

We don’t have access to the representation returned by the call in such a case, although it’s present in the response data and accessible using the getResponseEntity() method of the ClientResource class.

The snippet below describes this:

ClientResource clientResource = (...)
JSONObject jsonObj = (...)
try {
    Representation representation = clientResource.post(
                                    new JsonRepresentation(jsonObj));
    (...)
} catch (ResourceException ex) {
    Representation responseRepresentation
                           = clientResource.getResponseEntity();
    JsonRepresentation jsonRepr
                   = new JsonRepresentation(responseRepresentation);
    JSONObject errors = jsonRepr.getJsonObject();
    (...)
}

Note that this approach can also be used along with annotated interfaces.

Catching annotated exceptions

On the client side, when the response is received, this custom exception will be thrown instead of a simple ResourceException and the content of the response (our JSON content for example) deserialized within the fields of the exception.

The following code describes how to use this approach on the client side along with the annotated interfaces:

ClientResource cr = new ClientResource("http://...);
ContactResource contactResource = cr.wrap(ContactResource.class);
try {
    Contact newContact = new Contact();
    newContact.setName("my name");
    Contact addedContact = contactResource.addContact(newContact);
} catch(MyValidationException ex) {
    String errorMessage = ex.getMessage();
    (...)
}

In your context, the code of the previous MyValidationException exception described above is reused.

Conclusion

As you can see, Restlet Framework provides very convenient and declarative ways to implement and handle exceptions within REST applications on both client and server sides. This allows you to use a better design for your applications and modularize such processing.