How to handle SizeLimitExceededException from CommonsMultipartResolver in Spring WebFlow? - spring-webflow

I have the following situation. I have a CommonsMultipartResolver bean configured the following way.
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="2100000" />
And I have a few file upload fields in a Spring Web Flow view state jsp.
Everything works fine if the file is under the limit, but if the file exceeds the limit of 2MB-s I have to add a validation error to the binding result on my form.
My problem is that the multipart file resolver throws a org.apache.commons.fileupload.FileUploadBase.SizeL imitExceededException exception when the file limit is exceeded and I can't find a way to catch this in Spring Web Flow and add my FieldError to the form.
I tried using the on-exception attribute of the transition tag, but if I understand correctly it only works for exceptions that are thrown within Spring Web Flow.
I've also tried to use SimpleMappingExceptionResolver in spring mvc, but I do not want to redirect to a page, I want to handle this exception.
I also found this: https://jira.springsource.org/browse/SWF-158
But it's from version 1.0 and I'm assuming that this has been incorporated since or that a better way was found to handle these situations.
Any ideas on how to deal with this would be greatly appreciated.
Thanks.

In your SimpleMappingExceptionResolver you should be able to override the resolveException method, determine the exception type being caught and handle appropriately.
I've found some old code in our project that seems to be a solution to a similar exception;
public class GeneralMappingExceptionResolver extends SimpleMappingExceptionResolver {
#Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception exception) {
if(exception instanceof MaxUploadSizeExceededException) {
MaxUploadSizeExceededException maxe = (MaxUploadSizeExceededException)exception;
String errorMessage = "Max filesize exceeded, please ensure filesize is too large.");
HashMap<String, Object> model = new HashMap<String, Object>(2);
model.put("errorMessage", errorMessage);
return new ModelAndView("verification/psv/consent", model);
} else {
return super.resolveException(request, response, handler, exception); // Do whatever default behaviour is (ie throw to error page).
}
}
Note that the "verification/psv/consent" is the flow where this exception would have been thrown from and where it needs to return to. We only have the one page that has a file upload.
Obviously the errorMessage is just a parameter passed into the view so will need to be handled and displayed like an error message. You may also need to re-populate any other form fields that were submitted. Hopefully this is a point in the right direction though.

Related

Spring REST Exception Handling FileUploadBase$SizeLimitExceededException

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 .

How to call JSP from a java Controller

I have a simple J2EE application with Spring.
Now I want from Java controller call another page. For example, I'm in registrazione.jsp, I click on one button, and I call a method in registrazioneController.java.
Now I want from registrazioneController.java, call another page, for example
home.jsp and I want pass any parameter in get.
It is possible?
this is the method that I use when I click the button
registrazioenControlle.java
public ModelAndView internalLoadPage(HttpServletRequest request, HttpServletResponse response, Map model) throws Exception
{
//to do
//call another page for example home.html
request.getRequestDispatcher("home.jsp").forward(request, response);
return new ModelAndView("home", model);
}
I'm try to use this code but no found.
In addition to the answer provided in the comments, you can also use RedirectAttributes, and addAttribute method if you want to append a parameter to URL upon redirect. This will also give you the addFlashAttribute that will store the attributes in the flash scope which will make it available to the redirected page. You can also return a simple string as the view name e.g. like
public String internalLoadPage(RedirectAttributes redirectAttributes) throws Exception
{
redirectAttributes.addAttribute("paramterKey", "parameter");
redirectAttributes.addFlashAttribute("pageKey", "pageAttribute");
return "redirect:/home";
}
this assumes that the view suffix is configured in you view resolver configuration

Spring ExceptionHandler access to Model?

I have a spring controller which uses bean validation to validate input values, for example to save a topic:
public String save(#ModelAttribute("topic") #Valid Topic topic
This works nicely.
In my data layer i also use bean validation and when something invalid is send to the data layer a ConstraintViolationException is thrown. I catch this one in my controller with a exceptionhandler:
#ExceptionHandler({ ConstraintViolationException.class })
public ModelAndView handleValidationException(HttpServletRequest req,ConstraintViolationException e) {
This also works nicely.
The problem i'm facing is i can't access the model in my exceptionhandler, the docs clearly states this:
Important Note: the Model may not be a parameter of any #ExceptionHandler method.
Access to the model is needed cause i want to show the user the view with the form he just filled in. But because i can't access the model i can't get the filled out form data.
One solution i found is catching the ConstraintViolationException in my save() method, cause here i do have access to the model.
But i would prefer a generic solution and not have messy try/catch/finally when i need to save something to the database.
Does anybody know a good solution?
[edit]
Here's my current exception handler:
#ExceptionHandler({ ConstraintViolationException.class })
public ModelAndView handleValidationException(HttpServletRequest req,ConstraintViolationException e) {
log.debug("handleValidationException");
//no access to model, so i create new one, but this new one doesn't have the values the user entered in the html form.
ModelAndView mav = new ModelAndView();
mav.addObject("exception", e);
mav.setViewName("add_topc");
return mav;
}
After some thoughts on this subject i came to the conclusion that a single error page displaying a error is enough. Mainly because the errors thrown by the data layer are fatal errors that can't be corrected by the user, for example by changing field values. It of course would have been nice to display the error page in the context of where things did go wrong and save the users input. But that doesn't seem to be possible with spring.
A bit old but I found myself in a similar situation.
One possible solution would be to wrap the model in the contraintViolationException such that the exception handler could pull the model from the exception.
I haven't found any good explanation of why the model is cleared/not available in the exception handler - except someone who hypothesized that it was by design as the model could be in an invalid state.
SNIPET from Controller
...
throw new ConstraintViolationException(model);
SNIPET from ModelAwareException
public void ConstraintViolationException(Model saveModel) {
this.model = saveModel;
}
SNIPET from ExceptionHandler
#ExceptionHandler({ ConstraintViolationException.class })
public ModelAndView handleValidationException(HttpServletRequest req,ConstraintViolationException e, Model unpopulatedSpringModel) {
unpopulatedSpringModel = e.getModel();
return new ModelAndView("yourpage");
}

Setting Precedence of Multiple #ControllerAdvice #ExceptionHandlers

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
}

How do I omit ModelAttribute parameters from the ModelAndView returned by a Spring 3 MVC action?

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!!

Resources