From f9a98b47557c2d71e9dddb74315d662614708fed Mon Sep 17 00:00:00 2001 From: alindo Date: Tue, 19 Nov 2024 17:47:20 +0000 Subject: [PATCH] wip --- .../common/api/exceptions/ApiException.java | 36 ++++++++-- .../RestResponseEntityExceptionHandler.java | 67 ++++++++++++++++++ .../model/ErrorResponseMessage.java | 70 +++++++++++++++++++ 3 files changed, 166 insertions(+), 7 deletions(-) create mode 100644 src/main/java/com/databasepreservation/common/api/exceptions/RestResponseEntityExceptionHandler.java create mode 100644 src/main/java/com/databasepreservation/common/api/exceptions/model/ErrorResponseMessage.java diff --git a/src/main/java/com/databasepreservation/common/api/exceptions/ApiException.java b/src/main/java/com/databasepreservation/common/api/exceptions/ApiException.java index 49b3dccb..9eab4e58 100644 --- a/src/main/java/com/databasepreservation/common/api/exceptions/ApiException.java +++ b/src/main/java/com/databasepreservation/common/api/exceptions/ApiException.java @@ -7,27 +7,49 @@ */ package com.databasepreservation.common.api.exceptions; - import com.databasepreservation.common.exceptions.ViewerException; +import java.io.Serial; + /** * @author Bruno Ferreira */ -public class ApiException extends ViewerException { + +public class ApiException extends RuntimeException { + @Serial private static final long serialVersionUID = 4667937307148805083L; public static final int INVALID_PARAMETER_VALUE = 1; public static final int EMPTY_PARAMETER = 2; public static final int RESOURCE_ALREADY_EXISTS = 3; + private Throwable cause; private int code; - public ApiException(int code, String msg) { - super(msg); - this.code = code; + public ApiException() { } - public int getCode() { - return code; + public ApiException(Throwable cause) { + super(cause); } + + private static String getCauseMessage(Throwable e) { + StringBuilder message = new StringBuilder(); + Throwable cause = e; + + while (cause != null) { + message.append(" caused by ").append(cause.getClass().getSimpleName()).append(": "); + if (cause.getMessage() != null) { + message.append(cause.getMessage()); + } + cause = cause.getCause(); + } + return message.toString(); + } + + @Override + public synchronized Throwable getCause() { + return cause; + } + } diff --git a/src/main/java/com/databasepreservation/common/api/exceptions/RestResponseEntityExceptionHandler.java b/src/main/java/com/databasepreservation/common/api/exceptions/RestResponseEntityExceptionHandler.java new file mode 100644 index 00000000..b7dc8728 --- /dev/null +++ b/src/main/java/com/databasepreservation/common/api/exceptions/RestResponseEntityExceptionHandler.java @@ -0,0 +1,67 @@ +package com.databasepreservation.common.api.exceptions; + +import java.util.UUID; + +import com.databasepreservation.common.api.exceptions.model.ErrorResponseMessage; +import org.roda.core.data.exceptions.AlreadyExistsException; +import org.roda.core.data.exceptions.AuthenticationDeniedException; +import org.roda.core.data.exceptions.AuthorizationDeniedException; +import org.roda.core.data.exceptions.GenericException; +import org.roda.core.data.exceptions.NotFoundException; +import org.roda.core.data.exceptions.RequestNotValidException; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.context.request.ServletWebRequest; +import org.springframework.web.context.request.WebRequest; +import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; + + +@ControllerAdvice +public class RestResponseEntityExceptionHandler extends ResponseEntityExceptionHandler { + + @ExceptionHandler(value = {ApiException.class}) + protected ResponseEntity handleRestException(RuntimeException ex, WebRequest request) { + String message = "Internal server error"; + String details = ""; + Object objectDetails = null; + HttpStatus httpStatus = HttpStatus.INTERNAL_SERVER_ERROR; + UUID errorID = UUID.randomUUID(); + if (ex.getCause() instanceof AuthorizationDeniedException) { + message = "Forbidden"; + details = ex.getCause().getMessage(); + httpStatus = HttpStatus.FORBIDDEN; + } else if (ex.getCause() instanceof AuthenticationDeniedException) { + message = "Unauthorized access"; + details = ex.getCause().getMessage(); + httpStatus = HttpStatus.UNAUTHORIZED; + } else if (ex.getCause() instanceof NotFoundException) { + message = "Resource not found"; + details = ex.getCause().getMessage(); + httpStatus = HttpStatus.NOT_FOUND; + } else if (ex.getCause() instanceof AlreadyExistsException) { + message = "Resource already exists"; + details = ex.getCause().getMessage(); + httpStatus = HttpStatus.CONFLICT; + } else if (ex.getCause() instanceof GenericException || ex.getCause() instanceof RequestNotValidException) { + message = "Request was not valid"; + details = ex.getCause().getMessage(); + httpStatus = HttpStatus.BAD_REQUEST; + } + + String warn = "ERROR_ID: " + errorID + " - " + ex.getClass().getSimpleName() + ": " + ex.getMessage(); + LoggerFactory.getLogger(RestResponseEntityExceptionHandler.class).warn(warn); + + ErrorResponseMessage body = new ErrorResponseMessage(httpStatus.value(), errorID.toString(), message, details, + ((ServletWebRequest) request).getRequest().getRequestURI(), objectDetails); + + HttpHeaders responseHeaders = new HttpHeaders(); + responseHeaders.setContentType(MediaType.APPLICATION_JSON); + + return handleExceptionInternal(ex, body, responseHeaders, httpStatus, request); + } +} diff --git a/src/main/java/com/databasepreservation/common/api/exceptions/model/ErrorResponseMessage.java b/src/main/java/com/databasepreservation/common/api/exceptions/model/ErrorResponseMessage.java new file mode 100644 index 00000000..a95190b3 --- /dev/null +++ b/src/main/java/com/databasepreservation/common/api/exceptions/model/ErrorResponseMessage.java @@ -0,0 +1,70 @@ +package com.databasepreservation.common.api.exceptions.model; + +import java.io.Serial; +import java.io.Serializable; +import java.time.Instant; +import java.time.temporal.ChronoUnit; + +import com.fasterxml.jackson.annotation.JsonInclude; + +public class ErrorResponseMessage implements Serializable { + + @Serial + private static final long serialVersionUID = -2206131216992713872L; + + private final int status; + private final String errorId; + private final String message; + private final String details; + private final Instant timestamp; + private final String instance; + @JsonInclude(JsonInclude.Include.NON_NULL) + private Object objectDetails; + + public ErrorResponseMessage(int status, String errorId, String message, String details, String instance) { + this.status = status; + this.errorId = errorId; + this.message = message; + this.details = details; + this.timestamp = Instant.now().truncatedTo(ChronoUnit.MILLIS); + this.instance = instance; + } + + public ErrorResponseMessage(int status, String errorId, String message, String details, String instance, Object objectDetails) { + this.status = status; + this.errorId = errorId; + this.message = message; + this.details = details; + this.timestamp = Instant.now().truncatedTo(ChronoUnit.MILLIS); + this.instance = instance; + this.objectDetails = objectDetails; + } + + public int getStatus() { + return status; + } + + public String getErrorId() { + return errorId; + } + + public String getMessage() { + return message; + } + + public String getDetails() { + return details; + } + + public Instant getTimestamp() { + return timestamp; + } + + public String getInstance() { + return instance; + } + + public Object getObjectDetails() { + return objectDetails; + } +}