I have an ExceptionMapper as a #Provider for handling all of my exceptions.
So it is obvious that my class implements ExceptionMapper<Throwable> and we as know, all exceptions are extended Throwable somehow.
Now I used jax-rs #NotNull to checking my resources input values to be not null and by some search, I realized it will throw ConstraintViolationException when the annotated field is null.
So I tried to handle it and add some details to response (add some custom json model) in my ExceptionMapper like this:
#Provider
public class AllExceptionMapper implements ExceptionMapper<Throwable> {
private final Logger logger = LogManager.getLogger(AllExceptionMapper.class);
#Override
public Response toResponse(Throwable ex) {
Response response;
CustomError error;
// handle some errors
else if (ex instanceof ConstraintViolationException) {
error = new CustomError(
324, // predefined error type in our documents
"some details"
);
response = Response.status(Response.Status.BAD_REQUEST).entity(error).build();
}
// handle some other errors
return response;
}
}
The problem is this didn't work but if I would create another exception mapper provider which implements ExceptionMapper<ConstraintViolationException> and handle it there, it works without any problem.
As I said before (and also checked) all exceptions are extended from Throwable class somehow so what am I missing and why it is not working?
......
By didn't work, I mean it ignores my mapper (the one which implements from ExceptionMapper<Throwable>) and had the normal behavior which is returning the status code 400 with no response payload like there is no mapper at all
The way the ExceptionMapper is supposed to work, is that you can create a generic ExceptionMapper<Throwable>, which handles all errors. And then you can create more specific ExceptionMappers which handle the more specific errors in another way. All of this in separate classes.
The fact that doing it in a separate class works for you, lets me believe that there's a more specific ExceptionMapper somewhere, which handles the exception before you can.
The way ExceptionMappers are intended to be used is actually very clean, and also keeps your code clean. Wanting to keep the code in one central place, will result in a giant if...
Don't using #Privider and should be using 'ResourceConfig.register(MyExceptionMapper);'
public class ErrorHandler implements ExceptionMapper<Throwable> {
private final static Logger LOGGER = LoggerFactory.getLogger(ErrorHandler.class);
#Override
public Response toResponse(Throwable e) {
LOGGER.error("Handle a unknown error, " + e.getClass() + ": " + e.getMessage(), e);
e.printStackTrace();
Response.ResponseBuilder responseBuilder = Response.status(status);
responseBuilder.entity(exception);
return responseBuilder.build();
}
}
class Config extends ResourceConfig {
private final static Logger LOGGER = LoggerFactory.getLogger(Main.class);
public Config() {
super();
super.register(ErrorHandler.class);
}
}
Related
I have a bunch of custom spring converters in my codebase something like:
public class ElasticSearchConverter implements Converter<RequestModel, ResponseModel> {
#Override
public final ResponseModel convert(RequestModel requestModel) {
if(!requestModel.isValid()) {
throw new ElasticSearchException("Request Model is not valid");
}
... Implement converter
}
}
I call those converters from a service by using the spring ConversionService
#Service
public class ElasticService {
#Autowired
private ConversionService conversionService;
public ResponseModel getResponse(RequestModel requestModel) {
//Throws ConversionFailedException instead of ElasticSearchException
ResponseModel responseModel = conversionService.convert(requestModel, ResponseModel.class);
return responseModel;
}
}
My problem is when I throw my ElasticSearchException inside my ElasticSearchConverter it gets caught inside the spring ConversionUtils class and converted to a ConversionFailedException. I want to catch the specific ElasticSearchException that I'm throwing in my converter.
How can I catch a specific exception from a spring Converter class in my service class?
You need to implement class that will handle your Exceptions
#ControllerAdvice
public class ExceptionTranslator {
#ExceptionHandler(ConversionFailedException.class) //handle your Exception
#ResponseStatus(HttpStatus.BadRequest) // Define the status you want to return
#ResponseBody
public ErrorDTO processElasticSearchException(ConversionFailedException ex) {
return new ErrorDTO();
/* Format your response as you need*/
}
}
#ControllerAdvice "Classes with (the annotation) can be declared explicitly as Spring beans or auto-detected via classpath scanning" show documentation
#ExceptionHandler defines the exception you want to catch
#ResponseStatus defines the http response status
#ResponseBody Serialize automatically response as json object
For my projects, i define an ErrorDTO to format the response, you can do the same and you will just have to construct your object and to return it
You can also put the code you want to execute into this method and rise an other exception if needed
You could write a simple adapter class that wraps the Spring ConversionService. In that class you would have a convert() method that delegates to the wrapped ConversionService method in a try/catch, catch the ConversionFailedException, inspect it (e.g. using getRootCause()) and rethrow as the exception of your choice. Then for all classes that would otherwise use ConversionService you would use your wrapper class instead.
You're violating the Single Responsibility Principle. It's not the converter's job to validate the converted object. If you're able to do the conversion successfully, then validation should be done separately using one of the ways described here.
For example, consider you're converting an string to a latitude. If the string can be parsed into a double, the converter should be happy. If you want to validate that the double is within the range [-90,+90], you should do so in a validator and not the converter.
Not mixing up different concerns will help a lot when you're handling exceptions, say in a #ControllerAdvice.
I have service calls in my application that make remote network calls to other services as well as DB calls. Spring Boot has good support for rolling back bad transactions with #Transactional, but I wanted to know if I could define a custom rollback procedure using an annotation.
I would need to rollback the data on the other services as well as the database.
In code, I could do it like this:
#Transactional
public void doSomethingComplicated() {
try {
srvcOne.makeRemoteNetworkCall();
srvcTwo.makeDatabaseCall();
} catch(Exception e) {
srvcOne.rollBackNetworkCall();
}
}
but I was hoping I could do something like this:
#Transactional
#MyCustomRollbackListener(handler = MyCustomRollBackHandler.class)
public void doSomethingComplicated() {
srvcOne.makeRemoteNetworkCall();
srvcTwo.makeDatabaseCall();
}
and in the handler:
public class MyCustomRollBackHandler {
public void handleRollback() {
srvcOne.rollBackNetworkCall();
}
}
I implemented a global exception listener and I am able to get the class the exception came from, but I have no way to get the method and to retrieve any annotations on it. Here is my initial attempt:
#ControllerAdvice
public class RollbackExceptionListener{
private static final Logger logger = LoggerFactory.getLogger(RollbackExceptionListener.class);
#ExceptionHandler(Exception.class)
public void lookForAnnotationClassForException(final Exception exception) {
logger.error("Exception thrown", exception);
final StackTraceElement topElement = exception.getStackTrace()[0];
final Class callingClass = topElement.getClass();
final String methodName = topElement.getMethodName();
try {
// Can't get the method with just the name, need to
// know the params as well.
final Method method = callingClass.getMethod(methodName);
// Retrieve the annotation on the method and call the handler
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
Is there anyway to do something like this?
Arguments are not part of the Stacktrace. If the method is unique, i.e. not overloaded, you can probably find it with getMethods()? Something else that comes to mind, maybe you can look at Aspects to wrap the method in some handler before it is executed. Can be done either at compile time or runtime.
The aspect can do the rollback itself, it can enrich the exception with the information you need, or it can set some ThreadLocal variable with the handler class that was defined in the method before re-throwing the exception. You can then get this value from the ThreadLocal at the point where you catch the exception.
I am using Jersey for rest API, JerseyTests to unit test.
I have been following what seems to be conventional practice over the internet for PathParams checking and Exception Handling, but I don't quite understand what I am doing wrong here:
RoomApplicationResource.java
#Path("demandes")
public class RoomApplicationResource {
#GET
#Path("/{email}/{requestNumber}")
public Response getRoomApplication(
#PathParam("email") String email,
#PathParam("requestNumber") String requestNumber) throws NoRoomApplicationFoundException {
if (email == "wrong#email.com" || requestNumber == "wrong") {
throw new NoRoomApplicationFoundException("bad request");
}
String response =requestNumber+" is valid for "+email;
return Response.ok(response).build();
}
}
I handle Exceptions like this:
NotFoundMapper.java
#Provider
public class NotFoundMapper implements ExceptionMapper<NoRoomApplicationFoundException>{
#Override
public Response toResponse(NoRoomApplicationFoundException e) {
return Response.status(Response.Status.NOT_FOUND)
.entity(e.getMessage()).build();
}
}
NoRoomApplicationFoundException.java
public class NoRoomApplicationFoundException extends RuntimeException {
private static final long serialVersionUID = 1L;
public NoRoomApplicationFoundException() {
super();
}
public NoRoomApplicationFoundException(String msg) {
super(msg);
}
public NoRoomApplicationFoundException(String msg, Exception e) {
super(msg, e);
}
}
And I test like this:
RoomApplicationResourceTest.java
public class RoomApplicationResourceTest extends JerseyTest {
#Override
protected Application configure() {
return new ResourceConfig(RoomApplicationResource.class, NotFoundMapper.class);
}
// This test works fine as expected
#Test
public void whenParametersAreExistantReturnTheOkResponse() {
final Response res = target("demandes").path("valid#email.com").path("12345").request().get();
assertEquals(200, res.getStatus());
assertEquals("12345 is valid for valid#email.com", res.readEntity(String.class));
}
// This does not work as expected
#Test
public void whenEmailParameterDoNotMatchToAnyRoomApplicationThenReturns404() {
final Response res = target("demandes").path("wrong#email.com").path("12345").request().get();
assertEquals(404, res.getStatus());
assertEquals("bad request", res.readEntity(String.class));
}
}
Question 1: Is this way of doing conditional checking on params wrong? The result of the second test where the email is invalid should throw my custom exception and return a 404, but instead returns a 200 and the valid message.
Question 2: How should I handle missing parameters in this case? It seems Jersey throws a NotFoundException by default. Is there a simple way to customize the message of that error or perhaps use my custom exception as the throws NoRoomApplicationFoundException at the end of my resource method does not seem to be doing anything?
Thanks in Advance. Alex
Question 1
Yes. The problem is your use of == to compare Strings. You should instead be using String.equals(). See How do I compare Strings in Java?
if ("wrong#email.com".equals(email) || "wrong".equals(requestNumber)) {
throw new NoRoomApplicationFoundException("bad request");
}
Question 2:
This question seems to be related to your first question. But for me, as a general rule (this is just me), if I am authoring the exception class and the exception is specific to my JAX-RS application (meaning I will have no use for it outside the JAX-RS application), I will just make the exception extend WebApplicationException. This exception will be handled by default, and you can create the Response in that class. No need for any ExceptionMapper. For example
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;
public class NoRoomApplicationFoundException extends WebApplicationException {
private static final long serialVersionUID = 1L;
public NoRoomApplicationFoundException() {
this("Room not found", 400);
}
public NoRoomApplicationFoundException(String msg, int status) {
this(Response.status(status).entity(msg).build());
}
public NoRoomApplicationFoundException(Response response) {
super(response);
}
}
You could completely get rid of the NotFoundMapper and this would work just fine.
if ("wrong#email.com".equals(email) || "wrong".equals(requestNumber)) {
throw new NoRoomApplicationFoundException();
}
Some Resources:
Jersey Documentation for Exception Handling
WebApplicationException Javadoc to see different constructors. There are alot more ways you can construct your exception and chain up to the super class
What should I return if my object is null?. In general, if a resource can't be found, the general rule of thumb is to return a 404 Not Found. Not sure it applies to this specific case though.
Good day!
I have a #Controller. Some of its methods throw the same exception, but I want to handle this exceptions in different way.
Is there a way how to bind an #ExceptionHandler to a particular method?
You need to use AOP tools like CDI Interceptor or AspectJ to achieve this cross-cutting concerns. A Concern is a term that refers to a part of the system divided on the basis of the functionality.
Basically this type of feature is used to handle logging, security and also handling the errors... which are not part of your business logic...
Like if you want to change the logger for application from log4j to sl4j then you need to go through each and every classes where you have used log4j and change it. But if you have used AOP tools then you only need to go the interceptor class and change the implementation. Something like plug and play and very powerful tool.
Here is a code snippet using JavaEE CDI Interceptor
/*
Creating the interceptor binding
*/
#InterceptorBinding
#Retention(RUNTIME)
#Target({TYPE, METHOD})
public #interface BindException {
}
After we have define interceptor binding we need to define interceptor binding implementation
/*
Creating the interceptor implementation
*/
#Interceptor
#BindException
public class ExceptionCDIInterceptor {
#AroundInvoke
public Object methodInterceptor(InvocationContext ctx) throws Exception {
System.out.println("Invoked method " + ctx.getMethod().getName());
try {
return ctx.proceed(); // this line will try to execute your method
// and if the method throw the exception it will be caught
} catch (Exception ex) {
// here you can check for your expected exception
// code for Exception handler
}
}
}
Now we only need to apply interceptor to our method
/*
Some Service class where you want to implement the interceptor
*/
#ApplicationScoped
public class Service {
// adding annotation to thisMethodIsBound method to intercept
#BindException
public String thisMethodIsBound(String uid) {
// codes....
// if this block throw some exception then it will be handled by try catch block
// from ExceptionCDIInterceptor
}
}
You can achieve same feature using AspectJ also.
/*
Creating the Aspect implementation
*/
#Aspect
public class ExceptionAspectInterceptor {
#Around("execution(* com.package.name.SomeService.thisMethodIsBound.*(..))")
public Object methodInterceptor(ProceedingJoinPoint ctx) throws Throwable {
System.out.println("Invoked method " + ctx.getSignature().getName());
try {
return ctx.proceed(); // this line will try to execute your method
// and if the method throw the exception it will be caught
} catch (Exception ex) {
// here you can check for your expected exception
// codes for Exception handler
}
}
}
Now we only need to enable the AspectJ to our application config
/*
Enable the AspectJ in your application
*/
#Configuration
#EnableAspectJAutoProxy
public class AppConfig {
#Bean
public SomeService SomeService() {
return new SomeService();
}
}
/*
Some Service class where you want to implement the Aspect
*/
package com.package.name;
public class SomeService {
public String thisMethodIsBound(String uid) {
// codes....
// if this block throw some exception then it will be handled by try catch block
// from ExceptionAspectInterceptor
}
}
I have code example in my git repo https://github.com/prameshbhattarai/javaee-exceptionBinding by using CDI interceptor.
Just as an option (obviously, it's not ideal): you can wrap the exception into a custom exception in one of your methods and then catch it in an #ExceptionHandler
void boo() throws WrappingException {
try {
} catch (TargetException e) {
throw new WrappingException(e);
}
}
then
#ExceptionHandler(WrappingException.class)
public void handleWrappingException() {
// handle
}
#ExceptionHandler(TargetException.class)
public void handleTargetException() {
// handle
}
Could you please explain why do you need this? I'm asking out of curiosity, because I've never felt like this is required and here is why:
Exception usually represents a very specific "mistake" - something that went wrong in a very specific way.
Basically, exception represents a mistake, not a flow...
There are two "degrees of freedom" that spring can support out of the box:
Exception parameters. Maybe stuff like error code, which can be declared as a data field of exception itself.
Exception inheritance. Example:
If you have in your system a UserDoesNotExistException and you want to be more specific in a case of say, the system that manages the users that are retired in some flows, you can always create a more specific exception:
class UserRetiredException extends UserDoesNotExistException {...}
Obviously, spring can support both the cases: In ExceptionMapper you have the access to the exception anyway so you can do something like:
handleException(SomeExceptionWithErrorCode ex) {
if(ex.getErrorCode() == "A") {
// do this
}
else if(ex.getErrroCode() == "B") {
// do that
}
}
In the second case you just have different exception mappers for the different types of exceptions.
You can also consider #ControllerAdvice annotation to reuse code or something.
I don't think you can specify a specific #ExceptionHandler for a method, but you can bind an #ExceptionHandler method to a specific Exception.
So if you want to handle all DataIntegrityViolationException one way and all other Exceptions in another you should be able to achieve that with something like this:
#ExceptionHandler(DataIntegrityViolationException.class)
public void handleIntegrityViolation() {
// do stuff for integrity violation here
}
#ExceptionHandler(Exception.class)
public void handleEverythingElse() {
// do stuff for everything else here
}
You can derive sub-exceptions from the common exception thrown by other methods according to how you want to handle them.
Say you have declared the parent exception as ParentException. Derive sub classes like ChildAException extends ParentException, ChildBException extends ParentException etc.
Define a #ControllerAdvice class that catches the ParentException and define the specific behaviors in delegate methods.
#ControllerAdvice
public class ParentExceptionHandler {
#ExceptionHandler(ParentException.class)
public ResponseEntity<Object> handleParentException(ParentException pe) {
if (pe instanceof ChildAException) {
return handleChildAException((ChildAException) pe);
} else if (...) {
...
} else {
// handle parent exception
}
}
private ResponseEntity<Object> handleChildAException(ChildAException cae) {
// handle child A exception
}
}
I just got the same issue like you. So I checked the spring source code for this situation. It seems that spring will search in the #Controller class for any method that is annotated with #ExceptionHandler first, if nothing matched then it will continue to search for all class that is annotated with #ControllerAdvice. So you can just use the strategy below:
MyController with a #ExceptionHandler method:
#RestController
public class MyController {
#RequestMapping("/foo")
public String foo() {
throw new IllegalArgumentException();
}
#ExceptionHandler(IllegalArgumentException.class)
public ResponseEntity<String> handle(IllegalArgumentException ex) {
return new ResponseEntity<>("Specific handler", HttpStatus.BAD_REQUEST);
}
}
AnotherController without any method annotated with #ExceptionHandler:
#RestController
public class AnotherController {
#RequestMapping("/bar")
public String bar() {
throw new IllegalArgumentException();
}
}
A global #ControllerAdvice class:
#ControllerAdvice
public class GlobalExceptionHandler {
#ExceptionHandler(IllegalArgumentException.class)
public ResponseEntity<String> handle(IllegalArgumentException ex) {
return new ResponseEntity<>("Global handler", HttpStatus.BAD_REQUEST);
}
}
Then if you visiting http://ip:port/foo, you will get 400 status code with Specific handler, and 400 status code with Global handler when you visit http://ip:port/bar.
I agree that the inability to map a specific #ExceptionHandler to handle only one specific method in the #RestController should be a very desirable feature.
I tried try{}catch(Exception ex){} and catch no exception. But
exception handler handles it nicely.
Since we are talking about hibernate exceptions, these exceptions are usually thrown at the commit phase of transaction. The problem here is that seems like you have transaction opened right in your controller which is considered as a bad practice.
What you should do is - open transaction in the application layer.
Controller just maps xml/json to incomming RequestDto object.
And then you call the Service to handle the business logic.
The Service(or its method) should be annotated by #Transactional.
#RestController
public class MyController {
#Autowired // but better to use constructor injection
private MyService myService;
public ResponseDto doSomething(RequestDto request) {
try {
myService.doSomething(request);
} catch (DataIntegrityViolationException ex) {
// process exception
}
}
}
#Transactional
class MyService {
public void doSomething() {
// do your processing which uses jpa/hibernate under the hood
}
}
Once you done that, the try catch will start behaving as expected on controller level.
However, I would even go further as DatabaseExeption shouldn't really go that far to controller. The alternative would be to use manual transaction inside of a service and do a try catch there.
Then in the Service layer transform database exception in a more generic exception with all necessary information for controllers to process.
And then you should catch that more generic exception (MyDatabaseAccessException) in the controller and transform as you wish for the sake of a presentation layer.
===
The #ControllerAdvice suggested here is good for a global exception handling across controllers.
The #ExceptionHandler is not suitable for each method unless you wnat to have controller per method. And even after that it can clash with global #ControllerAdvice.
I am not sure why spring doesn't allow #ExceptionHandler at a method level, it would simplify a lot of cases like yours.
My solution is to annotate a method with a marker:
#ExceptionHandler(SomeException.class)
public ResponseEntity<String> handleSomeException(SomeException e, HandlerMethod handlerMethod) {
var marker = AnnotatedElementUtils.findMergedAnnotation(handlerMethod.getMethod(), MarkerAnnotation.class);
if (marker != null) return something();
else return somethingElse();
}
I have created a custom exception and I have an exception mapper that I want to be invoked when my exception is thrown, so that I can return proper http return codes and messages, but for some reason it doesn't work for my exception.
Here is the relevant code
My Exception class:
public class MyException extends ApplicationRuntimeException
{
private static final long serialVersionUID = 1L;
public MyException ()
{
super();
}
public MyException (String message)
{
super(message);
}
public MyException (String message, ErrorCode errorCode)
{
super(message, errorCode);
}
public MyException (Throwable t)
{
super(t);
}
public MyException (String message, Throwable t)
{
super(message, t);
}
}
My exception mapper class
#Provider
public class MyExceptionMapper implements ExceptionMapper<Exception>
{
#Override
public Response toResponse(Exception exception)
{
//code
return response;
}
}
cxf-config
<jaxrs:server id="serviceId" address="/">
<jaxrs:providers>
<bean class="com.package.MyExceptionMapper"/>
</jaxrs:providers>
</jaxrs:server>
This is essentially what my code is like, is there anything I'm missing or any other information I need to provide in order to resolve this issue?
The exception and exception mapper are located in two different projects, but I don't think that should matter because they are both in the class path.
Thanks
If this is your full CXF, you are missing the service bean in this server being executed.
The provider is tied to the service and will only execute for the service beans also defined in the server.
Aside from that everything looks fine so I would check and make sure your exception is really being thrown.
The exception mapper just handle exceptions thrown by service beans.
One option in this case is to throw a new WebApplicationException(Throwable, Response) in which the response should be built the same way as what've been done in your exception mapper.
If you are trying to do some validation, creating a custom message body reader is one good option, I think. By doing this, inside the message body reader, you also be able to access to other providers/exception mappers.
Good luck!