views:

3731

answers:

2

Hi all,

I'm new to Spring MVC, but not new to web development in Java. I'm attempting to create a simple form->controller example.

I have a form, a form controller (configured in a context XML pasted below) and my model (a simple bean). When I submit the form the value of my text input is always null, regardless. Any ideas?

Form controller spring configuration:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"&gt;

    <!-- controllers -->

    <bean name="/home.html" class="atc.web.view.controller.HomeController" />

    <!--bean name="/mirror.html" class="atc.web.view.controller.MirrorController" -->

    <bean name="/url-cache.html" class="atc.web.view.controller.URLCacheFormController">
     <property name="synchronizeOnSession" value="true" />
     <!--property name="sessionForm" value="false"-->
     <property name="commandName" value="urlForm"/>
     <property name="commandClass" value="atc.web.view.model.URLBean"/>
     <property name="validator">
      <bean class="atc.web.view.validators.URLValidator"/>
     </property>
     <property name="formView" value="mirror"/>
     <property name="successView" value="url-cache.html"/>
     <property name="URLCachingService" ref="urlCachingService" />
    </bean>

    <!-- end controllers -->

    <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
     <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
     <property name="prefix" value="/WEB-INF/jsp/" />
     <property name="suffix" value=".jsp" />
    </bean>

    <!-- custom beans -->

    <bean id="breakcrumbInjectionFilter" class="atc.web.view.filter.BreadcrumbInjectionFilter">
     <property name="contextPrefix" value="/home-web" />
    </bean>

    <!-- end custom beans -->
</beans>

The JSP that contains my form is as follows:

<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ include file="/WEB-INF/jspf/core/taglibs.jspf" %>
<html>
<head><title>Simple tools</title></head>
<style>
.error s {
 color:#FF0000;
}
</style>
<body>
<%@ include file="/WEB-INF/jspf/nav/nav.jspf" %>
    Errors: <form:errors path="url" cssClass="error"/>
    <form:form method="post" commandName="urlForm">
     <form:input path="url" />
     <input type="submit" align="center" value="Execute" />
    </form:form>
</body>
</html>

Here's the full source of my controller:

public class URLCacheFormController extends SimpleFormController {
    private URLCachingService cachingService;

    private static Logger log = Logger.getLogger(URLCacheFormController.class);

    public ModelAndView onSubmit(Object command) throws ServletException {
     log.debug(String.format("URLCachingFormController received request with object '%s'", command));
     URLBean urlBean = (URLBean) command;
     try {
      URL url = new URL(urlBean.getUrl());
      URLCache cache = cachingService.cacheURL(url);
      cache.cacheToTempFile();

     } catch (IOException e) {
      log.error("Invalid URL...", e);
     }
     return new ModelAndView(new RedirectView(getSuccessView()));
    }

    protected Object formBackingObject(HttpServletRequest request) throws ServletException {
     log.debug("formBackingObject() ");
     return new URLBean();
    }

    public void setURLCachingService(URLCachingService cachingService) {
     this.cachingService = cachingService;
    }
}

All of the above produces the following HTML:

<html>
<head></head>
<body>
<div><span><a href="home.html">Home </a></span><span> | <a href="url-cache.html">Page Mirror</a></span></div>
<div id="breadcrumb"><span>Your trail:<a href=""/></span></div>Attrs:<div/>
<form id="urlForm" method="post" action="/home-web/url-cache.html">
<input id="url" type="text" value="" name="url"/>
<input type="submit" align="center" value="Execute"/>
</form>
</body>
</html>

I'm now overriding doSubmitAction(Object command) but I still do not hit the method. The form submits but the next thing I know I'm presented with the blank form (after formBackingObject(HttpServletRequest request) is called).

That's to say, when I submit, the logging call on line 1 of doSubmitAction in the form controller is never executed. The validator executes, and fails (adds error messages correctly) because the value it's checking is always null (or put correctly, it's never set). The call to formBackingObject always occurs however. The request attribute of my form (the form input 'url') is always null.

Update: OK, so after some serious debugging as suggested by serg555, and removing validation, I can confirm the issue seems to be with mapping the request parameters - such as the value of 'url' from the form - to my command/bean; i.e. the URL is never being set on my bean, or the bean is being recreated.

Please help?

+1  A: 

I don't know where is the problem, let me show you my controller that works and maybe you will be able to figure out what's wrong:

public class TestController extends SimpleFormController  {

    public TestController () {
     setFormView("testView");
     setSuccessView("testView");
     setCommandClass(TestCmd.class);
     setCommandName("cmd");
    }

    protected Object formBackingObject(HttpServletRequest request)throws Exception {
     Object o = super.formBackingObject(request);
     TestCmd cmd = (TestCmd) o;

     return cmd;
    }


    public ModelAndView onSubmit(HttpServletRequest request, HttpServletResponse response, Object obj, BindException errors) throws Exception {

     TestCmd cmd = (TestCmd) obj;

     log.debug("test value = " + cmd.getTestValue());

     return super.onSubmit(request, response, obj, errors);
    }

}

Form:

<form method="post" action="/test.html">
    <form:input path="cmd.testValue"/>
    <input type="submit">
</form>

Inside App-servlet.xml:

    <bean id="urlMapping"
     class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
     <property name="alwaysUseFullPath" value="true" />
     <property name="mappings">
      <props>
       <prop key="/test.html">testController</prop>
      </props>
     </property>
    </bean>
    <bean id="testController"
     class="com.sample.TestController">
     <property name="synchronizeOnSession" value="true" />
     <property name="validator">
      <bean class="com.sample.TestValidator"/>
     </property>
    </bean>

Validator:

public class TestValidator implements Validator {

    public boolean supports(Class clazz) {
     return (TestCmd.class.equals(clazz));
    }

    public void validate(Object obj, Errors errors) {
     TestCmd cmd = (TestCmd) obj;
     if (obj == null) {
      // nothing
     } else {

      ValidationUtils.rejectIfEmptyOrWhitespace(errors, "testValue", null, "<b>Value</b> is required");

     }
    }

}

Command:

public class TestCmd {

    private String testValue;

    public TestCmd() {
    }

    public String getTestValue() {
     return testValue;
    }

    public void setTestValue(String testValue) {
     this.testValue = testValue;
    }

}
serg
The bean was incorrectly defined - the property setter had a return type other than void. Thanks for the help.
atc
+1  A: 

OK, you've verified that a url set within the controller does come through to the JSP, which suggests that the mapping is correct to the command bean.

One more thing. If the "onSubmit" code is never being reached, that may be a problem with the controller figuring out whether it's a form submission. It might help to add a name attribute to the submit button, and override "isFormSubmission" to check whether that name is present (i.e. whether the submit button has been clicked). If so, isFormSubmission should return true. (This shouldn't be necessary -- by default the controller assumes that any POST request is a form submission -- but if it helps you'll know more about the problem, and you'll have a work-around fix, at least.)

JacobM
Tried that and it seems the value never changes. If I set its initial value to "http://google.com" and then change it on the form and submit, the amended value isn't present: only "http://google.com" as set previously.
atc
OK, but that does suggest, at least, that the mapping is working from the form backing object to the field on the JSP, so that's a start.Can you post the code for your command bean?
JacobM
I changed my answer above, so please read it again. :)
JacobM
I've confirmed that the onSubmit is executing, it's purely the mapping of the form values to the command/bean - the URL field on my bean is never being set. WTF?
atc
OK, next things to look at: 1) your command bean -- are the getters and setters correct, including capitalization, etc. 2) look at the generated HTML for your form and look at the name attribute on the textbox generated by the form:input. Then debug and look at the request parameters when you hit the onSubmit method. Is there a parameter with the name given to the textbox?
JacobM
It was the setter - for some reason I'd returned this and can't remember why. The bean wasn't a true bean - grr!Thanks for the help.
atc