Spring initBinder for Handling Large List of Java Objects
Java developers face a slew of problems. Here's a look at probing a Spring MVC features that devs may require for apps.
Join the DZone community and get the full member experience.
Join For FreeIntroduction:
In this blog, I will be explaining one of the most common problems Java developers face while developing Spring MVC application, but invest lot of time and efforts understanding the issue and fixing it as any site does not easily state the solution.
My intention here is to provide an understanding of a feature of Spring MVC which developers may need to fine tune for their applications.
Scenario:
In any typical web application where we need to submit the data (which is filled by the user on the browser) from client side to server side.
In a Spring MVC based web application, all the data which is submitted from the UI comes to the Controller classes where the request is mapped.
Sometimes what may happen is request is not reaching the controller methods and in your AJAX calls you may get an HTTP 404 response in your error handling block.
Developers try to investigate these sort of issues by putting a debug on the controller method or request interceptors(if you have one) or filters ,check the data which is sent through AJAX,check request mappings etc.
But none of the above approaches helps them to resolve the problem because you are getting 404 on the browser and everything else seems to be working fine.
Now next step you may try is putting a breakpoint in the DispatcherServlet code as this is the first entry point java class invoked by the Spring framework before executing interceptors/filters and controllers in the chain.
For details on how can we put a breakpoint in Spring's Dispatcher Servlet refer http://www.coderanch.com/t/605222/vc/Eclipse-IDE-set-break-Spring
And finally you figure out you are getting some exception as below
org.springframework.beans.InvalidPropertyException :
Invalid property arraylist[256]' of bean class [packagename.ObjectClassName]:
Index of out of bounds in property path arraylist [256]';
nested exception is java.lang.IndexOutOfBoundsException: Index: 256, Size: 256
at org.springframework.beans.BeanWrapperImpl.getPropertyValue(BeanWrapperImpl.java:830)
at org.springframework.beans.BeanWrapperImpl.getNestedBeanWrapper(BeanWrapperImpl.java:574)
at org.springframework.beans.BeanWrapperImpl.getBeanW rapperForPropertyPath(BeanWrapperImpl.java:551)
at org.springframework.beans.BeanWrapperImpl.setPropertyValue(BeanWrapperImpl.java:911)
at org.springframework.beans.AbstractPropertyAccessor .setPropertyValues(AbstractPropertyAccessor.java:7 6)
at org.springframework.validation.DataBinder.applyPropertyValues(DataBinder.java:692)
at org.springframework.validation.DataBinder.doBind(DataBinder.java:588) at org.springframework.web.bind.WebDataBinder.doBind( WebDataBinder.java:191)
at org.springframework.web.bind.ServletRequestDataBinder.bind(ServletRequestDataBinder.java:111)
at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter$ServletHandlerMethodIn voker.doBind(AnnotationMethodHandlerAdapter.java:7 57)
at org.springframework.web.bind.annotation.support.Ha ndlerMethodInvoker.doBind(HandlerMethodInvoker.jav a:805)
at org.springframework.web.bind.annotation.support.HandlerMethodInvoker.resolveHandlerArguments(Handler MethodInvoker.java:359)
at org.springframework.web.bind.annotation.support.Ha dlerMethodInvoker.invokeHandlerMethod(HandlerMeth odInvoker.java:171)
at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.invokeHandlerMethod(An notationMethodHandlerAdapter.java:436)
at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.handle(AnnotationMethodHandlerAdapter.java:424)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:790)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:719)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:669) at org.springframework.web.servlet.FrameworkServlet.d oPost(FrameworkServlet.java:585) at javax.servlet.http.HttpServlet.service(HttpServlet.java:763) at javax.servlet.http.HttpServlet.service(HttpServlet .java:856) at com.ibm.ws.webcontainer.servlet.ServletWrapper.service(ServletWrapper.java:1213) at com.ibm.ws.webcontainer.servlet.ServletWrapper.service(ServletWrapper.java:1154) at com.ibm.ws.webcontainer.filter.WebAppFilterChain.doFilter(WebAppFilterChain.java:145) at org.springframework.web.filter.CharacterEncodingFi lter.doFilterInternal(CharacterEncodingFilter.java :88) at org.springframework.web.filter.OncePerRequestFilte r.doFilter(OncePerRequestFilter.java:76)
Looking at the exception you may figure out that Spring framework is trying to retrieve more than 255 objects and hence failing with IndexOutOfBoundsException.
You may be wondering why this is the case and how do I solve this?
Is this a problem with Spring framework or is the developer doing something wrong?
Well, the answer is no one is doing anything wrong and the framework is also working as intended.
These kinds of issues you may not face during initial development and testing because you will require large data sets to reproduce these kinds of issues.
Generally, you may face these issues in Data migration or pre-production environment where you have huge data sets.
Solution:
Before moving onto solution lets see why this happened and what is the root cause of the issue.
Issue is by default spring only maps only 255 objects to the java bean.
DataBinder.html # setAutoGrowCollectionLimit (int)
Spring developers has done this to prevent OutOfMemory problem.
The solution is very simple as below, in your controller class, you need to override init Binder method and configure the limit size which needs so that you can pass those many objects to your controller classes.
You have to be very careful while setting the max size as you may get OutOfMemory issues in your application if you set this value very high.
Just put some buffer while setting the size to say if you are passing 500 objects then you may configure it as 600 or 700 if it increases in future.
Also do not hardcode this value in java code externalize it in some property file or XML file.
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
/**
* @author Nitin Prabhu
*/
@Controller
public class Controller {
//Externalize this value
@Value("${myApplication.autoGrowCollectionLimit:600}")
private int autoGrowCollectionLimit;
@InitBinder
public void initBinder(WebDataBinder dataBinder) {
dataBinder.setAutoGrowCollectionLimit(autoGrowCollectionLimit);
}
}
@InitBinder plays the role to identify the methods which initialize the WebDataBinder. The @InitBinder method supports many arguments as by @RequestMapping methods.
@InitBinder method does not support command or form arguments. Mostly used argument is WebDataBinder in @InitBinder method.
WebDataBinder is a DataBinder that binds request parameter to JavaBean objects.
Hope this article helped you.
Opinions expressed by DZone contributors are their own.
Comments