I have multiple classes annotated with #ControllerAdvice, each with an #ExceptionHandler method in.
One handles Exception with the intention that if no more specific handler is found, this should be used.
Sadly Spring MVC appears to be always using the most generic case (Exception) rather than more specific ones (IOException for example).
Is this how one would expect Spring MVC to behave? I'm trying to emulate a pattern from Jersey, which assesses each ExceptionMapper (equivalent component) to determine how far the declared type that it handles is from the exception that has been thrown, and always uses the nearest ancestor.
Is this how one would expect Spring MVC to behave?
As of Spring 4.3.7, here's how Spring MVC behaves: it uses HandlerExceptionResolver instances to handle exceptions thrown by handler methods.
By default, the web MVC configuration registers a single HandlerExceptionResolver bean, a HandlerExceptionResolverComposite, which
delegates to a list of other HandlerExceptionResolvers.
Those other resolvers are
ExceptionHandlerExceptionResolver
ResponseStatusExceptionResolver
DefaultHandlerExceptionResolver
registered in that order. For the purpose of this question we only care about ExceptionHandlerExceptionResolver.
An AbstractHandlerMethodExceptionResolver that resolves exceptions
through #ExceptionHandler methods.
At context initialization, Spring will generate a ControllerAdviceBean for each #ControllerAdvice annotated class it detects. The ExceptionHandlerExceptionResolver will retrieve these from the context, and sort them using using AnnotationAwareOrderComparator which
is an extension of OrderComparator that supports Spring's Ordered
interface as well as the #Order and #Priority annotations, with an
order value provided by an Ordered instance overriding a statically
defined annotation value (if any).
It'll then register an ExceptionHandlerMethodResolver for each of these ControllerAdviceBean instances (mapping available #ExceptionHandler methods to the exception types they're meant to handle). These are finally added in the same order to a LinkedHashMap (which preserves iteration order).
When an exception occurs, the ExceptionHandlerExceptionResolver will iterate through these ExceptionHandlerMethodResolver and use the first one that can handle the exception.
So the point here is: if you have a #ControllerAdvice with an #ExceptionHandler for Exception that gets registered before another #ControllerAdvice class with an #ExceptionHandler for a more specific exception, like IOException, that first one will get called. As mentioned earlier, you can control that registration order by having your #ControllerAdvice annotated class implement Ordered or annotating it with #Order or #Priority and giving it an appropriate value.
Sotirios Delimanolis was very helpful in his answer, on further investigation we found that, in spring 3.2.4 anyway, the code that looks for #ControllerAdvice annotations also checks for the presence of #Order annotations and sorts the list of ControllerAdviceBeans.
The resulting default order for all controllers without the #Order annotation is Ordered#LOWEST_PRECEDENCE which means if you have one controller that needs to be the lowest priority then ALL your controllers need to have a higher order.
Here's an example showing how to have two exception handler classes with ControllerAdvice and Order annotations that can serve appropriate responses when either a UserProfileException or RuntimeException occurs.
class UserProfileException extends RuntimeException {
}
#ControllerAdvice
#Order(Ordered.HIGHEST_PRECEDENCE)
class UserProfileExceptionHandler {
#ExceptionHandler(UserProfileException)
#ResponseBody
ResponseEntity<ErrorResponse> handleUserProfileException() {
....
}
}
#ControllerAdvice
#Order(Ordered.LOWEST_PRECEDENCE)
class DefaultExceptionHandler {
#ExceptionHandler(RuntimeException)
#ResponseBody
ResponseEntity<ErrorResponse> handleRuntimeException() {
....
}
}
See ControllerAdviceBean#initOrderFromBeanType()
See ControllerAdviceBean#findAnnotatedBeans()
See ExceptionHandlerExceptionResolver#initExceptionHandlerAdviceCache()
Enjoy!
The order of exception handlers can be changed using the #Order annotation.
For example:
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.web.bind.annotation.ControllerAdvice;
#ControllerAdvice
#Order(Ordered.HIGHEST_PRECEDENCE)
public class CustomExceptionHandler {
//...
}
#Order's value can be any integer.
I also found in the documentation that :
https://docs.spring.io/spring-framework/docs/4.3.4.RELEASE/javadoc-api/org/springframework/web/servlet/mvc/method/annotation/ExceptionHandlerExceptionResolver.html#getExceptionHandlerMethod-org.springframework.web.method.HandlerMethod-java.lang.Exception-
ExceptionHandlerMethod
protected ServletInvocableHandlerMethod
getExceptionHandlerMethod(HandlerMethod handlerMethod,
Exception exception)
Find an #ExceptionHandler method for the given
exception. The default implementation searches methods in the class
hierarchy of the controller first and if not found, it continues
searching for additional #ExceptionHandler methods assuming some
#ControllerAdvice Spring-managed beans were detected. Parameters:
handlerMethod - the method where the exception was raised (may be
null) exception - the raised exception Returns: a method to handle the
exception, or null
So this means that if you want to solve this issue, you will need to add your specific exception handler within the controller throwing those exception. ANd to define one and only ControllerAdvice handling the Global default exception handler.
This simplies the process and we don't need the Order annotation to handle the problem.
you can also use a number value, like below
#Order(value = 100)
Lower values have higher priority. The default value is * {#code
Ordered.LOWEST_PRECEDENCE},indicating lowest priority (losing to any
other * specified order value)
Important Class to be handled :
**#Order(Ordered.HIGHEST_PRECEDENCE)**
public class FunctionalResponseEntityExceptionHandler {
private final Logger logger = LoggerFactory.getLogger(FunctionalResponseEntityExceptionHandler.class);
#ExceptionHandler(EntityNotFoundException.class)
public final ResponseEntity<Object> handleFunctionalExceptions(EntityNotFoundException ex, WebRequest request)
{
logger.error(ex.getMessage() + " " + ex);
ExceptionResponse exceptionResponse= new ExceptionResponse(new Date(), ex.getMessage(),
request.getDescription(false),HttpStatus.NOT_FOUND.toString());
return new ResponseEntity<>(exceptionResponse, HttpStatus.NOT_FOUND);
}
}
Other Exceptions with Low priority
#ControllerAdvice
public class GlobalResponseEntityExceptionHandler extends ResponseEntityExceptionHandler
{
private final Logger logger = LoggerFactory.getLogger(GlobalResponseEntityExceptionHandler.class);
#ExceptionHandler(Exception.class)
public final ResponseEntity<Object> handleAllException(Exception ex, WebRequest request)
{
logger.error(ex.getMessage()+ " " + ex);
ExceptionResponse exceptionResponse= new ExceptionResponse(new Date(), ex.toString(),
request.getDescription(false),HttpStatus.INTERNAL_SERVER_ERROR.toString());
}
}
There's a similar situation convered in the excellent "Exception Handling in Spring MVC" post on the Spring blog, in the section entitled Global Exception Handling. Their scenario involves checking for ResponseStatus annotations registered on the exception class, and if present, rethrowing the exception to let the framework handle them. You might be able to use this general tactic - try to determine if there is a might be a more appropriate handler out there and rethrowing.
Alternatively, there's some other exception handling strategies covered that you might look at instead.
If you want separate your exception handlers(like me), you can use #Import to do this.
#ControllerAdvice
class MyCustomExceptionHandler {
...
}
#ControllerAdvice
class MyOtherCustomExceptionHandler {
...
}
#Import({MyCustomExceptionHandler.class,MyOtherCustomExceptionHandler.class})
#ControllerAdvice
#Order(Ordered.LOWEST_PRECEDENCE)
class ApplicationExceptionHandler{
//Generic exception handlers
}
Related
When using the kafkalistener annotation at class level and the provided errorhandler property is ignored. When method is annotated with kafkalistner and the provided errorhandler is working. Is it expected behavior?
This is really bug. The piece of code:
String errorHandlerBeanName = resolveExpressionAsString(kafkaListener.errorHandler(), "errorHandler");
if (StringUtils.hasText(errorHandlerBeanName)) {
endpoint.setErrorHandler(this.beanFactory.getBean(errorHandlerBeanName, KafkaListenerErrorHandler.class));
}
Is missed in the:
private void processMultiMethodListeners(Collection<KafkaListener> classLevelListeners, List<Method> multiMethods,
Object bean, String beanName) {
Unfortunately I don't see a simple way to workaround this. Please, consider to have a single #KafkaListener method with an Object as payload and manual type routing in that method to others.
Feel free to raise a GitHub issue on the matter!
I am using Spring Boot 1.5.3.RELEASE and using a Controller that takes a MultipartFile with some other information as arguments and returns a file.
Now I am facing the org.apache.tomcat.util.http.fileupload.FileUploadBase$SizeLimitExceededException when the file exceeds the maximum Sizes.
spring.http.multipart.maxFileSize=17728640
spring.http.multipart.maxRequestSize=17728640
This works well but i need a custom Response and actually the Exception is throwed only at server side before the method call.
Can anyone tell me how can I define a Custom Error Handler that handles this exception and response something like ResponseEntity.status(HttpStatus.CONFLICT).body("size_exceeded")
My Method:
#SuppressWarnings("rawtypes")
#RequestMapping(value = "/{Id}/attachments", method = RequestMethod.POST)
public ResponseEntity addTaskAttachment(#RequestParam("file") MultipartFile file, #PathVariable Long Id,
#CurrentUser User currentUser) {
// Some code here
ResponseEntity.status(HttpStatus.OK).body(attachmentAsByteArray);
}
You are correct in your observation that an Exception Handler with #RestControllerAdvice wouldn't work for multi part exceptions and reason being MultipartFile parsing & validation step preceding the mapping resolver step.
As advised in first accepted answer by geoand for this SO question here , you need to define and register an ErrorController.
Also, note that as already mentioned in that answer , Spring Boot already defines a BasicErrorController that you can extend to add new content types to return a JSON etc ( since default is text/html ) by adding a new public method with #RequestMapping & #Produces .
I have a method in my #Transaction enabled class which is getting invoked from outside class using java reflection. Unfortunately Spring Transaction is not working inside this method.
Class looks something like below:
#Transactional
public class CartServiceImpl implements CartService {
#Autowired
AnnotatedMethodInvoker annotatedMethodInvoker;
#Override
#VersionedParent
public BasicResponse addCartItem(AddCartItemRequest addCartItemRequest) throws Exception{
String currentMethodName = Thread.currentThread().getStackTrace()[1].getMethodName();
return (BasicResponse)annotatedMethodInvoker.invoke(this, currentMethodName, addCartItemRequest, AddCartItemRequest.class);
}
#VersionedMethod(parentMethod="addCartItem", minimumVersion=Constants.BUILD_VERSION_1_0_1, description="Added Epoch")
private BasicResponse addCartItemWithEpoch(AddCartItemRequest addCartItemRequest){
/***** Implementation detail goes here *****/
}
#VersionedMethod(parentMethod="addCartItem", minimumVersion=Constants.BUILD_VERSION_1_0_2, description="Added Cart filter here")
private BasicResponse addCartItemWithCartFilter(AddCartItemRequest addCartItemRequest){
/***** Implementation detail goes here *****/
}
}
In the AnnotatedMethodInvoker class's invoke() method I am invoking 1 of the two private methods addCartItemWithEpoch() & addCartItemWithCartFilter().
The problem is that when a RuntimeException is occurring, the Transaction is still getting committed, which essentially means that Spring Transaction is not working in my private method.
Surprizingly the below statement returns true if I put it inside my private method:
TransactionSynchronizationManager.isActualTransactionActive()
My application is currently using Spring AOP transaction throughout and it works seamlessly. application-config.xml snippet below:
<context:annotation-config />
<context:component-scan base-package="com.apraxalpha.dealernews" />
<tx:annotation-driven />
I have read this blog which suggests using AspectJ based Transaction over Spring AOP Transaction approach. I am not really confident about this, because my entire application is otherwise using Spring AOP based Transaction. now just for 1 single implementation change, I don't want to change everything to AspectJ transaction.
1) Is there any such middle path to have both the approaches in my application ?
2) Is there any other way to have Transaction in my private method getting invoked using java reflection?
Went through several other posts on similar problem :
1) Spring Transaction not working while calling private method of a Spring Bean class or
2) calling a method from another method in same class
Seems like either you use AspectJ instead of Spring AOP to call the bean class methods
OR
If you don't want to use AspectJ and stick to Spring AOP then a simple refatoring of the class will solve the problem as below:
In the question the method public method addCartItem and the actual implementation methods(which are actually having the implementation code) are all in the same bean class.
Consider creating a delegator class as a layer above this bean class and put the public method addCartItem in that class. Then make the both private methods as public and try to call the bean class methods from the delegator method.
Consider a class (an ORM entity):
public class MyEntity {
Long id;
MyOtherEntity assoc;
// ... getters and setters
}
I want for it to be bound automatically in a Spring MVC controller, something like that:
public ModelAndView method(HttpServletRequest request,
HttpServletResponse response, MyEntity command) {
}
It works well for simple properties like id, but for assoc it throws an exception NullValueInNestedPathException, since assoc wasn't instantiated by constructor. The question is, how can I tell ServletRequestDataBinder (or BeanWrapper or anything) to instantiate properties automatically as it makes its way through the nested property path?
I could of course make another class derived from MyEntity and put an instantiation in it, but then I won't be able to save it using simple Hibernate call, since derived class won't be mapped.
Ok, one way to do that in case of MultiActionController is to override newCommandObject; but I would like a more generic solution.
I have a Spring MVC controller with an action that's called using AJAX.
#SessionAttributes({"userContext"})
public class Controller
{
...
#RequestMapping(value = "/my-url", method= { RequestMethods.POST })
public ModelAndView doSomething(#ModelAttribute("userContext") UserContext context,
SessionStatus sessionStatus)
{
BusinessObject obj = doSomeBusinessLogic(context.getUserName());
sessionStatus.setComplete();
ModelAndView mav = new ModelAndView("jsonView");
mav.addObject("someInt", obj.getId());
return mav;
}
}
When I run this action, I get the following exception:
net.sf.json.JSONException: There is a cycle in the hierarchy!
at t.sf.json.util.CycleDetectionStrategy$StrictCycleDetectionStrategy.handleRepeatedReferenceAsObject(CycleDetectionStrategy.java:97)
at net.sf.json.JSONObject._fromBean(JSONObject.java:833)
at net.sf.json.JSONObject.fromObject(JSONObject.java:168)
at org.springframework.web.servlet.view.json.writer.jsonlib.PropertyEditorRegistryValueProcessor.processObjectValue(PropertyEditorRegistryValueProcessor.java:127)
at net.sf.json.JSONObject._fromMap(JSONObject.java:1334)
Truncated. see log file for complete stacktrace
After doing some debugging I found out that Spring is placing the UserContext object onto the ModelAndView that I am returning. If I hard-code my user name and remove the context object from the method's parameters, the action runs successfully. Is there a way to configure Spring to omit the ModelAttribute-annotated parameters from the returned ModelAndView? As you can see, sessionStatus.setComplete() has no effect.
I've had similar problems in the past with #SessionAttributes. By declaring #SessionAttributes({"userContext"}) you're telling Spring that you want "userContext" to always be available in the model, and so Spring has no choice but to send your UserContext object out to the model, just in case you're going to be redirecting or doing something else which might end up at another Controller.
The "solution" (and I didn't like it much, but it worked) was to omit the #SessionAttributes annotation on the controller, add an HttpSession parameter to the necessary methods and "manually" manage what's in it.
I'm interested to see if there's a better way, because it seems #SessionAttributes has tremendous potential to tidy up controller-level code.
I registered a WebArgumentResolver to get to my session variable. This allowed me to keep this session variable out of the response while keeping my action unit testable.
Along with #ModelAttribute, pass #ModelMap as a method argument.
Based on business logic, error conditions -- if you do not need the attribute for certain scenarios, then remove it from the map.
public ModelAndView foo(#ModelAttribute("userContext") UserContext, #ModelMap map){
if(success){
return success.jsp
}
else{
map.remove("userContext");
return "error.jsp"
}
}
Not totally satisfied with having to pass the ModelMap as well, but I did not find any other easier way of doing it.
Cheers!!