KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > springframework > web > servlet > mvc > AbstractFormController


1 /*
2  * Copyright 2002-2006 the original author or authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16
17 package org.springframework.web.servlet.mvc;
18
19 import java.util.Map JavaDoc;
20
21 import javax.servlet.ServletException JavaDoc;
22 import javax.servlet.http.HttpServletRequest JavaDoc;
23 import javax.servlet.http.HttpServletResponse JavaDoc;
24 import javax.servlet.http.HttpSession JavaDoc;
25
26 import org.springframework.validation.BindException;
27 import org.springframework.validation.Errors;
28 import org.springframework.web.HttpSessionRequiredException;
29 import org.springframework.web.bind.ServletRequestDataBinder;
30 import org.springframework.web.servlet.ModelAndView;
31
32 /**
33  * <p>Form controller that auto-populates a form bean from the request.
34  * This, either using a new bean instance per request, or using the same bean
35  * when the <code>sessionForm</code> property has been set to <code>true</code>.</p>
36  *
37  * <p>This class is the base class for both framework subclasses like
38  * {@link SimpleFormController SimpleFormController} and
39  * {@link AbstractWizardFormController AbstractWizardFormController}, and
40  * custom form controllers you can provide yourself.</p>
41  *
42  * <p>Both form-input views and after-submission views have to be provided
43  * programmatically. To provide those views using configuration properties,
44  * use the {@link SimpleFormController SimpleFormController}.</p>
45  *
46  * <p>Subclasses need to override <code>showForm</code> to prepare the form view,
47  * and <code>processFormSubmission</code> to handle submit requests. For the latter,
48  * binding errors like type mismatches will be reported via the given "errors" holder.
49  * For additional custom form validation, a validator (property inherited from
50  * BaseCommandController) can be used, reporting via the same "errors" instance.</p>
51  *
52  * <p>Comparing this Controller to the Struts notion of the <code>Action</code>
53  * shows us that with Spring, you can use any ordinary JavaBeans or database-
54  * backed JavaBeans without having to implement a framework-specific class
55  * (like Struts' <code>ActionForm</code>). More complex properties of JavaBeans
56  * (Dates, Locales, but also your own application-specific or compound types)
57  * can be represented and submitted to the controller, by using the notion of
58  * a <code>java.beans.PropertyEditor</code>. For more information on that
59  * subject, see the workflow of this controller and the explanation of the
60  * {@link BaseCommandController BaseCommandController}.</p>
61  *
62  * <p><b><a name="workflow">Workflow
63  * (<a HREF="BaseCommandController.html#workflow">and that defined by superclass</a>):</b><br>
64  * <ol>
65  * <li><b>The controller receives a request for a new form (typically a GET).</b></li>
66  * <li>Call to {@link #formBackingObject formBackingObject()} which by default,
67  * returns an instance of the commandClass that has been configured
68  * (see the properties the superclass exposes), but can also be overridden
69  * to e.g. retrieve an object from the database (that needs to be modified
70  * using the form).</li>
71  * <li>Call to {@link #initBinder initBinder()} which allows you to register
72  * custom editors for certain fields (often properties of non-primitive
73  * or non-String types) of the command class. This will render appropriate
74  * Strings for those property values, e.g. locale-specific date strings.</li>
75  * <li><em>Only if <code>bindOnNewForm</code> is set to <code>true</code></em>, then
76  * {@link org.springframework.web.bind.ServletRequestDataBinder ServletRequestDataBinder}
77  * gets applied to populate the new form object with initial request parameters and the
78  * {@link #onBindOnNewForm(HttpServletRequest, Object, BindException)} callback method is
79  * called. <em>Note:</em> any defined Validators are not applied at this point, to allow
80  * partial binding. However be aware that any Binder customizations applied via
81  * initBinder() (such as
82  * {@link org.springframework.validation.DataBinder#setRequiredFields(String[])} will
83  * still apply. As such, if using bindOnNewForm=true and initBinder() customizations are
84  * used to validate fields instead of using Validators, in the case that only some fields
85  * will be populated for the new form, there will potentially be some bind errors for
86  * missing fields in the errors object. Any view (JSP, etc.) that displays binder errors
87  * needs to be intelligent and for this case take into account whether it is displaying the
88  * initial form view or subsequent post results, skipping error display for the former.</li>
89  * <li>Call to {@link #showForm(HttpServletRequest, HttpServletResponse, BindException) showForm()}
90  * to return a View that should be rendered (typically the view that renders
91  * the form). This method has to be implemented in subclasses.</li>
92  * <li>The showForm() implementation will call {@link #referenceData referenceData()},
93  * which you can implement to provide any relevant reference data you might need
94  * when editing a form (e.g. a List of Locale objects you're going to let the
95  * user select one from).</li>
96  * <li>Model gets exposed and view gets rendered, to let the user fill in the form.</li>
97  * <li><b>The controller receives a form submission (typically a POST).</b>
98  * To use a different way of detecting a form submission, override the
99  * {@link #isFormSubmission isFormSubmission} method.
100  * </li>
101  * <li>If <code>sessionForm</code> is not set, {@link #formBackingObject formBackingObject()}
102  * is called to retrieve a form object. Otherwise, the controller tries to
103  * find the command object which is already bound in the session. If it cannot
104  * find the object, it does a call to {@link #handleInvalidSubmit handleInvalidSubmit}
105  * which - by default - tries to create a new form object and resubmit the form.</li>
106  * <li>The {@link org.springframework.web.bind.ServletRequestDataBinder ServletRequestDataBinder}
107  * gets applied to populate the form object with current request parameters.
108  * <li>Call to {@link #onBind onBind(HttpServletRequest, Object, Errors)} which allows
109  * you to do custom processing after binding but before validation (e.g. to manually
110  * bind request parameters to bean properties, to be seen by the Validator).</li>
111  * <li>If <code>validateOnBinding</code> is set, a registered Validator will be invoked.
112  * The Validator will check the form object properties, and register corresponding
113  * errors via the given {@link org.springframework.validation.Errors Errors}</li> object.
114  * <li>Call to {@link #onBindAndValidate onBindAndValidate()} which allows you
115  * to do custom processing after binding and validation (e.g. to manually
116  * bind request parameters, and to validate them outside a Validator).</li>
117  * <li>Call {@link #processFormSubmission(HttpServletRequest, HttpServletResponse,
118  * Object, BindException) processFormSubmission()} to process the submission, with
119  * or without binding errors. This method has to be implemented in subclasses.</li>
120  * </ol>
121  * </p>
122  *
123  * <p>In session form mode, a submission without an existing form object in the
124  * session is considered invalid, like in case of a resubmit/reload by the browser.
125  * The {@link #handleInvalidSubmit handleInvalidSubmit} method is invoked then,
126  * by default trying to resubmit. It can be overridden in subclasses to show
127  * corresponding messages or to redirect to a new form, in order to avoid duplicate
128  * submissions. The form object in the session can be considered a transaction
129  * token in that case.</p>
130  *
131  * <p>Note that views should never retrieve form beans from the session but always
132  * from the request, as prepared by the form controller. Remember that some view
133  * technologies like Velocity cannot even access a HTTP session.</p>
134  *
135  * <p><b><a name="config">Exposed configuration properties</a>
136  * (<a HREF="BaseCommandController.html#config">and those defined by superclass</a>):</b><br>
137  * <table border="1">
138  * <tr>
139  * <td><b>name</b></td>
140  * <td><b>default</b></td>
141  * <td><b>description</b></td>
142  * </tr>
143  * <tr>
144  * <td>bindOnNewForm</td>
145  * <td>false</td>
146  * <td>Indicates whether to bind servlet request parameters when
147  * creating a new form. Otherwise, the parameters will only be
148  * bound on form submission attempts.</td>
149  * </tr>
150  * <tr>
151  * <td>sessionForm</td>
152  * <td>false</td>
153  * <td>Indicates whether the form object should be kept in the session
154  * when a user asks for a new form. This allows you e.g. to retrieve
155  * an object from the database, let the user edit it, and then persist
156  * it again. Otherwise, a new command object will be created for each
157  * request (even when showing the form again after validation errors).</td>
158  * </tr>
159  * </table>
160  * </p>
161  *
162  * @author Rod Johnson
163  * @author Juergen Hoeller
164  * @author Alef Arendsen
165  * @author Rob Harrop
166  * @author Colin Sampaleanu
167  * @see #showForm(HttpServletRequest, HttpServletResponse, BindException)
168  * @see #processFormSubmission
169  * @see SimpleFormController
170  * @see AbstractWizardFormController
171  */

172 public abstract class AbstractFormController extends BaseCommandController {
173
174     private boolean bindOnNewForm = false;
175
176     private boolean sessionForm = false;
177
178
179     /**
180      * Create a new AbstractFormController.
181      * <p>Subclasses should set the following properties, either in the constructor
182      * or via a BeanFactory: commandName, commandClass, bindOnNewForm, sessionForm.
183      * Note that "commandClass" doesn't need to be set when overriding
184      * {@link #formBackingObject}, since the latter determines the class anyway.
185      * <p>"cacheSeconds" is by default set to 0 (-> no caching for all form controllers).
186      * @see #setCommandName
187      * @see #setCommandClass
188      * @see #setBindOnNewForm
189      * @see #setSessionForm
190      * @see #formBackingObject
191      */

192     public AbstractFormController() {
193         setCacheSeconds(0);
194     }
195
196     /**
197      * Set if request parameters should be bound to the form object
198      * in case of a non-submitting request, i.e. a new form.
199      */

200     public final void setBindOnNewForm(boolean bindOnNewForm) {
201         this.bindOnNewForm = bindOnNewForm;
202     }
203
204     /**
205      * Return if request parameters should be bound in case of a new form.
206      */

207     public final boolean isBindOnNewForm() {
208         return bindOnNewForm;
209     }
210
211     /**
212      * Activate resp. deactivate session form mode. In session form mode,
213      * the form is stored in the session to keep the form object instance
214      * between requests, instead of creating a new one on each request.
215      * <p>This is necessary for either wizard-style controllers that populate a
216      * single form object from multiple pages, or forms that populate a persistent
217      * object that needs to be identical to allow for tracking changes.
218      */

219     public final void setSessionForm(boolean sessionForm) {
220         this.sessionForm = sessionForm;
221     }
222
223     /**
224      * Return if session form mode is activated.
225      */

226     public final boolean isSessionForm() {
227         return sessionForm;
228     }
229
230
231     /**
232      * Handles two cases: form submissions and showing a new form.
233      * Delegates the decision between the two to {@link #isFormSubmission},
234      * always treating requests without existing form session attribute
235      * as new form when using session form mode.
236      * @see #isFormSubmission
237      * @see #showNewForm
238      * @see #processFormSubmission
239      */

240     protected ModelAndView handleRequestInternal(HttpServletRequest JavaDoc request, HttpServletResponse JavaDoc response)
241             throws Exception JavaDoc {
242
243         // Form submission or new form to show?
244
if (isFormSubmission(request)) {
245             // Fetch form object from HTTP session, bind, validate, process submission.
246
try {
247                 Object JavaDoc command = getCommand(request);
248                 ServletRequestDataBinder binder = bindAndValidate(request, command);
249                 BindException errors = new BindException(binder.getBindingResult());
250                 return processFormSubmission(request, response, command, errors);
251             }
252             catch (HttpSessionRequiredException ex) {
253                 // Cannot submit a session form if no form object is in the session.
254
if (logger.isDebugEnabled()) {
255                     logger.debug("Invalid submit detected: " + ex.getMessage());
256                 }
257                 return handleInvalidSubmit(request, response);
258             }
259         }
260
261         else {
262             // New form to show: render form view.
263
return showNewForm(request, response);
264         }
265     }
266
267     /**
268      * Determine if the given request represents a form submission.
269      * <p>The default implementation treats a POST request as form submission.
270      * Note: If the form session attribute doesn't exist when using session form
271      * mode, the request is always treated as new form by handleRequestInternal.
272      * <p>Subclasses can override this to use a custom strategy, e.g. a specific
273      * request parameter (assumably a hidden field or submit button name).
274      * @param request current HTTP request
275      * @return if the request represents a form submission
276      */

277     protected boolean isFormSubmission(HttpServletRequest JavaDoc request) {
278         return "POST".equals(request.getMethod());
279     }
280
281     /**
282      * Return the name of the HttpSession attribute that holds the form object
283      * for this form controller.
284      * <p>The default implementation delegates to the {@link #getFormSessionAttributeName()}
285      * variant without arguments.
286      * @param request current HTTP request
287      * @return the name of the form session attribute, or <code>null</code> if not in session form mode
288      * @see #getFormSessionAttributeName
289      * @see javax.servlet.http.HttpSession#getAttribute
290      */

291     protected String JavaDoc getFormSessionAttributeName(HttpServletRequest JavaDoc request) {
292         return getFormSessionAttributeName();
293     }
294
295     /**
296      * Return the name of the HttpSession attribute that holds the form object
297      * for this form controller.
298      * <p>Default is an internal name, of no relevance to applications, as the form
299      * session attribute is not usually accessed directly. Can be overridden to use
300      * an application-specific attribute name, which allows other code to access
301      * the session attribute directly.
302      * @return the name of the form session attribute
303      * @see javax.servlet.http.HttpSession#getAttribute
304      */

305     protected String JavaDoc getFormSessionAttributeName() {
306         return getClass().getName() + ".FORM." + getCommandName();
307     }
308
309
310     /**
311      * Show a new form. Prepares a backing object for the current form
312      * and the given request, including checking its validity.
313      * @param request current HTTP request
314      * @param response current HTTP response
315      * @return the prepared form view
316      * @throws Exception in case of an invalid new form object
317      * @see #getErrorsForNewForm
318      */

319     protected final ModelAndView showNewForm(HttpServletRequest JavaDoc request, HttpServletResponse JavaDoc response)
320             throws Exception JavaDoc {
321
322         logger.debug("Displaying new form");
323         return showForm(request, response, getErrorsForNewForm(request));
324     }
325
326     /**
327      * Create a BindException instance for a new form.
328      * Called by {@link #showNewForm}.
329      * <p>Can be used directly when intending to show a new form but with
330      * special errors registered on it (for example, on invalid submit).
331      * Usually, the resulting BindException will be passed to
332      * {@link #showForm(HttpServletRequest, HttpServletResponse, BindException)},
333      * after registering the errors on it.
334      * @param request current HTTP request
335      * @return the BindException instance
336      * @throws Exception in case of an invalid new form object
337      * @see #showNewForm
338      * @see #showForm(HttpServletRequest, HttpServletResponse, BindException)
339      * @see #handleInvalidSubmit
340      */

341     protected final BindException getErrorsForNewForm(HttpServletRequest JavaDoc request) throws Exception JavaDoc {
342         // Create form-backing object for new form.
343
Object JavaDoc command = formBackingObject(request);
344         if (command == null) {
345             throw new ServletException JavaDoc("Form object returned by formBackingObject() must not be null");
346         }
347         if (!checkCommand(command)) {
348             throw new ServletException JavaDoc("Form object returned by formBackingObject() must match commandClass");
349         }
350
351         // Bind without validation, to allow for prepopulating a form, and for
352
// convenient error evaluation in views (on both first attempt and resubmit).
353
ServletRequestDataBinder binder = createBinder(request, command);
354         BindException errors = new BindException(binder.getBindingResult());
355         if (isBindOnNewForm()) {
356             logger.debug("Binding to new form");
357             binder.bind(request);
358             onBindOnNewForm(request, command, errors);
359         }
360
361         // Return BindException object that resulted from binding.
362
return errors;
363     }
364
365     /**
366      * Callback for custom post-processing in terms of binding for a new form.
367      * Called when preparing a new form if <code>bindOnNewForm</code> is <code>true</code>.
368      * <p>The default implementation delegates to <code>onBindOnNewForm(request, command)</code>.
369      * @param request current HTTP request
370      * @param command the command object to perform further binding on
371      * @param errors validation errors holder, allowing for additional
372      * custom registration of binding errors
373      * @throws Exception in case of invalid state or arguments
374      * @see #onBindOnNewForm(javax.servlet.http.HttpServletRequest, Object)
375      * @see #setBindOnNewForm
376      */

377     protected void onBindOnNewForm(HttpServletRequest JavaDoc request, Object JavaDoc command, BindException errors)
378             throws Exception JavaDoc {
379
380         onBindOnNewForm(request, command);
381     }
382
383     /**
384      * Callback for custom post-processing in terms of binding for a new form.
385      * <p>Called by the default implementation of the
386      * {@link #onBindOnNewForm(HttpServletRequest, Object, BindException)} variant
387      * with all parameters, after standard binding when displaying the form view.
388      * Only called if <code>bindOnNewForm</code> is set to <code>true</code>.
389      * <p>The default implementation is empty.
390      * @param request current HTTP request
391      * @param command the command object to perform further binding on
392      * @throws Exception in case of invalid state or arguments
393      * @see #onBindOnNewForm(HttpServletRequest, Object, BindException)
394      * @see #setBindOnNewForm(boolean)
395      */

396     protected void onBindOnNewForm(HttpServletRequest JavaDoc request, Object JavaDoc command) throws Exception JavaDoc {
397     }
398
399
400     /**
401      * Return the form object for the given request.
402      * <p>Calls {@link #formBackingObject} if not in session form mode.
403      * Else, retrieves the form object from the session. Note that the form object
404      * gets removed from the session, but it will be re-added when showing the
405      * form for resubmission.
406      * @param request current HTTP request
407      * @return object form to bind onto
408      * @throws org.springframework.web.HttpSessionRequiredException
409      * if a session was expected but no active session (or session form object) found
410      * @throws Exception in case of invalid state or arguments
411      * @see #formBackingObject
412      */

413     protected final Object JavaDoc getCommand(HttpServletRequest JavaDoc request) throws Exception JavaDoc {
414         // If not in session-form mode, create a new form-backing object.
415
if (!isSessionForm()) {
416             return formBackingObject(request);
417         }
418
419         // Session-form mode: retrieve form object from HTTP session attribute.
420
HttpSession JavaDoc session = request.getSession(false);
421         if (session == null) {
422             throw new HttpSessionRequiredException("Must have session when trying to bind (in session-form mode)");
423         }
424         String JavaDoc formAttrName = getFormSessionAttributeName(request);
425         Object JavaDoc sessionFormObject = session.getAttribute(formAttrName);
426         if (sessionFormObject == null) {
427             throw new HttpSessionRequiredException("Form object not found in session (in session-form mode)");
428         }
429
430         // Remove form object from HTTP session: we might finish the form workflow
431
// in this request. If it turns out that we need to show the form view again,
432
// we'll re-bind the form object to the HTTP session.
433
if (logger.isDebugEnabled()) {
434             logger.debug("Removing form session attribute [" + formAttrName + "]");
435         }
436         session.removeAttribute(formAttrName);
437
438         return currentFormObject(request, sessionFormObject);
439     }
440
441     /**
442      * Retrieve a backing object for the current form from the given request.
443      * <p>The properties of the form object will correspond to the form field values
444      * in your form view. This object will be exposed in the model under the specified
445      * command name, to be accessed under that name in the view: for example, with
446      * a "spring:bind" tag. The default command name is "command".
447      * <p>Note that you need to activate session form mode to reuse the form-backing
448      * object across the entire form workflow. Else, a new instance of the command
449      * class will be created for each submission attempt, just using this backing
450      * object as template for the initial form.
451      * <p>The default implementation calls {@link #createCommand()},
452      * creating a new empty instance of the specified command class.
453      * Subclasses can override this to provide a preinitialized backing object.
454      * @param request current HTTP request
455      * @return the backing object
456      * @throws Exception in case of invalid state or arguments
457      * @see #setCommandName
458      * @see #setCommandClass
459      * @see #createCommand
460      */

461     protected Object JavaDoc formBackingObject(HttpServletRequest JavaDoc request) throws Exception JavaDoc {
462         return createCommand();
463     }
464
465     /**
466      * Return the current form object to use for binding and further processing,
467      * based on the passed-in form object as found in the HttpSession.
468      * <p>The default implementation simply returns the session form object as-is.
469      * Subclasses can override this to post-process the session form object,
470      * for example reattaching it to a persistence manager.
471      * @param sessionFormObject the form object retrieved from the HttpSession
472      * @return the form object to use for binding and further processing
473      * @throws Exception in case of invalid state or arguments
474      */

475     protected Object JavaDoc currentFormObject(HttpServletRequest JavaDoc request, Object JavaDoc sessionFormObject) throws Exception JavaDoc {
476         return sessionFormObject;
477     }
478
479
480     /**
481      * Prepare the form model and view, including reference and error data.
482      * Can show a configured form page, or generate a form view programmatically.
483      * <p>A typical implementation will call
484      * <code>showForm(request, errors, "myView")</code>
485      * to prepare the form view for a specific view name, returning the
486      * ModelAndView provided there.
487      * <p>For building a custom ModelAndView, call <code>errors.getModel()</code>
488      * to populate the ModelAndView model with the command and the Errors instance,
489      * under the specified command name, as expected by the "spring:bind" tag.
490      * You also need to include the model returned by {@link #referenceData}.
491      * <p>Note: If you decide to have a "formView" property specifying the
492      * view name, consider using SimpleFormController.
493      * @param request current HTTP request
494      * @param response current HTTP response
495      * @param errors validation errors holder
496      * @return the prepared form view, or <code>null</code> if handled directly
497      * @throws Exception in case of invalid state or arguments
498      * @see #showForm(HttpServletRequest, BindException, String)
499      * @see org.springframework.validation.Errors
500      * @see org.springframework.validation.BindException#getModel
501      * @see #referenceData(HttpServletRequest, Object, Errors)
502      * @see SimpleFormController#setFormView
503      */

504     protected abstract ModelAndView showForm(
505             HttpServletRequest JavaDoc request, HttpServletResponse JavaDoc response, BindException errors)
506             throws Exception JavaDoc;
507
508     /**
509      * Prepare model and view for the given form, including reference and errors.
510      * <p>In session form mode: Re-puts the form object in the session when
511      * returning to the form, as it has been removed by getCommand.
512      * <p>Can be used in subclasses to redirect back to a specific form page.
513      * @param request current HTTP request
514      * @param errors validation errors holder
515      * @param viewName name of the form view
516      * @return the prepared form view
517      * @throws Exception in case of invalid state or arguments
518      */

519     protected final ModelAndView showForm(HttpServletRequest JavaDoc request, BindException errors, String JavaDoc viewName)
520             throws Exception JavaDoc {
521
522         return showForm(request, errors, viewName, null);
523     }
524
525     /**
526      * Prepare model and view for the given form, including reference and errors,
527      * adding a controller-specific control model.
528      * <p>In session form mode: Re-puts the form object in the session when returning
529      * to the form, as it has been removed by getCommand.
530      * <p>Can be used in subclasses to redirect back to a specific form page.
531      * @param request current HTTP request
532      * @param errors validation errors holder
533      * @param viewName name of the form view
534      * @param controlModel model map containing controller-specific control data
535      * (e.g. current page in wizard-style controllers or special error message)
536      * @return the prepared form view
537      * @throws Exception in case of invalid state or arguments
538      */

539     protected final ModelAndView showForm(
540             HttpServletRequest JavaDoc request, BindException errors, String JavaDoc viewName, Map JavaDoc controlModel)
541             throws Exception JavaDoc {
542
543         // In session form mode, re-expose form object as HTTP session attribute.
544
// Re-binding is necessary for proper state handling in a cluster,
545
// to notify other nodes of changes in the form object.
546
if (isSessionForm()) {
547             String JavaDoc formAttrName = getFormSessionAttributeName(request);
548             if (logger.isDebugEnabled()) {
549                 logger.debug("Setting form session attribute [" + formAttrName + "] to: " + errors.getTarget());
550             }
551             request.getSession().setAttribute(formAttrName, errors.getTarget());
552         }
553
554         // Fetch errors model as starting point, containing form object under
555
// "commandName", and corresponding Errors instance under internal key.
556
Map JavaDoc model = errors.getModel();
557
558         // Merge reference data into model, if any.
559
Map JavaDoc referenceData = referenceData(request, errors.getTarget(), errors);
560         if (referenceData != null) {
561             model.putAll(referenceData);
562         }
563
564         // Merge control attributes into model, if any.
565
if (controlModel != null) {
566             model.putAll(controlModel);
567         }
568
569         // Trigger rendering of the specified view, using the final model.
570
return new ModelAndView(viewName, model);
571     }
572
573     /**
574      * Create a reference data map for the given request, consisting of
575      * bean name/bean instance pairs as expected by ModelAndView.
576      * <p>The default implementation returns <code>null</code>.
577      * Subclasses can override this to set reference data used in the view.
578      * @param request current HTTP request
579      * @param command form object with request parameters bound onto it
580      * @param errors validation errors holder
581      * @return a Map with reference data entries, or <code>null</code> if none
582      * @throws Exception in case of invalid state or arguments
583      * @see ModelAndView
584      */

585     protected Map JavaDoc referenceData(HttpServletRequest JavaDoc request, Object JavaDoc command, Errors errors) throws Exception JavaDoc {
586         return null;
587     }
588
589
590     /**
591      * Process form submission request. Called by {@link #handleRequestInternal}
592      * in case of a form submission, with or without binding errors. Implementations
593      * need to proceed properly, typically showing a form view in case of binding
594      * errors or performing a submit action else.
595      * <p>Subclasses can implement this to provide custom submission handling like
596      * triggering a custom action. They can also provide custom validation and call
597      * {@link #showForm(HttpServletRequest, HttpServletResponse, BindException)}
598      * or proceed with the submission accordingly.
599      * <p>For a success view, call <code>errors.getModel()</code> to populate the
600      * ModelAndView model with the command and the Errors instance, under the
601      * specified command name, as expected by the "spring:bind" tag. For a form view,
602      * simply return the ModelAndView object provided by
603      * {@link #showForm(HttpServletRequest, HttpServletResponse, BindException)}.
604      * @param request current servlet request
605      * @param response current servlet response
606      * @param command form object with request parameters bound onto it
607      * @param errors holder without errors (subclass can add errors if it wants to)
608      * @return the prepared model and view, or <code>null</code>
609      * @throws Exception in case of errors
610      * @see #handleRequestInternal
611      * @see #isFormSubmission
612      * @see #showForm(HttpServletRequest, HttpServletResponse, BindException)
613      * @see org.springframework.validation.Errors
614      * @see org.springframework.validation.BindException#getModel
615      */

616     protected abstract ModelAndView processFormSubmission(
617             HttpServletRequest JavaDoc request, HttpServletResponse JavaDoc response, Object JavaDoc command, BindException errors)
618             throws Exception JavaDoc;
619
620     /**
621      * Handle an invalid submit request, e.g. when in session form mode but no form object
622      * was found in the session (like in case of an invalid resubmit by the browser).
623      * <p>The default implementation simply tries to resubmit the form with a new
624      * form object. This should also work if the user hit the back button, changed
625      * some form data, and resubmitted the form.
626      * <p>Note: To avoid duplicate submissions, you need to override this method.
627      * Either show some "invalid submit" message, or call {@link #showNewForm} for
628      * resetting the form (prepopulating it with the current values if "bindOnNewForm"
629      * is true). In this case, the form object in the session serves as transaction token.
630      * <pre>
631      * protected ModelAndView handleInvalidSubmit(HttpServletRequest request, HttpServletResponse response) throws Exception {
632      * return showNewForm(request, response);
633      * }</pre>
634      * You can also show a new form but with special errors registered on it:
635      * <pre>
636      * protected ModelAndView handleInvalidSubmit(HttpServletRequest request, HttpServletResponse response) throws Exception {
637      * BindException errors = getErrorsForNewForm(request);
638      * errors.reject("duplicateFormSubmission", "Duplicate form submission");
639      * return showForm(request, response, errors);
640      * }</pre>
641      * @param request current HTTP request
642      * @param response current HTTP response
643      * @return a prepared view, or <code>null</code> if handled directly
644      * @throws Exception in case of errors
645      * @see #showNewForm
646      * @see #getErrorsForNewForm
647      * @see #showForm(HttpServletRequest, HttpServletResponse, BindException)
648      * @see #setBindOnNewForm
649      */

650     protected ModelAndView handleInvalidSubmit(HttpServletRequest JavaDoc request, HttpServletResponse JavaDoc response)
651             throws Exception JavaDoc {
652
653         Object JavaDoc command = formBackingObject(request);
654         ServletRequestDataBinder binder = bindAndValidate(request, command);
655         BindException errors = new BindException(binder.getBindingResult());
656         return processFormSubmission(request, response, command, errors);
657     }
658
659 }
660
Popular Tags