In JAX-RS I have several exceptions extending WebApplicationException, for example NotAuthorizedException and NotFoundException. These are mapped to the corresponding HTTP status codes, so for basic use cases I can just throw instances of these exceptions.
In Spring MVC I noticed that I have to write my own exception classes, and add the corresponding #ResponseStatus annotations:
#ResponseStatus(HttpStatus.UNAUTHORIZED)
private class NotAuthorizedException extends RuntimeException {
NotAuthorizedException(String message) {
super(message);
}
}
Is there a simpler way? Do pre-defined exception classes exist?
Related
I have a Spring Boot server application where exceptions are caught, logged and handled when possible and when not possible they are handled by #ControllerAdvice as the last resort.
I want to add more logic for exception handling, for example, sending a metric to a monitoring solution on each exception. For example there's a Spring bean MetricsBean which is a service which sends metrics. Using MetricsBean is easy for errors which are handled by #ControllerAdvice because it's also a Spring bean.
However using MetricsBean is not as straightforward in such code:
try {
// business logic
} catch (Exception ex) {
logger.error(ex)
}
because that would mean manually autowiring MetricsBean into each service class. Also logger object is not a Spring bean.
So I have a dilemma how do I send a metric from inside or around logger.error() method when the logger class itself is not a Spring bean. I'm wondering what the best practice is in such cases. Here're some of the options:
Should logger also be a Spring bean? (seems tedious and error-prone)
Should there be a custom Exception extending class which would be a Spring bean and will send a metric inside its constructor? (seems tedious and error-prone)
Should Spring AOP be used with the pointcut being logger.error() method?
I'm surprised that there's not an established solution for this pattern for such an established framework as Spring (most of the solutions focus on handling errors in #ControllerAdvice).
I'd like the solution to be as generic as possible (point 3 seems to win there) but perhaps there's a pattern which I'm missing.
Not sure if it is the best practice but I would use a UtilityClass as a facade exposing a static method. In that method, wire the metrics registry using Metrics.globalRegistry to avoid injection. This will avoid coupling the metric details from where it is used.
You can then either invoke the static method everywhere you are handling an exception (so besides logger.error(ex)) or combine this with your third option with the pointcut.
Simple example:
#UtilityClass
public class ExceptionUtility {
public static void handleException(Throwable throwable) {
Counter.builder("some.name.exception")
.tag("name", throwable.getClass().getName())
.register(Metrics.globalRegistry)
.increment();
}
}
Usage:
try {
...
} catch (RestClientException restClientException) {
ExceptionUtility.handleException(restClientException);
}
I have a Spring Boot Java application. There is a service class that throws a "401 Unauthorized" HttpClientErrorException since the access token used in the application has expired. I want to handle this exception globally for which I have used the #ControllerAdvice annotation.
The error is:
Caused by: org.springframework.web.client.HttpClientErrorException: 401 Unauthorized
The class is:
#Slf4j
#EnableWebMvc
#ControllerAdvice(basePackages = Service.class)
public class HttpClientErrorHandler{
#ExceptionHandler(HttpClientErrorException.class)
#ResponseStatus(HttpStatus.UNAUTHORIZED)
public String errorHandle(HttpClientErrorException e) {
log.error("log HttpClientErrorException: ", e);
return "HttpClientErrorException_message";
}
}
Since the exception was caused in the service class, I have mentioned it specifically in the basePackages. The entire configuration for the program is specified in the application.yml file. I have not used the xml configuration. I don't understand why the #ControllerAdvice annotation is not working. The program still throws the exception. Can someone explain?
#ControllerAdvice(basePackages = Service.class)
The exception is bubbled to #Controller class, and #ControllerAdvice is supposed to apply to controller, so you should set basePackageClasses to your controller package instead of your service package.
By default, #ControllerAdvice is applied to all Controller so you can remove the basePackageClasses unless you want to narrow down the controller advise
I had also faced similar issue,
try adding #Order(Ordered.HIGHEST_PRECEDENCE) below #ControllerAdvice.
We add it to get priority over Spring's default DefaultHandlerExceptionResolver. To understand more about why we add it read this answer.
Also no need to give base packages, it will consider all packages by default.
To handle exception of any other type you can include below existing exception handler for HttpClientErrorException you already have written,
#ExceptionHandler(Exception.class)
public Strring handleAnyExceptions(Exception ex) {
return "your message";
}
Hope it helps !
My Application - Java 1.6, Spring 3.1.2, Hibernate 4.1.1, Thymeleaf 2.0.15
Currently in my application, there is no any exception/error handling mechanism is implemented. I handling exceptions in ordinary cultural way. But now I need to introduce a "Robust Error Handling Mechanism". Please suggest me to implement Custom Error/Exception Handling mechanism with Example.
Thanks and appriciate from experts like you.
I'll make as an answer its easier to format. When you say "But I need to implement some Generalized Custom Exception so that same Exception could be thrown acrross the application." Its how I understood it and something that #ControllerAdvice is directly handling, but it applies only to Controllers. Than again, all you lower layers can declare throws on the method, and delegate it to Controller for exception handling. As an example, the following would be the error handling controller handling your custom exception
#ControllerAdvice
public class GlobalErrorHandler {
#ExceptionHandler(value = VermaException.class)
#ResponseBody
public String heightError(VermaException ex) {
return "error";
}
}
Your exception
public class VermaException extends Exception {
}
now whenever the the exception is thrown from the controller class, it will be captured and handled in your GlobalErrorHandler.
Again pasting the reference http://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc
UPDATE after comment
#ControllerAdvice is added in the version 3.2, for the earlier version you can have a CommonController extended by your controller, containing the error handler methods e.g. per Controller solution
#Controller
public class CommonController {
#ExceptionHandler
public #ResponseBody String handle(VermaException e) {
return "error";
}
}
an extending contorller
#Controller
public class ExceptionController extends CommonController {
#RequestMapping("/exception")
public #ResponseBody String exception() {
throw new VermaException();
}
}
I'm working on project which has the following structure:
DaoService - is Spring bean which has SessionFactory object and performs Database manipulation on Database using Hibernate. marked with #Repository
several BLogicService services - are Spring beans which have DaoService Autowired and performs some operation on POJOs and persist it in Hibernate. Marked with #Service annotations.
JSF 2.1 Managed Beans - iterate with XHTML pages and hold properties and JSF actions. Marked as #ManagedBean and receive BlLogicServices objects from Spring as #ManagedProperty
and finally XHTML pages which access managed beans.
What would be correct way to manage exception handling in such application? If i have exception on DAO Level, what would be the correct way to forwatd it to GUI?
If I were working with Spring MVC I would use `#ExceptionHandler, but how can it be done in JSF 2.1 with Spring?
To create a general exception catcher for all unexpected exceptions during BL processing you can implement ExceptionHandlerFactory and specify it in the faces-config.xml:
<factory>
<exception-handler-factory>
my.package.ExceptionHandlerFactory
</exception-handler-factory>
</factory>
It should create ExceptionHandler implementation which in turn implements handle method for example one consuming exceptions (I think I have taken it from JSF2 reference or similar source):
private static class MyExceptionHandler extends ExceptionHandlerWrapper {
private ExceptionHandler parent;
public WfExceptionHandler(ExceptionHandler parent) {
this.parent = parent;
}
#Override
public ExceptionHandler getWrapped() {
return this.parent;
}
#Override
public void handle() throws FacesException {
for (Iterator<ExceptionQueuedEvent> i =
getUnhandledExceptionQueuedEvents().iterator();
i.hasNext();) {
ExceptionQueuedEvent event = i.next();
i.remove();
ExceptionQueuedEventContext context =
(ExceptionQueuedEventContext) event.getSource();
Throwable t = context.getException();
myProcessing(t);
}
}
...
}
myProcessing(t) can retrieve managed bean which will print exception to your gui console or you can just use FacesContext.getCurrentInstance().addMessage(). You will also need to call FacesContext.getCurrentInstance().renderResponse() to update the view since unhandled exception aborted the JSF lifecycle.
Alternatively you can use try/catch in all your managed beans methods and execute equivalent of myProcessing(t) there. The difference is ExceptionHandler will also catch exceptions during page rendering, not necessarily generated by your classes.
Exceptions should not be passed to the UI.
The fact that you're using a web MVC layer says that you've got controllers of some kind to accept, validate, and bind incoming requests; choose a handler for fulfilling the request; package the response, good or bad; and choose the next view. The exception needs to make its way back to that controller, which will then make the necessary information and choose the error view.
The rest of your question is just confusing the issue. DAOs, MDBs, etc. - all need to be marshalled by a handler of some kind. (Does JSF still call those Actions? I never use it.) That handler should catch any exceptions and communicate them back to the UI via the controller.
I am using JAX WS to expose a WebService. Some of the operations of this service can generate exceptions. Not internal server exceptions, but rather exceptions that are dependent on the input arguments of the operation invocation.
If I specify that my operation throws a custom Exception, like so:
#WebService
#SOAPBinding(style = Style.RPC, use = Use.LITERAL)
public class MyServiceEndpointImpl implements MyServiceEndpoint {
#WebMethod
public void throwsException throws InvalidInputException;
}
I end up with the following stacktrace when running the application:
com.sun.xml.ws.model.RuntimeModelerException: runtime modeler error: Wrapper class com.mypackage.ws.services.jaxws.InvalidInputExceptionBean is not found. Have you run APT to generate them?
at com.sun.xml.ws.model.RuntimeModeler.getClass(RuntimeModeler.java:285)
at com.sun.xml.ws.model.RuntimeModeler.processExceptions(RuntimeModeler.java:1006)
at com.sun.xml.ws.model.RuntimeModeler.processRpcMethod(RuntimeModeler.java:969)
at com.sun.xml.ws.model.RuntimeModeler.processMethod(RuntimeModeler.java:546)
at com.sun.xml.ws.model.RuntimeModeler.processClass(RuntimeModeler.java:370)
at com.sun.xml.ws.model.RuntimeModeler.buildRuntimeModel(RuntimeModeler.java:256)
at com.sun.xml.ws.server.EndpointFactory.createSEIModel(EndpointFactory.java:322)
at com.sun.xml.ws.server.EndpointFactory.createEndpoint(EndpointFactory.java:188)
at com.sun.xml.ws.api.server.WSEndpoint.create(WSEndpoint.java:467)
at org.jvnet.jax_ws_commons.spring.SpringService.getObject(SpringService.java:333)
at org.jvnet.jax_ws_commons.spring.SpringService.getObject(SpringService.java:45)
at org.springframework.beans.factory.support.FactoryBeanRegistrySupport$1.run(FactoryBeanRegistrySupport.java:121)
Adding #XmlRootEntity to InvalidInputException does not solve the problem.
If this is not the recommended way to report faults over web services, then is there a better way? Should my exceptions inherit from RuntimeException and rely on the transport for the error handling (i.e., everything will end up wrapped in a SOAPException)? I was hoping for something like Spring-WS' SoapFaultAnnotationExceptionResolver. Is there something similar at all available for JAX-WS?
Did you try to annotate your exception with #WebFault? Also, do you implement getFaultInfo()?
EDIT: I realize my answer was maybe not detailed enough. As reminded in this thread (for example):
The JAX-WS 2.0 specification demands
that the exception annotated with
#WebFault must have two constructors
and one method [getter to obtain the fault information]:
WrapperException(String message, FaultBean faultInfo)
WrapperException(String message, FaultBean faultInfo, Throwable cause)
FaultBean getFaultInfo()
The WrapperException is replaced by
the name of the exception, and
FaultBean is replaced by the class
name that implements the fault bean.
The fault bean is a Java bean that
contains the information of the fault
and is used by the Web service client
to know the cause for the fault.
This is detailed in section 2.5 Fault of the JAX-WS specification. Does your exception conform to this? Can you post the code?
The OP is right. As per specification 2.1, section 3.7 Service Specific Exception, it is not required to use the #WebFault annotation, JAX-WS can generate the wrapper beans dynamically for exceptions that do not match the pattern described in section 2.5 (just provide a getter for the information you want to be present in the fault). For exceptions that match the pattern described in section 2.5 (i.e. exceptions that have a getFaultInfo method and #WebFault annotation), the FaultBean is used as input to JAXB when mapping the exception to XML Schema.
So the solution suggested above (matching the pattern described in section 2.5) is only a workaround. The generation of wrapper beans should just work for other exceptions. And I don't know why this fails here.
An addition to the answer above. I ended up with this as my InvalidInputException implementation:
#WebFault(faultBean = "com.mypackage.ws.exception.FaultBean")
public class InvalidInputException extends Exception {
private static final long serialVersionUID = 1L;
private FaultBean faultBean;
public InvalidInputException() {
super();
}
public InvalidInputException(String message, FaultBean faultBean, Throwable cause) {
super(message, cause);
this.faultBean = faultBean;
}
public InvalidInputException(String message, FaultBean faultBean) {
super(message);
this.faultBean = faultBean;
}
public FaultBean getFaultInfo() {
return faultBean;
}
}
And FaultBean is just a simple POJO with currently no data at all. Now, according to the JAX-WS specification (see 3.7 Service Specific Exception), it conforms to what is required of an exception annotated with #WebFault, so it will not create a wrapper bean for it, which probably is what was failing.
This is a decent workaround, but it does not explain the error in the question.