Time for the next answer in the series.
Fuzzy issue 3
The following code snippet below comes from a simple panel with a stateful Form Backing Object. Nothing can be wrong with this, can it? This is business as usual! Take a look at this implementation of showForm of the controller:
public class MyController extends ComponentController {
...
public ModelAndView showForm(HttpServletRequest request,
HttpServletResponse response, BindException errors, Map controlModel)
throws Exception {
ModelAndView modelAndView = super.showForm(request, response, errors, controlModel);
if (myFBO.getSelectedEntityId() != null) {
BeanUtils.copyProperties(myEntityManager.find(myFBO.getSelectedEntityId()), myFormBackingObject);
}
return modelAndView;
}
}
Question: what will happen here?
A. If one field in the form fails to bind or validate all entered or changed values by the editor are lost and the form reopens in the previous state. You wonder how this can happen since the form backing object is supposed to be stateful.
B. If one field in the form fails to bind or validate the onSubmit and showForm are invoked within the same request. You wonder how this can happen since WebManager is supposed to use the post and redirect pattern.
C. If one field in the form fails to bind or validate some properties are updated, some are not.
The correct answers are A and B. If one field in the form fails bind or validate, Spring will generate a "Binding error" which contains information about the binding error. Note that binding happens when a form is submitted, just after invoking the lifecycle method initBinder and just before onBindAndValidate (in which you can perform any additional custom binding). If binding fails, Spring will NOT invoke the onSubmit method but the showForm method instead (and so answer B is true). That makes sense, if binding fails there is nothing to submit. The showForm is invoked to render the resulting form which is supposed to show the binding error.
In the code above Spring will provide this binding error information via the "errors" parameter of the showForm method. But the code snippet above completely ignores any errors out there! It just copies properties from the Business object to the Form Backing Object as if nothing happened. Since the onSubmit has not been invoked, the Business object will still contain the old values and so the properties of Form Backing Object will be overwritten with those old values. And so answer A is true also.
What then? The cure is simple, just check if there are any binding errors and return the ModelAndView object directly if there are. This code snippet will do the trick:
public class MyController extends ComponentController {
...
public ModelAndView showForm(HttpServletRequest request,
HttpServletResponse response, BindException errors, Map controlModel)
throws Exception {
ModelAndView modelAndView = super.showForm(request, response, errors, controlModel);
if (errors.hasErrors()) {
return modelAndView;
}
if (myFBO.getSelectedEntityId() != null) {
BeanUtils.copyProperties(myEntityManager.find(myFBO.getSelectedEntityId()), myFormBackingObject);
}
return modelAndView;
}
}
Ivo Ladage is product architect and is part of one of the SCRUM-teams. Ivo has special interests in Workflow and Authorization processes and Spring MVC.
Other blog entries: