All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.kiwiproject.jaxrs.exception.JaxrsExceptionMapper Maven / Gradle / Ivy

Go to download

Kiwi is a utility library. We really like Google's Guava, and also use Apache Commons. But if they don't have something we need, and we think it is useful, this is where we put it.

There is a newer version: 4.4.0
Show newest version
package org.kiwiproject.jaxrs.exception;

import static java.util.Collections.unmodifiableMap;
import static java.util.Objects.isNull;
import static java.util.stream.Collectors.toUnmodifiableList;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.kiwiproject.collect.KiwiMaps.isNotNullOrEmpty;

import lombok.extern.slf4j.Slf4j;
import org.kiwiproject.collect.KiwiMaps;
import org.kiwiproject.json.JsonHelper;

import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * Map a {@link JaxrsException} to a {@link Response}.
 * 

* The mapped response has the status of the given {@link JaxrsException} and media type JSON. */ @Slf4j @Provider public class JaxrsExceptionMapper implements ExceptionMapper { /** * The map key under which the list of {@link ErrorMessage} objects resides. */ public static final String KEY_ERRORS = "errors"; private static final JsonHelper JSON_HELPER = JsonHelper.newDropwizardJsonHelper(); /** * Convert the given {@link JaxrsException} to a response containing a JSON entity. * * @param exception the exception to convert * @return a response * @see #buildResponse(JaxrsException) */ @Override public Response toResponse(JaxrsException exception) { return buildResponse(exception); } /** * Convert the given {@link JaxrsException} to a JSON response. *

* The response entity contains the information in the JaxrsException. * * @param exception the exception to convert * @return a response * @see #buildResponseEntity(JaxrsException) */ public static Response buildResponse(JaxrsException exception) { return buildResponseBuilder(exception).build(); } /** * Convert the given {@link JaxrsException} to a JSON response builder. *

* The response entity contains the information in the JaxrsException. * * @param exception the exception to convert * @return a response builder * @see #buildResponseEntity(JaxrsException) */ public static Response.ResponseBuilder buildResponseBuilder(JaxrsException exception) { return Response.status(exception.getStatusCode()) .entity(buildResponseEntity(exception)) .type(MediaType.APPLICATION_JSON_TYPE); } /** * Convert the given {@link JaxrsException} to a map that can be used as a JSON response entity. *

* The response entity will contain an "errors" key containing the {@link ErrorMessage} objects. * If the exception contains any other data ({@link JaxrsException#getOtherData()}), those key/value pairs * are also included in the response entity. See the note in {@link JaxrsException#setOtherData(Map)} regarding * the behavior if {@code otherData} contains an "errors" key. * * @param exception the exception to convert * @return a map that can be used as a response entity * @see JaxrsException#getOtherData() * @see JaxrsException#setOtherData(Map) */ public static Map buildResponseEntity(JaxrsException exception) { var entity = new HashMap(); if (isNotNullOrEmpty(exception.getOtherData())) { entity.putAll(exception.getOtherData()); } entity.put(KEY_ERRORS, exception.getErrors()); return entity; } /** * Convert the given {@link Response} to a {@link JaxrsException}. *

* Attempts to convert a response entity into errors and other data in the resulting exception * using {@link #toJaxrsException(int, Map)}. * * @param response the response to convert * @return a new exception instance, or null if the response is null * @see #toJaxrsException(int, Map) */ public static JaxrsException toJaxrsException(Response response) { if (isNull(response)) { return null; } int status = response.getStatus(); if (!response.hasEntity()) { return new JaxrsException(response.getStatusInfo().getReasonPhrase(), status); } String entityText; try { entityText = response.readEntity(String.class); if (isBlank(entityText)) { return new JaxrsException(response.getStatusInfo().getReasonPhrase(), status); } } catch (Exception e) { LOG.warn("Error reading entity from response", e); return new JaxrsException((String) null, status); } Map entity; try { entity = JSON_HELPER.toMap(entityText); } catch (Exception e) { LOG.warn("Error converting response text to Map", e); return new JaxrsException(entityText, status); } return toJaxrsException(status, entity); } /** * Convert the given HTTP status code and an entity into a {@link JaxrsException}. *

* Looks for an entry with key "errors" and a list of objects, and attempts to convert them into * {@link ErrorMessage} objects. The conversion from a generic map to an {@link ErrorMessage} is * done using {@link ErrorMessage#valueOf(Map)}. *

* Any other entries in the map are converted to "other data" in the exception. * * @param status the HTTP status * @param entity the entity as a map * @return a new exception instance * @see JaxrsException#setErrors(List) * @see JaxrsException#setOtherData(Map) */ public static JaxrsException toJaxrsException(int status, Map entity) { if (KiwiMaps.isNullOrEmpty(entity) || !entity.containsKey(KEY_ERRORS)) { var ex = new JaxrsException((String) null, status); ex.setOtherData(entity); return ex; } try { var errorMessages = extractErrorMessages(entity); var ex = new JaxrsException(errorMessages, status); var otherData = new HashMap<>(entity); otherData.remove(KEY_ERRORS); otherData.remove(null); ex.setOtherData(unmodifiableMap(otherData)); return ex; } catch (Exception e) { LOG.warn("Error converting given entity map: {}", entity, e); return new JaxrsException((String) null, status); } } @SuppressWarnings("unchecked") private static List extractErrorMessages(Map entity) { if (entity.containsKey(KEY_ERRORS)) { var errorObjects = (List) entity.get(KEY_ERRORS); return errorObjects.stream() .map(JaxrsExceptionMapper::toErrorMessageOrNull) .filter(Objects::nonNull) .collect(toUnmodifiableList()); } return List.of(); } @SuppressWarnings("unchecked") private static ErrorMessage toErrorMessageOrNull(Object obj) { if (obj instanceof ErrorMessage) { return (ErrorMessage) obj; } else if (obj instanceof Map) { return ErrorMessage.valueOf((Map) obj); } return null; } }