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.