3

Exception Serializer in Lagom

 2 years ago
source link: https://blog.knoldus.com/exception-serializer-in-lagom/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Exception Serializer in Lagom

Reading Time: 2 minutes

In this blog, I will show you how to handle error responses from a Lagom service and convert them into well-defined exceptions using exception serializers.

Suppose we have an unmanaged service in Lagom. The unmanaged service hits a target service and the success response is deserialized into a POJO corresponding to the service call. But in the case of an error response, we get deserialization exception since the response with details of the error cannot be mapped to that POJO.

Using Lagom’s ExceptionSerializer, we can map all the responses with status codes in the range of 400-500 to appropriate exceptions specific to the service with a well-defined message. It allows the client of the service to take a suitable action based on the exception generated.

Exception serializers convert exceptions to RawExceptionMessage. The raw exception message contains a status code, a message body, and a protocol descriptor which defines the content type of the message. It also allows an exception to be recreated from an error code and their serialized form.

This is how the exception serializer is defined:

xxxxxxxxxx
public interface ExternalService extends Service {
    @Override
    default Descriptor descriptor() {
        return Service.named("external-service")
                .withCalls(
                        Service.restCall(Method.GET, "/user", this::getUser)
                )
                .withExceptionSerializer(ExternalServiceExceptionSerializer.INSTANCE)
                .withAutoAcl(true);
    }
     ServiceCall getUser();
}

The ExceptionSerializer subclass needs to override the serialize and deserialize methods which define how the exceptions are serialized to RawExceptionMessage and deserialized to Throwable. Following example shows how it can be done.

xxxxxxxxxx
public class ExternalServiceExceptionSerializer implements ExceptionSerializer {
    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
    public static final ExternalServiceExceptionSerializer INSTANCE = new ExternalServiceExceptionSerializer();
    @Override
    public RawExceptionMessage serialize(Throwable exception, Collection accept) {
        return null;
    }
    @Override
    public Throwable deserialize(RawExceptionMessage message) {
        return ExceptionFactory.getInstance(mapExceptionToFault(message));
    }
    Fault mapExceptionToFault(RawExceptionMessage message) {
        Fault fault;
        try {
            String errorJson = message.messageAsText();
            fault = OBJECT_MAPPER.readValue(errorJson, Fault.class);
        } catch (IOException ex) {
            fault = Fault.builder()
                    .errorMessage("No payload available")
                    .build();
        }
        return fault;
    }
}

The above class takes the serialized exception message(RawExceptionMessage) and maps it to a Fault POJO containing the error string.

xxxxxxxxxx
public class Fault {
    @JsonProperty("message")
    String errorMessage;
}

The Fault POJO is provided to an exception factory class which determines the exception to throw based on the contents of the error message.

xxxxxxxxxx
public class ExceptionFactory {
    public static Throwable getInstance(Fault fault) {
        if (fault == null)
            return null;
        return determineException(fault);
    }
    private static Throwable determineException(Fault fault) {
        String message = fault.getErrorMessage();
        if (StringUtils.isNotEmpty(message) && message.contains("Requires authentication")) {
            return new AuthenticationException(fault);
        }
        return new GenericException(fault);
    }
    public static class AuthenticationException extends RuntimeException {
        Fault fault;
        public AuthenticationException(Fault fault) {
            this.fault = fault;
        }
    }
    public static class GenericException extends RuntimeException {
        Fault fault;
        public GenericException(Fault fault) {
            this.fault = fault;
        }
    }
}

That is it. We have now successfully implemented exception serializer in our service. Now the service will always throw the custom exceptions specified in the exception factory whenever the backend service gives the error response.

Hope you enjoyed reading. The complete code is available here.

References :

https://www.lagomframework.com/documentation/1.3.x/java/ServiceErrorHandling.html

knoldus-advt-sticker1


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK