I have made some subclass for exception handling. The method throws a subclass exception. I have made #ExceptionHandler for both super class as well as subclasses. But only if there is no handling for super class of exception (handleSuperException(SuperclassExceptionexception e)), then the subclass exceptions are handled. SubClassAException, SubClassBException, SubClassCException extends SuperclassExceptionexception.
public class Controller #PostMapping("/path/") {
public ResponseEntity<String> method() throws SuperclassException{
}
#ExceptionHandler(SuperclassException.class)
public ResponseEntity handleSuperException(SuperclassExceptionexception e) {
//hadle superclass related
}
#ExceptionHandler({SubClassAException.class, SubClassBException.class, SubClassCException.class})
public ResponseEntity handleSubClassException(SuperclassExceptionexception e) {
//handle more specific
}
But it never goes to handleSubClassException even if subclass exceptions are thrown.
Unable to reproduce!
Here is Minimal, Reproducible Example, tested with Spring Boot 2.2.0 (Spring 5.2.0).
package web.controller;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.GetMapping;
#Controller
public class FailController {
#GetMapping("/dmz/fail")
public String failSuper() {
throw new SuperclassException("failSuper()");
}
#GetMapping("/dmz/failA")
public String failA() {
throw new SubClassAException("failA()");
}
#GetMapping("/dmz/failB")
public String failB() {
throw new SubClassBException("failB()");
}
#ExceptionHandler(SuperclassException.class)
public ResponseEntity<?> handleSuperException(SuperclassException e) {
return ResponseEntity.badRequest().body("handleSuperException: " + e);
}
#ExceptionHandler({SubClassAException.class, SubClassBException.class})
public ResponseEntity<?> handleSubClassException(SuperclassException e) {
return ResponseEntity.badRequest().body("handleSubClassException: " + e);
}
}
class SuperclassException extends RuntimeException {
public SuperclassException(String message) {
super(message);
}
}
class SubClassAException extends SuperclassException {
public SubClassAException(String message) {
super(message);
}
}
class SubClassBException extends SuperclassException {
public SubClassBException(String message) {
super(message);
}
}
I'm using /dmz/ so the Spring Security setup won't require login. In a plain vanilla Spring Boot setup, that will of course not be needed.
Output (http://localhost:8080/dmz/fail)
handleSuperException: web.controller.SuperclassException: failSuper()
Output (http://localhost:8080/dmz/failA)
handleSubClassException: web.controller.SubClassAException: failA()
Output (http://localhost:8080/dmz/failB)
handleSubClassException: web.controller.SubClassBException: failB()
Related
I use java spring.
I try to use ControllerAdvice in my project as a global event handler:
#ControllerAdvice
public class ExceptionsHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(RestClientException.class)
public void RestClientException(RestClientException ex)
{
System.out.println("RestClientException" + ex.getMessage());
}
#ExceptionHandler
public void Exception(Exception ex)
{
System.out.println("Exception" + ex.getMessage());
}
}
In this function I create and throw an exception:
#Component
#RequiredArgsConstructor
public class Scheduler {
public void schedule() throws JsonProcessingException, Exception {
throw new Exception();
}
}
And:
#Component
#RequiredArgsConstructor
public class RestfulReaderServiceImpl implements RestfulReaderService {
public String getData(String url) throws RestClientException {
throw new RestClientException();
}
}
But when those functions are fired and exceptions are thrown the exception handlers
in class ExceptionsHandler does not execute.
Any idea why exception handlers in ExceptionsHandler class not catches and handles the
exceptions?
#ControllerAdvice only handles exceptions thrown from Controller methods i.e. methods annotated with #RequestMapping or any of its shortcut annotations (#GetMapping etc.). You can check this by calling your exception throwing method from any #RequestMapping annotated method.
I've tried registering custom exception mappers in multiple ways:
#Provider
public class ConstraintViolationMapper implements ExceptionMapper<ConstraintViolationException> {
#Override
public Response toResponse(ConstraintViolationException exception) {
...
}
}
#Provider
public class ConstraintViolationMapper implements ExceptionMapper<ResteasyViolationException> {
#Override
public Response toResponse(ResteasyViolationException exception) {
...
}
}
#Provider
public class ConstraintViolationMapper implements ExceptionMapper<ValidationException> {
#Override
public Response toResponse(ValidationException exception) {
...
}
}
But all that happens is the default behaviour by the ResteasyViolationExceptionMapper. My custom ExceptionMapper is never called. I don't know what else to try.
I don't know why it didn't work when I first tried it, but this works
#Provider
public class ConstraintViolationMapper implements ExceptionMapper<ValidationException> {
#Override
public Response toResponse(ValidationException exception) {
...
}
}
Have a custom error controller on Spring boot:
package com.example.controllers;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.boot.web.servlet.error.ErrorController;
import javax.servlet.http.HttpServletRequest;
#Controller
public class CustomErrorController implements ErrorController
{
#RequestMapping("/error")
public String handleError(HttpServletRequest request)
{
...
}
#Override
public String getErrorPath()
{
return "/error";
}
}
But, when compile says: getErrorPath() in ErrorController has been deprecated. Ok, i found information: use server.error.path property. Ok, add this in application.properties and delete the function, but now says: CustomErrorController is not abstract and does not override abstract method getErrorPath() in ErrorController, ¿need a deprecated function?.
How to made the custom error controller?, the ErrorController requires getErrorPath but it is deprecated, what is the correct alternative?.
Starting version 2.3.x, Spring boot has deprecated this method. Just return null as it is anyway going to be ignored. Do not use #Override annotation if you want to prevent future compilation error when the method is totally removed. You can also suppress the deprecation warning if you want, however, the warning (also the #Override annotation) is helpful to remind you to cleanup/fix your code when the method is removed.
#Controller
#RequestMapping("/error")
#SuppressWarnings("deprecation")
public class CustomErrorController implements ErrorController {
public String error() {
// handle error
// ..
}
public String getErrorPath() {
return null;
}
}
#Controller
public class CustomErrorController implements ErrorController {
#RequestMapping("/error")
public ModelAndView handleError(HttpServletResponse response) {
int status = response.getStatus();
if ( status == HttpStatus.NOT_FOUND.value()) {
System.out.println("Error with code " + status + " Happened!");
return new ModelAndView("error-404");
} else if (status == HttpStatus.INTERNAL_SERVER_ERROR.value()) {
System.out.println("Error with code " + status + " Happened!");
return new ModelAndView("error-500");
}
System.out.println(status);
return new ModelAndView("error");
}
}
there is an #ControllerAdvice annotation
#ControllerAdvice
public class MyErrorController {
#ExceptionHandler(RuntimeException.class)
public String|ResponseEntity|AnyOtherType handler(final RuntimeException e) {
.. do handle ..
}
#ExceptionHandler({ Exception1.class, Exception2.class })
public String multipleHandler(final Exception e) {
}
}
To handle errors, There is no need to define a controller class
implementing an error controller.
To handle errors in your entire application instead of writing
#Controller
public class CustomErrorController implements ErrorController{
#RequestMapping("/error")
public String handleError(HttpServletRequest request)
{
...
}
}
use the below class
#ControllerAdvice
public class myExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(Exception.class)
public final ResponseEntity<YourResponseClass> handleAllExceptions(Exception ex, WebRequest request) {
YourResponseClassexceptionResponse = new YourResponseClass(new Date(), ex.getMessage());// Its an example you can define a class with your own structure
return new ResponseEntity<>(exceptionResponse, HttpStatus.INTERNAL_SERVER_ERROR);
}
#ExceptionHandler(CustomException.class)
public final ResponseEntity<YourResponseClass> handleAllExceptions(Exception ex, WebRequest request) {
YourResponseClass exceptionResponse = new YourResponseClass(new Date(), ex.getMessage()); // For reference
return new ResponseEntity<YourResponseClass>(exceptionResponse, HttpStatus.INTERNAL_SERVER_ERROR);
}
#ExceptionHandler(BadCredentialsException.class)
public final ResponseEntity<YourResponseClass> handleBadCredentialsException(BadCredentialsException ex, WebRequest request){
YourResponseClass exceptionResponse = new YourResponseClass(new Date(), ex.getMessage());// For refernece
return new ResponseEntity<>(exceptionResponse, HttpStatus.UNAUTHORIZED);
}
}
The class above annoted with #ControllerAdvice acts as custom exception handler and it handles all the expecptions thrown by ur application. In above code sample only three exceptions are showed for understanding. It can handle many exceptions
In your application if there's any exception thrown it will come to this class and send the response back. You can have a customized message and structure as per ur needs.
#Controller
public class AppErrorController implements ErrorController {
#RequestMapping("/error")
public String handleError(HttpServletRequest request) {
Object status = request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
if(status != null) {
int statusCode = Integer.valueOf(status.toString());
if (statusCode == HttpStatus.FORBIDDEN.value()) {
return "errorpages/error-403";
} else if (statusCode == HttpStatus.NOT_FOUND.value()) {
return "errorpages/error-404";
} else if (statusCode == HttpStatus.INTERNAL_SERVER_ERROR.value()) {
return "errorpages/error-500";
}
}
return "errorpages/error";
}
}
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
#Aspect
public class ExceptionAlerts {
public ExceptionAlerts() {
System.err.println("Class Scanned");
}
#AfterThrowing(pointcut = "com.name.papp.star", throwing = "ex")
public void doRecoveryAction(Throwable ex) throws Throwable {
System.err.println(">>>>>>>>>>>>>>>>Recovery Actions++++++++++++++++");
}
}
Service Interface
public interface SignInService {
CustomerSignInDTO signIn()
throws LappException;
}
Service Implementation Class
public class SignInServiceImpl implements SignInService {
#Override
#Transactional(readOnly = false, rollbackFor = Exception.class)
public CustomerSignInDTO signInCustomer(CustomerDeviceDTO customerDeviceDTO,
String mobileNumber, boolean createIfNotExist)
throws LappException {
// Throwing Exception Here
}
}
Problem -
Spring-Boot-1.2.5
The method doRecoveryActions never gets called. I am also using #ExceptionHandler somewhere to prepare the error response. Is it because #ExceptionHandler catches all the exceptions and doRecoveryActions is never called? Any suggestions would be appreciated!
...if the instance needs to be constructed manually, perhaps by a 3rd party factory class? Previously, (Jersey 1.x), you would do something like this:
public class MyInjectableProvider extends PerRequestTypeInjectableProvider<Context, MyInjectable> {
public MyInjectableProvider() {
super(MyInjectable.class);
}
#Override
public Injectable<MyInjectable> getInjectable(ComponentContext ic, Context context) {
MyInjectable myInjectableInstance = //...
return new Injectable<MyInjectable>() {
#Override
public MyInjectable getValue() {
return myInjectableInstance;
}
};
}
}
The anonymous local class is able to access an instance to return within some scope. This is useful when you're not working with classes that have default constructors, but they need to be constructed on a per-request basis.
Jersey 2.0 switched over to HK2 as a dependency injection framework, but alas, the migration page (https://jersey.java.net/documentation/latest/migration.html) doesn't provide an example of this kind of binding, and the HK2 documentation doesn't provide examples using an AbstractBinder.
To elaborate just a bit more, I'm trying to provide resource-local, container-agnostic JPA EntityManager instances to my resources. These have to be fetched from a singleton factory class, and should only stick around for a single "unit of work," which is a request in my case. I'm aware there are workarounds (Just inject the factory, or bind to a threadlocal), but I found the previous solution elegant and would like to recreate it if possible.
EDIT:
After digging through the HK2 javadocs for a bit, I've discovered that something similar can be achieved as follows:
public class MyInjectableProvider extends AbstractBinder
implements Factory<MyInjectable> {
#Override
protected void configure() {
bindFactory(this).to(MyInjectable.class);
}
#Override
public MyInjectable provide() {
return getMyInjectable();
}
#Override
public void dispose(MyInjectable instance) {}
}
And to register it...
public class MyResourceConfig extends ResourceConfig {
public MyResourceConfig() {
register(new MyInjectableProvider());
}
}
This "seems to work," but it also seems a bit unclear. dispose() is never called, for example. Also, this binding seems to implicitly behave as RequestScoped. Modifying the configuration to bindFactory(this).to(MyInjectable.class).in(RequestScoped.class); doesn't appear to actually change the behavior. Am I missing something, or is this the intended solution?
In place of Factory<T>.dispose(T), registering with the injectable CloseableService may do most of what you want. A CloseableFactory adapter will be required. CloseableService closes() all registered resources upon exiting the request scope.
For a specific example, see the ConnectionFactory below.
import org.glassfish.hk2.api.Factory;
import org.glassfish.jersey.server.CloseableService;
import javax.inject.Inject;
import javax.ws.rs.InternalServerErrorException;
import java.sql.Connection;
import java.sql.SQLException;
import static com.google.common.base.Preconditions.checkNotNull;
public class ConnectionFactory implements Factory<Connection> {
private final CloseableService closeableService;
#Inject
public ConnectionFactory(CloseableService closeableService) {
this.closeableService = checkNotNull(closeableService);
}
public Connection provide() {
final Connection connection;
try {
connection = acquireConnection();
} catch (SQLException e) {
throw new InternalServerErrorException(e);
}
try {
closeableService.add(new CloseableConnection(connection));
} catch (Throwable t) {
closeQuietly(connection);
throw runtime(t);
}
return connection;
}
public void dispose(Connection connection) {
closeQuietly(connection);
}
private static RuntimeException runtime(Throwable t) {
throw ConnectionFactory.<RuntimeException>unchecked(t);
}
private static <T extends Throwable> T unchecked(Throwable t) throws T {
throw (T) t;
}
private static void closeQuietly(Connection connection) {
try {
connection.close();
} catch (SQLException ignore) {}
}
}
Below is a less general version of a CloseableFactory - a CloseableConnection.
import java.io.Closeable;
import java.io.IOException;
import java.sql.Connection;
import java.sql.SQLException;
import static com.google.common.base.Preconditions.checkNotNull;
public final class CloseableConnection implements Closeable {
private final Connection connection;
public CloseableConnection(Connection connection) {
this.connection = checkNotNull(connection);
}
public void close() throws IOException {
try {
connection.close();
} catch (SQLException e) {
throw new IOException(e);
}
}
}