Spring Form Validation Error: java.lang.IllegalArgumentException: 'items' must not be null

Chandra Prakash picture Chandra Prakash · Jun 26, 2014 · Viewed 28.4k times · Source

I am trying to Validate Spring form which includes:

  1. Normal input fields
  2. Radio buttons whose options are set in controller (later may come from database)
  3. Check box whose options are also set in controller
  4. Select field which is also set in controller

Method 1 approach is followed.

Model:

public class Student {
    private Long id;

    @NotEmpty
    private String name;

    @NotEmpty
    private String address;

    @NotEmpty
    private String contactNumber;

    @NotEmpty
    private String grade;

    @NotEmpty
    private String gender;

    @NotEmpty
    @Email
    private String email;

    @NotEmpty
    private List<String> extraSubjects = new ArrayList<String>();

    /****  Corresponding getter setters ****/

}

Controller:-

@Controller
@RequestMapping("/student")
public class StudentController {
    private Logger logger = Logger.getLogger(StudentController.class);

    @RequestMapping("/home")
    public String getHomePage() {
        return "home";
    }

    @RequestMapping(value = "/add", method = RequestMethod.GET)
    public String addStudentForm(Model model) {
        Student student = new Student();
        model.addAttribute("studentForm", student);

        LinkedHashMap<Integer, String> gradeList = new LinkedHashMap<Integer, String>();
        gradeList.put(1, "Class 1");
        gradeList.put(2, "Class 2");
        gradeList.put(3, "Class 3");
        gradeList.put(4, "Class 4");
        gradeList.put(5, "Class 5");
        model.addAttribute("gradeList", gradeList);

        List<String> genderList = new ArrayList<String>();
        genderList.add("Male");
        genderList.add("Female");
        model.addAttribute("genderList", genderList);
        student.setGender("Male");

        List<String> extraSubjects = new ArrayList<String>();
        extraSubjects.add("Math II");
        extraSubjects.add("Population");

        List<String> extraSubjectsStd = new ArrayList<String>();
        extraSubjectsStd.add("Math II");
        extraSubjectsStd.add("Population");
        student.setExtraSubjects(extraSubjectsStd);

        extraSubjects.add("Computer");
        model.addAttribute("extraSubjects", extraSubjects);
        return "addstudent";
    }

    @RequestMapping(value = "/add", method = RequestMethod.POST)
    public String addStudent(
            @Valid @ModelAttribute("studentForm") Student student,
            BindingResult result) {

        if (result.hasErrors()) {
            logger.info("Returning add page with errors");
            return "addstudent";
        }
            //Stuffs to save in database 
        return "redirect:add";
    }
}

JSP Page:-

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://www.springframework.org/tags" prefix="spring"%>
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Insert title here</title>
<style>
.error {
    color: #ff0000;
}

.errorblock {
    color: #000;
    background-color: #ffEEEE;
    border: 3px solid #ff0000;
    padding: 8px;
    margin: 16px;
}
</style>
</head>
<body>
    <form:form action="add" method="post" commandName="studentForm">
        <form:errors path="*" cssClass="errorblock" element="div" />
        <table border="0">
            <tr>
                <td colspan="3" align="center"><h3>Add Student</h3></td>
            </tr>
            <tr>
                <td><form:label path="name">
                        <spring:message code="label.studentname" />
                    </form:label></td>
                <td><form:input path="name" /></td>
                <td><form:errors path="name" cssClass="error" /></td>
            </tr>
            <tr>
                <td><form:label path="address">
                        <spring:message code="label.address" />
                    </form:label></td>
                <td><form:input path="address" /></td>
                <td><form:errors path="address" cssClass="error" />
            </tr>
            <tr>
                <td>Contact Number:</td>
                <td><form:input path="contactNumber" /></td>
                <td><form:errors path="contactNumber" cssClass="error" />
            </tr>
            <tr>
                <td><form:label path="grade">
                        <spring:message code="label.grade" />
                    </form:label></td>
                <td><form:select path="grade">
                        <form:option value="" label="--- Select ---" />
                        <form:options items="${gradeList}" />
                    </form:select></td>
                <td><form:errors path="grade" cssClass="error" />
            </tr>
            <tr>
                <td>Gender:</td>
                <td><form:radiobuttons path="gender" items="${genderList}" />
                </td>
                <td><form:errors path="gender" cssClass="error" />
            </tr>
            <tr>
                <td>Extra Subjects:</td>
                <td><form:checkboxes items="${extraSubjects}"
                        path="extraSubjects" /></td>
                <td><form:errors path="extraSubjects" cssClass="error" />
            </tr>
            <tr>
                <td>Email Address:</td>
                <td><form:input items="${email}" path="email" /></td>
                <td><form:errors path="email" cssClass="error" />
            </tr>
            <tr>
                <td></td>
                <td align="center"><input type="submit" value="Save" /></td>
                <td></td>
            </tr>
        </table>
    </form:form>
</body>
</html>

Now, When I run my application, the forms get loaded while I perform get operation. The value in the radio buttons, select fields, and checkboxes are loaded and selected as well. If I submit my form with no validation errors it works fine but when I send my form with validation errors, I get following errors:

StackTrace:-

Jun 26, 2014 4:32:00 PM org.apache.catalina.core.ApplicationDispatcher invoke
SEVERE: Servlet.service() for servlet jsp threw exception
java.lang.IllegalArgumentException: 'items' must not be null
    at org.springframework.util.Assert.notNull(Assert.java:112)
    at org.springframework.web.servlet.tags.form.AbstractMultiCheckedElementTag.setItems(AbstractMultiCheckedElementTag.java:85)
    at org.apache.jsp.WEB_002dINF.pages.addstudent_jsp._jspx_meth_form_005fradiobuttons_005f0(addstudent_jsp.java:810)
    at org.apache.jsp.WEB_002dINF.pages.addstudent_jsp._jspService(addstudent_jsp.java:360)
    at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
    at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:432)
    at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:390)
    at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:334)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:748)
    at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:486)
    at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:411)
    at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:338)
    at org.springframework.web.servlet.view.InternalResourceView.renderMergedOutputModel(InternalResourceView.java:209)
    at org.springframework.web.servlet.view.AbstractView.render(AbstractView.java:266)
    at org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1225)
    at org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1012)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:959)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:876)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:931)
    at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:833)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:646)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:807)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:501)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:170)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:98)
    at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:950)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1040)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:607)
    at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:313)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at java.lang.Thread.run(Unknown Source)

DEBUG org.springframework.web.servlet.DispatcherServlet- Error rendering view [org.springframework.web.servlet.view.JstlView: name 'addstudent'; URL [/WEB-INF/pages/addstudent.jsp]] in DispatcherServlet with name 'mvc-dispatcher'
org.apache.jasper.JasperException: An exception occurred processing JSP page /WEB-INF/pages/addstudent.jsp at line 62

59:             </tr>
60:             <tr>
61:                 <td>Gender:</td>
62:                 <td><form:radiobuttons path="gender" items="${genderList}" />
63:                 </td>
64:                 <td><form:errors path="gender" cssClass="error" />
65:             </tr>    

Stacktrace:
        at org.apache.jasper.servlet.JspServletWrapper.handleJspException(JspServletWrapper.java:568)
        at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:470)
        at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:390)
        at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:334)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
        at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
        at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:748)
        at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:486)
        at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:411)
        at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:338)
        at org.springframework.web.servlet.view.InternalResourceView.renderMergedOutputModel(InternalResourceView.java:209)
        at org.springframework.web.servlet.view.AbstractView.render(AbstractView.java:266)
        at org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1225)
        at org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1012)
        at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:959)
        at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:876)
        at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:931)
        at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:833)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:646)
        at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:807)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
        at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220)
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122)
        at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:501)
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:170)
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:98)
        at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:950)
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
        at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1040)
        at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:607)
        at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:313)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
        at java.lang.Thread.run(Unknown Source)
    Caused by: java.lang.IllegalArgumentException: 'items' must not be null
        at org.springframework.util.Assert.notNull(Assert.java:112)
        at org.springframework.web.servlet.tags.form.AbstractMultiCheckedElementTag.setItems(AbstractMultiCheckedElementTag.java:85)
        at org.apache.jsp.WEB_002dINF.pages.addstudent_jsp._jspx_meth_form_005fradiobuttons_005f0(addstudent_jsp.java:810)
        at org.apache.jsp.WEB_002dINF.pages.addstudent_jsp._jspService(addstudent_jsp.java:360)
        at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
        at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:432)
        ... 42 more
    Jun 26, 2014 4:32:00 PM org.apache.catalina.core.StandardWrapperValve invoke
    SEVERE: Servlet.service() for servlet [mvc-dispatcher] in context with path [/sms] threw exception [An exception occurred processing JSP page /WEB-INF/pages/addstudent.jsp at line 62

    59:             </tr>
    60:             <tr>
    61:                 <td>Gender:</td>
    62:                 <td><form:radiobuttons path="gender" items="${genderList}" />
    63:                 </td>
    64:                 <td><form:errors path="gender" cssClass="error" />
    65:             </tr>


    Stacktrace:] with root cause
    java.lang.IllegalArgumentException: 'items' must not be null
        at org.springframework.util.Assert.notNull(Assert.java:112)
        at org.springframework.web.servlet.tags.form.AbstractMultiCheckedElementTag.setItems(AbstractMultiCheckedElementTag.java:85)
        at org.apache.jsp.WEB_002dINF.pages.addstudent_jsp._jspx_meth_form_005fradiobuttons_005f0(addstudent_jsp.java:810)
        at org.apache.jsp.WEB_002dINF.pages.addstudent_jsp._jspService(addstudent_jsp.java:360)
        at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
        at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:432)
        at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:390)
        at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:334)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
        at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
        at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:748)
        at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:486)
        at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:411)
        at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:338)
        at org.springframework.web.servlet.view.InternalResourceView.renderMergedOutputModel(InternalResourceView.java:209)
        at org.springframework.web.servlet.view.AbstractView.render(AbstractView.java:266)
        at org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1225)
        at org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1012)
        at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:959)
        at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:876)
        at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:931)
        at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:833)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:646)
        at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:807)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
        at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220)
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122)
        at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:501)
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:170)
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:98)
        at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:950)
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
        at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1040)
        at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:607)
        at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:313)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
        at java.lang.Thread.run(Unknown Source)

I am able to figure out that the List that I have used for specifying genders, gradeList, extraSubjects are appear to be NULL after post request.

  • How can I render those values again in form after post if validation fails?
  • Should I have to set the value in lists on POST method too as in GET method?

Answer

Serge Ballesta picture Serge Ballesta · Jun 30, 2014

In you get part, you populate you model, not only with studentForm but also with genderList, gradeList and extraSubjects. And you use all in your jsp.

In the post part, you have a studentForm initialized by the parameters in the request. But your never initialize the lists. So in the JSP, ${gradeList}, ${genderList} and ${extraSubjects} and all null, so the error.

You have to initialize the three lists in addStudent before forwarding to the view if validation fails. You can do that either as proposed by Neil McGuigan, ie by having spring to automatically populate the model, or by populating it yourself. For example, you could have :

void initLists(Model model) {
    LinkedHashMap<Integer, String> gradeList = new LinkedHashMap<Integer, String>();
    gradeList.put(1, "Class 1");
    gradeList.put(2, "Class 2");
    gradeList.put(3, "Class 3");
    gradeList.put(4, "Class 4");
    gradeList.put(5, "Class 5");
    model.addAttribute("gradeList", gradeList);

    List<String> genderList = new ArrayList<String>();
    genderList.add("Male");
    genderList.add("Female");
    model.addAttribute("genderList", genderList);

    List<String> extraSubjects = new ArrayList<String>();
    extraSubjects.add("Math II");
    extraSubjects.add("Population");
    extraSubjects.add("Computer");
    model.addAttribute("extraSubjects", extraSubjects);
}

and call initLists both in addStudentForm and in addStudent. Example code for last :

@RequestMapping(value = "/add", method = RequestMethod.POST)
public String addStudent(
        @Valid @ModelAttribute("studentForm") Student student,
        BindingResult result, Model model) {

    if (result.hasErrors()) {
        logger.info("Returning add page with errors");
        initLists(model);
        return "addstudent";
    }
        //Stuffs to save in database 
    return "redirect:add";
}

Of course, all list initialization should be removed from addStudentForm once you call initLists from there ...