Skip to content
Snippets Groups Projects
Commit 65785ed2 authored by Lars Johansson's avatar Lars Johansson
Browse files

Added global exception handler

parent 6cf5092a
No related branches found
No related tags found
No related merge requests found
/*
* Copyright (C) 2022 European Spallation Source ERIC.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package org.openepics.names.rest.controller;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.commons.lang3.StringUtils;
import org.openepics.names.util.ExceptionUtil;
import org.openepics.names.util.ServiceHttpStatusException;
import org.openepics.names.util.response.Response;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
/**
* Global exception handler to ensure exceptions are communicated to request origin in a uniform way.
*
* @author Lars Johansson
*/
@RestControllerAdvice
public class GlobalControllerExceptionHandler extends ResponseEntityExceptionHandler {
// note
// no logging here
private static final Logger LOGGER = Logger.getLogger(GlobalControllerExceptionHandler.class.getName());
@ExceptionHandler
protected ResponseEntity<Response> handleConflict(RuntimeException ex, WebRequest request) {
LOGGER.log(Level.INFO, "handleConflict, ex.getMessage: " + ex.getMessage());
Response response = new Response("", "");
response.setMessage(ExceptionUtil.OPERATION_COULD_NOT_BE_PERFORMED);
HttpStatus resultStatus = HttpStatus.INTERNAL_SERVER_ERROR;
if (ex instanceof ServiceHttpStatusException) {
response.setMessage(StringUtils.trimToEmpty(ex.getMessage()));
response.setDetails(StringUtils.trimToEmpty(((ServiceHttpStatusException) ex).getDetails()));
resultStatus = ((ServiceHttpStatusException) ex).getHttpStatus();
}
return new ResponseEntity<>(response, Response.HEADER_JSON, resultStatus);
}
}
/*
* Copyright (C) 2021 European Spallation Source ERIC.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package org.openepics.names.util;
import org.springframework.http.HttpStatus;
import org.springframework.web.server.ResponseStatusException;
/**
* Utility class to assist in handling of exceptions.
* <br/><br/>
* Note
* <ul>
* <li>400 {@link HttpStatus#BAD_REQUEST}</li>
* <li>401 {@link HttpStatus#UNAUTHORIZED}</li>
* <li>403 {@link HttpStatus#FORBIDDEN}</li>
* <li>404 {@link HttpStatus#NOT_FOUND}</li>
* <li>409 {@link HttpStatus#CONFLICT}</li>
* <li>500 {@link HttpStatus#INTERNAL_SERVER_ERROR}</li>
* <li>501 {@link HttpStatus#NOT_IMPLEMENTED}</li>
* </ul>
*
* @author Lars Johansson
*/
public class ExceptionUtil {
public static final String ONE_OR_MORE_ELEMENTS_ARE_NOT_CORRECT = "One or more elements are not correct.";
public static final String OPERATION_COULD_NOT_BE_PERFORMED = "Operation could not be performed.";
/**
* This class is not to be instantiated.
*/
private ExceptionUtil() {
throw new IllegalStateException("Utility class");
}
/**
* Convert service http status exception to response status exception.
*
* @param e service http status exception
* @return response status exception
*/
public static ResponseStatusException convertException(ServiceHttpStatusException e) {
switch (e.getHttpStatus()) {
case BAD_REQUEST:
throw ExceptionUtil.createResponseStatusExceptionBadRequest();
case UNAUTHORIZED:
throw ExceptionUtil.createResponseStatusExceptionUnauthorized();
case FORBIDDEN:
throw ExceptionUtil.createResponseStatusExceptionForbidden();
case NOT_FOUND:
throw ExceptionUtil.createResponseStatusExceptionNotFound();
case CONFLICT:
throw ExceptionUtil.createResponseStatusExceptionConflict();
case INTERNAL_SERVER_ERROR:
throw ExceptionUtil.createResponseStatusExceptionInternalServerError();
case NOT_IMPLEMENTED:
throw ExceptionUtil.createResponseStatusExceptionNotImplemented();
default:
throw ExceptionUtil.createResponseStatusExceptionInternalServerError();
}
}
/**
* Create response status exception.
* Intended for communication from server to client.
*
* @param status http status
* @param reason reason
* @param cause cause
* @return response status exception
*/
protected static ResponseStatusException createResponseStatusException(HttpStatus status, String reason, Throwable cause) {
return new ResponseStatusException(status, reason, cause);
}
/**
* Create response status exception for {@link HttpStatus#BAD_REQUEST}.
* Intended for communication from server to client.
*
* @return response status exception
*/
protected static ResponseStatusException createResponseStatusExceptionBadRequest() {
return createResponseStatusException(HttpStatus.BAD_REQUEST, OPERATION_COULD_NOT_BE_PERFORMED, null);
}
/**
* Create response status exception for {@link HttpStatus#UNAUTHORIZED}.
* Intended for communication from server to client.
*
* @return response status exception
*/
protected static ResponseStatusException createResponseStatusExceptionUnauthorized() {
return createResponseStatusException(HttpStatus.UNAUTHORIZED, OPERATION_COULD_NOT_BE_PERFORMED, null);
}
/**
* Create response status exception for {@link HttpStatus#FORBIDDEN}.
* Intended for communication from server to client.
*
* @return response status exception
*/
protected static ResponseStatusException createResponseStatusExceptionForbidden() {
return createResponseStatusException(HttpStatus.FORBIDDEN, OPERATION_COULD_NOT_BE_PERFORMED, null);
}
/**
* Create response status exception for {@link HttpStatus#NOT_FOUND}.
* Intended for communication from server to client.
*
* @return response status exception
*/
protected static ResponseStatusException createResponseStatusExceptionNotFound() {
return createResponseStatusException(HttpStatus.NOT_FOUND, OPERATION_COULD_NOT_BE_PERFORMED, null);
}
/**
* Create response status exception for {@link HttpStatus#CONFLICT}.
* Intended for communication from server to client.
*
* @return response status exception
*/
protected static ResponseStatusException createResponseStatusExceptionConflict() {
return createResponseStatusException(HttpStatus.CONFLICT, OPERATION_COULD_NOT_BE_PERFORMED, null);
}
/**
* Create response status exception for {@link HttpStatus#INTERNAL_SERVER_ERROR}.
* Intended for communication from server to client.
*
* @return response status exception
*/
public static ResponseStatusException createResponseStatusExceptionInternalServerError() {
return createResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, OPERATION_COULD_NOT_BE_PERFORMED, null);
}
/**
* Create response status exception for {@link HttpStatus#NOT_IMPLEMENTED}.
* Intended for communication from server to client.
*
* @return response status exception
*/
public static ResponseStatusException createResponseStatusExceptionNotImplemented() {
return createResponseStatusException(HttpStatus.NOT_IMPLEMENTED, OPERATION_COULD_NOT_BE_PERFORMED, null);
}
/**
* Create service http status exception.
* Intended for communication inside server.
*
* @param status http status
* @param message message
* @param details details
* @param userFriendly user friendly
* @param cause cause
* @return service http status exception
*/
protected static ServiceHttpStatusException createServiceHttpStatusException(HttpStatus status, String message, String details, String userFriendly, Throwable cause) {
return new ServiceHttpStatusException(status, message, details, userFriendly, cause);
}
/**
* Create service http status exception for {@link HttpStatus#BAD_REQUEST}.
* Intended for communication inside server.
*
* @param message message
* @return service http status exception
*/
public static ServiceHttpStatusException createServiceHttpStatusExceptionBadRequest(String message, String details, String userFriendly) {
return createServiceHttpStatusException(HttpStatus.BAD_REQUEST, message, details, userFriendly, null);
}
/**
* Create service http status exception for {@link HttpStatus#UNAUTHORIZED}.
* Intended for communication inside server.
*
* @param message message
* @return service http status exception
*/
public static ServiceHttpStatusException createServiceHttpStatusExceptionUnauthorized(String message, String details, String userFriendly) {
return createServiceHttpStatusException(HttpStatus.UNAUTHORIZED, message, details, userFriendly, null);
}
/**
* Create service http status exception for {@link HttpStatus#FORBIDDEN}.
* Intended for communication inside server.
*
* @param message message
* @return service http status exception
*/
public static ServiceHttpStatusException createServiceHttpStatusExceptionForbidden(String message, String details, String userFriendly) {
return createServiceHttpStatusException(HttpStatus.FORBIDDEN, message, details, userFriendly, null);
}
/**
* Create service http status exception for {@link HttpStatus#NOT_FOUND}.
* Intended for communication inside server.
*
* @param message message
* @return service http status exception
*/
public static ServiceHttpStatusException createServiceHttpStatusExceptionNotFound(String message, String details, String userFriendly) {
return createServiceHttpStatusException(HttpStatus.NOT_FOUND, message, details, userFriendly, null);
}
/**
* Create service http status exception for {@link HttpStatus#CONFLICT}.
* Intended for communication inside server.
*
* @param message message
* @return service http status exception
*/
public static ServiceHttpStatusException createServiceHttpStatusExceptionConflict(String message, String details, String userFriendly) {
return createServiceHttpStatusException(HttpStatus.CONFLICT, message, details, userFriendly, null);
}
/**
* Create service http status exception for {@link HttpStatus#INTERNAL_SERVER_ERROR}.
* Intended for communication inside server.
*
* @param message message
* @return service http status exception
*/
public static ServiceHttpStatusException createServiceHttpStatusExceptionInternalServerError(String message, String details, String userFriendly) {
return createServiceHttpStatusException(HttpStatus.INTERNAL_SERVER_ERROR, message, details, userFriendly, null);
}
/**
* Create service http status exception for {@link HttpStatus#NOT_IMPLEMENTED}.
* Intended for communication inside server.
*
* @param message message
* @return service http status exception
*/
public static ServiceHttpStatusException createServiceHttpStatusExceptionNotImplemented(String message, String details, String userFriendly) {
return createServiceHttpStatusException(HttpStatus.NOT_IMPLEMENTED, message, details, userFriendly, null);
}
}
/*
* Copyright (C) 2021 European Spallation Source ERIC.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package org.openepics.names.util;
import org.springframework.http.HttpStatus;
/**
* Exception class to assist in handling of service layer exceptions.
*
* @author Lars Johansson
*/
public class ServiceHttpStatusException extends RuntimeException {
/**
*
*/
private static final long serialVersionUID = 5346696855950320329L;
private final HttpStatus httpStatus;
private final String details;
private final String userFriendly;
/**
* Public constructor.
*
* @param httpStatus http status
* @param message message
* @param cause cause
*/
public ServiceHttpStatusException(HttpStatus httpStatus, String message, String details, String userFriendly, Throwable cause) {
super(message, cause);
this.httpStatus = httpStatus;
this.details = details;
this.userFriendly = userFriendly;
}
/**
* Return http status of exception.
*
* @return http status
*/
public HttpStatus getHttpStatus() {
return httpStatus;
}
/**
* Returns details of exception.
*
* @return details
*/
public String getDetails() {
return details;
}
/**
* Return user friendly of exception.
*
* @return user friendly
*/
public String getUserFriendly() {
return userFriendly;
}
}
/*
* Copyright (C) 2022 European Spallation Source ERIC.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package org.openepics.names.util.response;
import org.springframework.http.HttpHeaders;
/**
* This class is used to ensure response to request origin are handled in a uniform way.
*
* @author Lars Johansson
*/
public class Response {
private static final String CONTENT_TYPE = "Content-Type";
private static final String APP_JSON = "application/json";
public static final HttpHeaders HEADER_JSON = new HttpHeaders();
private String message = null;
private String details = null;
public Response() {
HEADER_JSON.add(Response.CONTENT_TYPE, Response.APP_JSON);
}
public Response(String message) {
this();
this.message = message;
this.details = "";
}
public Response(String message, String details) {
this();
this.message = message;
this.details = details;
}
public String getMessage() {
return this.message;
}
public void setMessage(String message) {
this.message = message;
}
public String getDetails() {
return this.details;
}
public void setDetails(String details) {
this.details = details;
}
}
/*
* Copyright (C) 2021 European Spallation Source ERIC.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package org.openepics.names.util;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import org.junit.jupiter.api.Test;
import org.springframework.http.HttpStatus;
import org.springframework.web.server.ResponseStatusException;
/**
* Unit tests for ExceptionUtil class.
*
* @author Lars Johansson
*
* @see ExceptionUtil
*/
public class ExceptionUtilTest {
/**
* Test of create response status exception.
*/
@Test
public void createResponseStatusException() {
ResponseStatusException exception = ExceptionUtil.createResponseStatusException(HttpStatus.I_AM_A_TEAPOT, null, null);
assertEquals(HttpStatus.I_AM_A_TEAPOT, exception.getStatus());
assertNull (exception.getReason());
assertNull (exception.getCause());
exception = ExceptionUtil.createResponseStatusExceptionBadRequest();
assertEquals(HttpStatus.BAD_REQUEST, exception.getStatus());
assertEquals(ExceptionUtil.OPERATION_COULD_NOT_BE_PERFORMED, exception.getReason());
assertEquals(null, exception.getCause());
exception = ExceptionUtil.createResponseStatusExceptionUnauthorized();
assertEquals(HttpStatus.UNAUTHORIZED, exception.getStatus());
assertEquals(ExceptionUtil.OPERATION_COULD_NOT_BE_PERFORMED, exception.getReason());
assertEquals(null, exception.getCause());
exception = ExceptionUtil.createResponseStatusExceptionForbidden();
assertEquals(HttpStatus.FORBIDDEN, exception.getStatus());
assertEquals(ExceptionUtil.OPERATION_COULD_NOT_BE_PERFORMED, exception.getReason());
assertEquals(null, exception.getCause());
exception = ExceptionUtil.createResponseStatusExceptionNotFound();
assertEquals(HttpStatus.NOT_FOUND, exception.getStatus());
assertEquals(ExceptionUtil.OPERATION_COULD_NOT_BE_PERFORMED, exception.getReason());
assertEquals(null, exception.getCause());
exception = ExceptionUtil.createResponseStatusExceptionConflict();
assertEquals(HttpStatus.CONFLICT, exception.getStatus());
assertEquals(ExceptionUtil.OPERATION_COULD_NOT_BE_PERFORMED, exception.getReason());
assertEquals(null, exception.getCause());
exception = ExceptionUtil.createResponseStatusExceptionInternalServerError();
assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, exception.getStatus());
assertEquals(ExceptionUtil.OPERATION_COULD_NOT_BE_PERFORMED, exception.getReason());
assertEquals(null, exception.getCause());
exception = ExceptionUtil.createResponseStatusExceptionNotImplemented();
assertEquals(HttpStatus.NOT_IMPLEMENTED, exception.getStatus());
assertEquals(ExceptionUtil.OPERATION_COULD_NOT_BE_PERFORMED, exception.getReason());
assertEquals(null, exception.getCause());
}
/**
* Test of create service http status exception.
*/
@Test
public void createServiceHttpStatusException() {
ServiceHttpStatusException exception = ExceptionUtil.createServiceHttpStatusException(null, null, null, null, null);
assertNull(exception.getHttpStatus());
assertNull(exception.getMessage());
assertNull(exception.getDetails());
assertNull(exception.getUserFriendly());
assertNull(exception.getCause());
exception = ExceptionUtil.createServiceHttpStatusExceptionBadRequest(null, null, null);
assertEquals(HttpStatus.BAD_REQUEST, exception.getHttpStatus());
assertEquals(null, exception.getMessage());
assertEquals(null, exception.getDetails());
assertEquals(null, exception.getUserFriendly());
assertEquals(null, exception.getCause());
exception = ExceptionUtil.createServiceHttpStatusExceptionUnauthorized(null, null, null);
assertEquals(HttpStatus.UNAUTHORIZED, exception.getHttpStatus());
assertEquals(null, exception.getMessage());
assertEquals(null, exception.getDetails());
assertEquals(null, exception.getUserFriendly());
assertEquals(null, exception.getCause());
exception = ExceptionUtil.createServiceHttpStatusExceptionForbidden(null, null, null);
assertEquals(HttpStatus.FORBIDDEN, exception.getHttpStatus());
assertEquals(null, exception.getMessage());
assertEquals(null, exception.getDetails());
assertEquals(null, exception.getUserFriendly());
assertEquals(null, exception.getCause());
exception = ExceptionUtil.createServiceHttpStatusExceptionNotFound(null, null, null);
assertEquals(HttpStatus.NOT_FOUND, exception.getHttpStatus());
assertEquals(null, exception.getMessage());
assertEquals(null, exception.getDetails());
assertEquals(null, exception.getUserFriendly());
assertEquals(null, exception.getCause());
exception = ExceptionUtil.createServiceHttpStatusExceptionConflict(null, null, null);
assertEquals(HttpStatus.CONFLICT, exception.getHttpStatus());
assertEquals(null, exception.getMessage());
assertEquals(null, exception.getDetails());
assertEquals(null, exception.getUserFriendly());
assertEquals(null, exception.getCause());
exception = ExceptionUtil.createServiceHttpStatusExceptionInternalServerError(null, null, null);
assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, exception.getHttpStatus());
assertEquals(null, exception.getMessage());
assertEquals(null, exception.getDetails());
assertEquals(null, exception.getUserFriendly());
assertEquals(null, exception.getCause());
exception = ExceptionUtil.createServiceHttpStatusExceptionNotImplemented(null, null, null);
assertEquals(HttpStatus.NOT_IMPLEMENTED, exception.getHttpStatus());
assertEquals(null, exception.getMessage());
assertEquals(null, exception.getDetails());
assertEquals(null, exception.getUserFriendly());
assertEquals(null, exception.getCause());
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment