3

I was developing a SpringMVC 3 trivial application, but I got stuck somewhere. Essentially, a model attribute whose fields are filled in the GET operation are returned NULL in the POST (even if I don't make any operation on them). I've checked on this and other forums and the only answer I came up with was to implement Editors for the classes I should put into the model, an initializer that could be used to register custom editors and make it available to the application (in the servletname-servlet.xml file). All operations that I did, but definitely no luck. I was wondering if someone out there could give me a hand. Thank you.

The following controller:

@Controller
@RequestMapping(value="/nourish")
public class NourishController {

private PetDAO pdao = new PetDAO();
private UserDAO udao = new UserDAO();
private FeedVirtualPet feedvp = new FeedVirtualPet();

@RequestMapping(method = RequestMethod.GET)
public String nourish(Model model, HttpServletRequest request){
    NourishPetDTO npdto = new NourishPetDTO();
    PetDTO      pdto=pdao.findPetByBusinessKey((PetDTO)request.getSession().getAttribute("pet"));
    npdto.setPet(pdto);
    npdto.setAmt(0);
    model.addAttribute("npdto", npdto);
    return "nourish";
}


@RequestMapping(method = RequestMethod.POST)
public String nourishPOST(@ModelAttribute("npdto") NourishPetDTO npdto,
        //BindingResult result,
        HttpServletRequest request){

    System.out.println("*****nourishPOST.npdto.amt: "+npdto.getAmt());
    System.out.println("*****nourishPOST.npdto.pet.petname: "+npdto.getPet().getPetName());
    System.out.println("*****nourishPOST.npdto.pet.hunger:     "+npdto.getPet().getHunger());
    PetDTO pdto = feedvp.feed(npdto.getPet(), npdto.getAmt());
    request.getSession().setAttribute("user", pdto.getOwner());
    return "redirect:detailPet";
}
}

has methods for both GET and POST operations, and is associated to the following jsp - in this view, all the model informations are correctly displayed through EL:

<%@ page language="java" contentType="text/html; charset=ISO-8859-1" session="true"  pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Nourish your pet!!</title>
</head>
<body>
Your stats for <h3>${npdto.pet.petName}</h3><br>
    Please note that any value you put inside will:
        <ol>
            <li>Subtract value to your current hunger level</li>
            <li>Add (value) to your current health level</li>
        </ol>
    Please note that any value you'll put will in many manners "resized":
    <ol>
        <li>It must be even. If not, a default 0 value will be  applied</li>
        <li>It can't be greater than 4. If it's greater, the maxium value  of 4 will be anyway considered.</li>
        <li>If it ain't a number, a default zero value will be passed</li>
    </ol>
<table>
    <tr><td>Health</td><td>${npdto.pet.health}</td></tr>
    <tr><td>Hunger</td><td>${npdto.pet.hunger}</td></tr>
</table>
<form action="nourish" method="post"  >
    nourishment: <input type="text" name="amt"/>
    <input type="submit" value="Nourish!"/>
</form>
</body>
</html>

(Please note how I didn't use the model attribute that's returning NULL, to be sure I wasn't doing anything to it)

The POST operations fail on the instruction

System.out.println("*****nourishPOST.npdto.pet.petname:"+npdto.getPet().getPetName());

as Tomcat returns a NullPointerException.

As aforementioned, I have been searching a solution to this problem, and everything I could find is to add Editor classes & register Editors to a binder. Result is still the same.

Anyway, these are the classes:

NourishPetEditor.java

public class NourishPetEditor extends PropertyEditorSupport {

private PetEditor pedit;

public PetEditor getPedit() {
    return pedit;
}


public NourishPetEditor() {
    // TODO Auto-generated constructor stub
    pedit =  new PetEditor();
}

@Override 
public String getAsText(){
    NourishPetDTO npdto= (NourishPetDTO)getValue();
    return super.getAsText()+","+npdto.getAmt();
}

public NourishPetDTO makeNourishPetDTOInstance(String [] parts){

    NourishPetDTO npdto = new NourishPetDTO(); 
    npdto.setPet(pedit.makePetDTOInstance(parts));
    npdto.setAmt(Integer.parseInt(parts[9]));
    return npdto;

}


public void setAsText(String key){
    String []parts = key.split(",");
    NourishPetDTO npdto = makeNourishPetDTOInstance(parts);
    setValue(npdto);
}
}

PetEditor.java

package com.virtualpet.dtoeditors;
import java.beans.PropertyEditorSupport;

import com.virtualpet.virtualpet_daos.PetDAO;  
import com.virtualpet.virtualpet_dtos.PetDTO;
import com.virtualpet.virtualpet_dtos.UserDTO;
public class PetEditor extends PropertyEditorSupport{

private PetDAO pdao;

public PetEditor() {
    // TODO Auto-generated constructor stub
}

public PetEditor(PetDAO pdao) {
    // TODO Auto-generated constructor stub
    this.pdao = pdao;

}


public String getAsText(){
    PetDTO pdto = (PetDTO) this.getValue();

    return pdto.getClass().getName()+","+ //0
    pdto.getPetName()+","+ //1
    pdto.getHealth()+","+  //2
    pdto.getHunger()+","+  //3
    pdto.getMood()+","+","+ //4
    pdto.getOwner().getClass().getName()+","+ //5
    pdto.getOwner().getUsername()+","+ //6
    pdto.getOwner().getEmail()+","+pdto.getOwner().getPassword(); //7,8

}

public void setAsText(String key) throws IllegalArgumentException {
    String []parts = key.split(",");
    PetDTO pdto = makePetDTOInstance(parts);
    setValue(pdto);
}


public UserDTO makeUserDTOInstance(String[] parts)
        throws InstantiationException, IllegalAccessException,
        ClassNotFoundException {

            UserDTO udto = (UserDTO)Class.forName(parts[5]).newInstance();
            udto.setUsername(parts[6]);
            udto.setEmail(parts[7]);
            udto.setPassword(parts[8]);
            return udto;
}


public PetDTO makePetDTOInstance(String[]parts){
    try{
        PetDTO pdto = (PetDTO) Class.forName(parts[0]).newInstance();
        pdto.setPetName(parts[1]);
        pdto.setHealth(Integer.parseInt(parts[2]));
        pdto.setHunger(Integer.parseInt(parts[3]));
        pdto.setMood(Integer.parseInt(parts[4]));

        UserDTO udto = makeUserDTOInstance(parts);

        pdto.setOwner(udto);
        return pdto;
    }
    catch (Exception e){
        throw new IllegalArgumentException();
    }
}
}

I'll spare you UserEditor, as it's pretty much similar to PetEditor. Finally, the Initializer to bind the custom editors & the Model Classes.

VirtualPetDTOInitializer.java

package com.virtualpet.dtoeditors;

import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.support.WebBindingInitializer;
import org.springframework.web.context.request.WebRequest;

import com.virtualpet.virtualpet_dtos.NourishPetDTO;
import com.virtualpet.virtualpet_dtos.PetDTO;
import com.virtualpet.virtualpet_dtos.UserDTO;

public class VirtualPetDTOInitializer implements WebBindingInitializer {

public void initBinder(WebDataBinder binder, WebRequest arg1) {
    // TODO Auto-generated method stub
        binder.registerCustomEditor(UserDTO.class, new UserEditor( ));
        binder.registerCustomEditor(PetDTO.class, new PetEditor( ));
        binder.registerCustomEditor(NourishPetDTO.class, new NourishPetEditor());
}
}

This class defines a property value in dispatcher-servlet.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans ...>
<bean id="userDao"
    class="com.virtualpet.virtualpet_daos.UserDAO"/>
<bean id="petDao"
    class="com.virtualpet.virtualpet_daos.PetDAO" />
<bean    class="org.springframwork.web.servlet.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
    <property name="webBindingInitializer">
        <bean class="com.virtualpet.dtoeditors.VirtualPetDTOInitializer"/>
    </property>
</bean>
</beans>

Being a total rookie on Spring MVC, I must tell you that this error is something I got even before I implemented these classes. So, looks like they're not a factor and yet, their implementation is everything I could find aboud model attribute returned null after POST. Could anyone please help? Thanks in advance.

  • make sure you have a mapping for `/detailPet` if you are redirecting to it from the `POST` – blurfus Apr 29 '15 at 22:02
  • there was already, in a controller called DetailPetController, that I didn't post for the sake of readability (and also, I had no problems with that controller). Thank you very much for your time – Luigi Iaderosa Apr 30 '15 at 07:23

1 Answers1

2

You need to do one of the following:

  1. Store the values of npdto in hidden form fields

  2. Store npdto in session

  3. Re-read npdto from the database in your post handler

You probably want #2, in which case add @SessionAttributes("npdto") on top of your Controller.

You should also add a SessionStatus parameter to your post handler, and call sessionStatus.complete() to clear the item from session when you don't need it any more.

See Spring MVC: Validation, Post-Redirect-Get, Partial Updates, Optimistic Concurrency, Field Security for a reference answer.

Community
  • 1
  • 1
Neil McGuigan
  • 46,580
  • 12
  • 123
  • 152
  • thank you! that was exactly what I was missing. Yet, something's puzzling me...what good is adding a variable or an object to the Model if I have to add that variable/model to SessionAttribute? I'm following a Spring Recipe manual, and it's ok if you want to learn "hands on", yet many things are left untold and that's one of those...Anyway I can at least move on. – Luigi Iaderosa Apr 30 '15 at 07:24
  • 1
    @LuigiIaderosa Model is just the data that your view needs for the current response. It doesn't care about the next request. That's what session is for. Model is useful to keep your view and data separate though. – Neil McGuigan Apr 30 '15 at 17:56