4

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

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

9
  • You don't show how you've mapped the URL to the controller. Do you have that in your app context? Perhaps you should update your question to show that as well. Commented Jun 27, 2009 at 15:51
  • The URL mapping is defined in the spring context XML that I've already provided. I've added the rest of the context XML for clarity, however. Commented Jun 27, 2009 at 16:29
  • This isn't an answer to your question, but you may find it easier to use Spring 2.5-style annotated controllers instead of the old FormCintroller hierarchy. It's considerably simpler and more intuitive. Commented Jun 27, 2009 at 16:31
  • Try to add <property name="synchronizeOnSession" value="true" /> to your controller definition. Also you need onSubmit(), why you switched to doSubmitAction()? Check your generated html of the form, maybe something is there. Also check what comes in the request inside onSubmit() method. Commented Jun 27, 2009 at 16:56
  • @serg555, I've moved back to onSubmit() and I've set synchronizeOnSession to true. The value is still null - the default value defined in the bean. I've also added my implementation of formBackingObject Commented Jun 27, 2009 at 18:17

5 Answers 5

2

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;
    }

}
Sign up to request clarification or add additional context in comments.

1 Comment

The bean was incorrectly defined - the property setter had a return type other than void. Thanks for the help.
1

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.)

5 Comments

Tried that and it seems the value never changes. If I set its initial value to "google.com" and then change it on the form and submit, the amended value isn't present: only "google.com" as set previously.
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?
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?
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?
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.
1

The cause was the fact that my form bean was not adhering to the Java Bean contract - the setter returned instead of being void and thus couldn't be matched by Spring to invoke.

1 Comment

That helped me Thank you! I was missing setters in my class
0

Try to put the following code in the aplication-context.xml. This could be your solution if you are having your form encType=multipart/form-data.

<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="10000000"/>
</bean>

Also try to remove the content Type from the jsp page. Well in my case this helped me...!!

2 Comments

This is not relevant at all. I've since added a post describing the fix (which was buried in comments).
Dude struggled due to this issue for like 2 days thus i put it here so if at all anybody is struggling with the same issue he might get some help. The answer might be irrelevant as I'm new to stackoverflow.(See my reputation ;))
0

I once spent a lot of time investigating a similar issue. Finally I found the culprit inside a Binder's initialization method:

@InitBinder
void allowFields(final WebDataBinder binder) {
    binder.setAllowedFields("name");
}

This method sets a restriction on fields that are allowed for binding. And all the other fields stay unbound, naturally resulting in null values.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.