KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > springframework > web > portlet > mvc > SimpleFormController


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.portlet.mvc;
18
19 import java.util.Map JavaDoc;
20
21 import javax.portlet.ActionRequest;
22 import javax.portlet.ActionResponse;
23 import javax.portlet.PortletException;
24 import javax.portlet.PortletRequest;
25 import javax.portlet.RenderRequest;
26 import javax.portlet.RenderResponse;
27
28 import org.springframework.validation.BindException;
29 import org.springframework.validation.Errors;
30 import org.springframework.web.portlet.ModelAndView;
31
32 /**
33  * <p>Concrete FormController implementation that provides configurable
34  * form and success views, and an onSubmit chain for convenient overriding.
35  * Automatically resubmits to the form view in case of validation errors,
36  * and renders the success view in case of a valid submission.</p>
37  *
38  * <p>The workflow of this Controller does not differ much from the one described
39  * in the {@link AbstractFormController AbstractFormController}. The difference
40  * is that you do not need to implement {@link #showForm showForm},
41  * {@link #processFormSubmission processFormSubmission}, and
42  * {@link #renderFormSubmission renderFormSubmission}: A form view and a
43  * success view can be configured declaratively.</p>
44  *
45  * <p>This controller is different from it's servlet counterpart in that it must take
46  * into account the two phases of a portlet request: the action phase and the render
47  * phase. See the JSR-168 spec for more details on these two phases.
48  * Be especially aware that the action phase is called only once, but that the
49  * render phase will be called repeatedly by the portal -- it does this every time
50  * the page containing the portlet is updated, even if the activity is in some other
51  * portlet. The main difference in the methods in this class is that the
52  * <code>onSubmit</code> methods have all been split into <code>onSubmitAction</code>
53  * and <code>onSubmitRender</code> to account for the two phases.</p>
54  *
55  * <p><b><a name="workflow">Workflow
56  * (<a HREF="AbstractFormController.html#workflow">in addition to the superclass</a>):</b><br>
57  * <ol>
58  * <li>Call to {@link #processFormSubmission processFormSubmission} which inspects
59  * the {@link org.springframework.validation.Errors Errors} object to see if
60  * any errors have occurred during binding and validation.</li>
61  * <li>If errors occured, the controller will return the configured formView,
62  * showing the form again (possibly rendering according error messages).</li>
63  * <li>If {@link #isFormChangeRequest isFormChangeRequest} is overridden and returns
64  * true for the given request, the controller will return the formView too.
65  * In that case, the controller will also suppress validation. Before returning the formView,
66  * the controller will invoke {@link #onFormChange}, giving sub-classes a chance
67  * to make modification to the command object.
68  * This is intended for requests that change the structure of the form,
69  * which should not cause validation and show the form in any case.</li>
70  * <li>If no errors occurred, the controller will call
71  * {@link #onSubmitAction(ActionRequest, ActionResponse, Object, BindException) onSubmitAction}
72  * during the action phase and then {@link #onSubmitRender(RenderRequest, RenderResponse,
73         * Object, BindException) onSubmitRender} during the render phase, which in case of the
74  * default implementation delegate to {@link #onSubmitAction(Object, BindException)
75  * onSubmitAction} and {@link #onSubmitRender(Object, BindException) onSubmitRender}
76  * with just the command object.
77  * The default implementation of the latter method will return the configured
78  * <code>successView</code>. Consider just implementing {@link #doSubmitAction doSubmitAction}
79  * for simply performing a submit action during the action phase and then rendering
80  * the success view during the render phase.</li>
81  * </ol>
82  * </p>
83  *
84  * <p>The submit behavior can be customized by overriding one of the
85  * {@link #onSubmitAction onSubmitAction} or {@link #onSubmitRender onSubmitRender}
86  * methods. Submit actions can also perform custom validation if necessary
87  * (typically database-driven checks), calling {@link #showForm(RenderRequest,
88         * RenderResponse, BindException) showForm} in case of validation errors to show
89  * the form view again. You do not have to override both the <code>onSubmitAction</code> and
90  * <code>onSubmitRender</code> methods at a given level unless you truly have custom logic to
91  * perform in both.<p>
92  *
93  * <p><b>WARNING:</b> Make sure that any one-time system updates (such as database
94  * updates or file writes) are performed in either an {@link #onSubmitAction onSubmitAction}
95  * method or the {@link #doSubmitAction doSubmitAction} method. Logic in the
96  * {@link #onSubmitRender onSubmitRender} methods may be executed repeatedly by
97  * the portal whenever the page containing the portlet is updated.</p>
98  *
99  * <p><b><a name="config">Exposed configuration properties</a>
100  * (<a HREF="AbstractFormController.html#config">and those defined by superclass</a>):</b><br>
101  * <table border="1">
102  * <tr>
103  * <td><b>name</b></td>
104  * <td><b>default</b></td>
105  * <td><b>description</b></td>
106  * </tr>
107  * <tr>
108  * <td>formView</td>
109  * <td><i>null</i></td>
110  * <td>Indicates what view to use when the user asks for a new form
111  * or when validation errors have occurred on form submission.</td>
112  * </tr>
113  * <tr>
114  * <td>successView</td>
115  * <td><i>null</i></td>
116  * <td>Indicates what view to use when successful form submissions have
117  * occurred. Such a success view could e.g. display a submission summary.
118  * More sophisticated actions can be implemented by overriding one of
119  * the {@link #onSubmitRender(Object) onSubmitRender()} methods.</td>
120  * </tr>
121  * <table>
122  * </p>
123  *
124  * <p>Parameters indicated with <code>setPassRenderParameters</code> will be
125  * preserved if the form has errors or if a form change request occurs.
126  * If there are render parameters you need in <code>onSubmitRender</code>,
127  * then you need to pass those forward from <code>onSubmitAction</code>.
128  *
129  * @author John A. Lewis
130  * @author Juergen Hoeller
131  * @author Rob Harrop
132  * @author Nick Lothian
133  * @author Rainer Schmitz
134  * @since 2.0
135  */

136 public class SimpleFormController extends AbstractFormController {
137
138     private String JavaDoc formView;
139
140     private String JavaDoc successView;
141
142
143     /**
144      * Create a new SimpleFormController.
145      * <p>Subclasses should set the following properties, either in the constructor
146      * or via a BeanFactory: commandName, commandClass, sessionForm, formView,
147      * successView. Note that commandClass doesn't need to be set when overriding
148      * <code>formBackingObject</code>, as this determines the class anyway.
149      * @see #setCommandClass(Class)
150      * @see #setCommandName(String)
151      * @see #setSessionForm(boolean)
152      * @see #setFormView
153      * @see #setSuccessView
154      * @see #formBackingObject(PortletRequest)
155      */

156     public SimpleFormController() {
157         // AbstractFormController sets default cache seconds to 0.
158
super();
159     }
160
161     /**
162      * Set the name of the view that should be used for form display.
163      */

164     public final void setFormView(String JavaDoc formView) {
165         this.formView = formView;
166     }
167
168     /**
169      * Return the name of the view that should be used for form display.
170      */

171     public final String JavaDoc getFormView() {
172         return this.formView;
173     }
174
175     /**
176      * Set the name of the view that should be shown on successful submit.
177      */

178     public final void setSuccessView(String JavaDoc successView) {
179         this.successView = successView;
180     }
181
182     /**
183      * Return the name of the view that should be shown on successful submit.
184      */

185     public final String JavaDoc getSuccessView() {
186         return this.successView;
187     }
188
189
190     /**
191      * This implementation shows the configured form view, delegating to the
192      * analogous showForm version with a controlModel argument.
193      * <p>Can be called within onSubmit implementations, to redirect back to the form
194      * in case of custom validation errors (i.e. not determined by the validator).
195      * <p>Can be overridden in subclasses to show a custom view, writing directly
196      * to the response or preparing the response before rendering a view.
197      * <p>If calling showForm with a custom control model in subclasses, it's preferable
198      * to override the analogous showForm version with a controlModel argument
199      * (which will handle both standard form showing and custom form showing then).
200      * @see #setFormView
201      * @see #showForm(RenderRequest, RenderResponse, BindException, Map)
202      */

203     protected ModelAndView showForm(RenderRequest request, RenderResponse response, BindException errors)
204             throws Exception JavaDoc {
205
206         return showForm(request, response, errors, null);
207     }
208
209     /**
210      * This implementation shows the configured form view.
211      * <p>Can be called within onSubmit implementations, to redirect back to the form
212      * in case of custom validation errors (i.e. not determined by the validator).
213      * <p>Can be overridden in subclasses to show a custom view, writing directly
214      * to the response or preparing the response before rendering a view.
215      * @param request current render request
216      * @param errors validation errors holder
217      * @param controlModel model map containing controller-specific control data
218      * (e.g. current page in wizard-style controllers or special error message)
219      * @return the prepared form view
220      * @throws Exception in case of invalid state or arguments
221      * @see #setFormView
222      */

223     protected ModelAndView showForm(RenderRequest request, RenderResponse response, BindException errors, Map JavaDoc controlModel)
224             throws Exception JavaDoc {
225
226         return showForm(request, errors, getFormView(), controlModel);
227     }
228
229     /**
230      * Create a reference data map for the given request and command,
231      * consisting of bean name/bean instance pairs as expected by ModelAndView.
232      * <p>Default implementation delegates to referenceData(request).
233      * Subclasses can override this to set reference data used in the view.
234      * @param request current portlet request
235      * @param command form object with request parameters bound onto it
236      * @param errors validation errors holder
237      * @return a Map with reference data entries, or null if none
238      * @throws Exception in case of invalid state or arguments
239      * @see ModelAndView
240      */

241     protected Map JavaDoc referenceData(PortletRequest request, Object JavaDoc command, Errors errors) throws Exception JavaDoc {
242         return referenceData(request);
243     }
244
245     /**
246      * Create a reference data map for the given request.
247      * Called by referenceData version with all parameters.
248      * <p>Default implementation returns null.
249      * Subclasses can override this to set reference data used in the view.
250      * @param request current portlet request
251      * @return a Map with reference data entries, or null if none
252      * @throws Exception in case of invalid state or arguments
253      * @see #referenceData(PortletRequest, Object, Errors)
254      * @see ModelAndView
255      */

256     protected Map JavaDoc referenceData(PortletRequest request) throws Exception JavaDoc {
257         return null;
258     }
259
260
261     /**
262      * This implementation calls <code>showForm</code> in case of errors,
263      * and delegates to <code>onSubmitRender<code>'s full version else.
264      * <p>This can only be overridden to check for an action that should be executed
265      * without respect to binding errors, like a cancel action. To just handle successful
266      * submissions without binding errors, override one of the <code>onSubmitRender</code>
267      * methods.
268      * @see #showForm(RenderRequest, RenderResponse, BindException)
269      * @see #onSubmitRender(RenderRequest, RenderResponse, Object, BindException)
270      * @see #onSubmitRender(Object, BindException)
271      * @see #onSubmitRender(Object)
272      * @see #processFormSubmission(ActionRequest, ActionResponse, Object, BindException)
273      */

274     protected ModelAndView renderFormSubmission(RenderRequest request, RenderResponse response, Object JavaDoc command, BindException errors)
275             throws Exception JavaDoc {
276
277         if (errors.hasErrors() || isFormChangeRequest(request)) {
278             return showForm(request, response, errors);
279         }
280         else {
281             return onSubmitRender(request, response, command, errors);
282         }
283     }
284
285     /**
286      * This implementation does nothing in case of errors,
287      * and delegates to <code>onSubmitAction</code>'s full version else.
288      * <p>This can only be overridden to check for an action that should be executed
289      * without respect to binding errors, like a cancel action. To just handle successful
290      * submissions without binding errors, override one of the <code>onSubmitAction</code>
291      * methods or <code>doSubmitAction</code>.
292      * @see #showForm
293      * @see #onSubmitAction(ActionRequest, ActionResponse, Object, BindException)
294      * @see #onSubmitAction(Object, BindException)
295      * @see #onSubmitAction(Object)
296      * @see #doSubmitAction(Object)
297      * @see #renderFormSubmission(RenderRequest, RenderResponse, Object, BindException)
298      */

299     protected void processFormSubmission(
300             ActionRequest request, ActionResponse response, Object JavaDoc command, BindException errors)
301             throws Exception JavaDoc {
302
303         if (errors.hasErrors()) {
304             if (logger.isDebugEnabled()) {
305                 logger.debug("Data binding errors: " + errors.getErrorCount());
306             }
307             if (isRedirectAction()) {
308                 setFormSubmit(response);
309             }
310             passRenderParameters(request, response);
311         }
312         else if (isFormChangeRequest(request)) {
313             logger.debug("Detected form change request -> routing request to onFormChange");
314             if (isRedirectAction()) {
315                 setFormSubmit(response);
316             }
317             passRenderParameters(request, response);
318             onFormChange(request, response, command, errors);
319         }
320         else {
321             logger.debug("No errors - processing submit");
322             onSubmitAction(request, response, command, errors);
323         }
324     }
325
326     /**
327      * This implementation delegates to <code>isFormChangeRequest</code>:
328      * A form change request changes the appearance of the form
329      * and should not get validated but just show the new form.
330      * @see #isFormChangeRequest
331      */

332     protected boolean suppressValidation(PortletRequest request) {
333         return isFormChangeRequest(request);
334     }
335
336     /**
337      * Determine whether the given request is a form change request.
338      * A form change request changes the appearance of the form
339      * and should always show the new form, without validation.
340      * <p>Gets called by suppressValidation and processFormSubmission.
341      * Consequently, this single method determines to suppress validation
342      * <i>and</i> to show the form view in any case.
343      * @param request current portlet request
344      * @return whether the given request is a form change request
345      * @see #suppressValidation
346      * @see #processFormSubmission
347      */

348     protected boolean isFormChangeRequest(PortletRequest request) {
349         return false;
350     }
351
352     /**
353      * Called during form submission if
354      * {@link #isFormChangeRequest(PortletRequest)}
355      * returns <code>true</code>. Allows subclasses to implement custom logic
356      * to modify the command object to directly modify data in the form.
357      * <p>Default implementation delegates to
358      * <code>onFormChange(request, response, command)</code>.
359      * @param request current action request
360      * @param response current action response
361      * @param command form object with request parameters bound onto it
362      * @param errors validation errors holder, allowing for additional
363      * custom validation
364      * @throws Exception in case of errors
365      * @see #isFormChangeRequest(PortletRequest)
366      * @see #onFormChange(ActionRequest, ActionResponse, Object)
367      */

368     protected void onFormChange(ActionRequest request, ActionResponse response, Object JavaDoc command, BindException errors)
369             throws Exception JavaDoc {
370
371         onFormChange(request, response, command);
372     }
373
374     /**
375      * Simpler <code>onFormChange</code> variant, called by the full version
376      * <code>onFormChange(request, response, command, errors)</code>.
377      * <p>Default implementation is empty.
378      * @param request current action request
379      * @param response current action response
380      * @param command form object with request parameters bound onto it
381      * @throws Exception in case of errors
382      * @see #onFormChange(ActionRequest, ActionResponse, Object, BindException)
383      */

384     protected void onFormChange(ActionRequest request, ActionResponse response, Object JavaDoc command)
385             throws Exception JavaDoc {
386     }
387
388
389     /**
390      * Submit render phase callback with all parameters. Called in case of submit without errors
391      * reported by the registered validator, or on every submit if no validator.
392      * <p>Default implementation delegates to <code>onSubmitRender(Object, BindException)</code>.
393      * For simply performing a submit action and rendering the specified success view,
394      * do not implement an <code>onSubmitRender</code> at all.
395      * <p>Subclasses can override this to provide custom rendering to display results of
396      * the action phase. Implementations can also call <code>showForm</code> to return to the form
397      * if the <code>onSubmitAction</code> failed custom validation. Do <i>not</i> implement multiple
398      * <code>onSubmitRender</code> methods: In that case,
399      * just this method will be called by the controller.
400      * <p>Call <code>errors.getModel()</code> to populate the ModelAndView model
401      * with the command and the Errors instance, under the specified command name,
402      * as expected by the "spring:bind" tag.
403      * @param request current render request
404      * @param response current render response
405      * @param command form object with request parameters bound onto it
406      * @param errors Errors instance without errors (subclass can add errors if it wants to)
407      * @return the prepared model and view
408      * @throws Exception in case of errors
409      * @see #onSubmitAction(ActionRequest, ActionResponse, Object, BindException)
410      * @see #onSubmitRender(Object, BindException)
411      * @see #doSubmitAction
412      * @see #showForm
413      * @see org.springframework.validation.Errors
414      * @see org.springframework.validation.BindException#getModel
415      */

416     protected ModelAndView onSubmitRender(RenderRequest request, RenderResponse response, Object JavaDoc command, BindException errors)
417             throws Exception JavaDoc {
418
419         return onSubmitRender(command, errors);
420     }
421
422     /**
423      * Submit action phase callback with all parameters. Called in case of submit without errors
424      * reported by the registered validator respectively on every submit if no validator.
425      * <p>Default implementation delegates to <code>onSubmitAction(Object, BindException)</code>.
426      * For simply performing a submit action consider implementing <code>doSubmitAction</code>
427      * rather than an <code>onSubmitAction</code> version.
428      * <p>Subclasses can override this to provide custom submission handling like storing
429      * the object to the database. Implementations can also perform custom validation and
430      * signal the render phase to call <code>showForm</code> to return to the form. Do <i>not</i>
431      * implement multiple <code>onSubmitAction</code> methods: In that case,
432      * just this method will be called by the controller.
433      * @param request current action request
434      * @param response current action response
435      * @param command form object with request parameters bound onto it
436      * @param errors Errors instance without errors (subclass can add errors if it wants to)
437      * @throws Exception in case of errors
438      * @see #onSubmitRender(RenderRequest, RenderResponse, Object, BindException)
439      * @see #onSubmitAction(Object, BindException)
440      * @see #doSubmitAction
441      * @see org.springframework.validation.Errors
442      */

443     protected void onSubmitAction(ActionRequest request, ActionResponse response, Object JavaDoc command, BindException errors)
444             throws Exception JavaDoc {
445
446         onSubmitAction(command, errors);
447     }
448
449     /**
450      * Simpler <code>onSubmitRender</code> version. Called by the default implementation
451      * of the <code>onSubmitRender</code> version with all parameters.
452      * <p>Default implementation calls <code>onSubmitRender(command)</code>, using the
453      * returned ModelAndView if actually implemented in a subclass. Else, the
454      * default behavior will apply: rendering the success view with the command
455      * and Errors instance as model.
456      * <p>Subclasses can override this to provide custom submission handling that
457      * does not need request and response.
458      * <p>Call <code>errors.getModel()</code> to populate the ModelAndView model
459      * with the command and the Errors instance, under the specified command name,
460      * as expected by the "spring:bind" tag.
461      * @param command form object with request parameters bound onto it
462      * @param errors Errors instance without errors
463      * @return the prepared model and view, or null
464      * @throws Exception in case of errors
465      * @see #onSubmitRender(RenderRequest, RenderResponse, Object, BindException)
466      * @see #onSubmitRender(Object)
467      * @see #onSubmitAction(Object, BindException)
468      * @see #setSuccessView
469      * @see org.springframework.validation.Errors
470      * @see org.springframework.validation.BindException#getModel
471      */

472     protected ModelAndView onSubmitRender(Object JavaDoc command, BindException errors) throws Exception JavaDoc {
473         ModelAndView mv = onSubmitRender(command);
474         if (mv != null) {
475             // simplest onSubmit version implemented in custom subclass
476
return mv;
477         }
478         else {
479             // default behavior: render success view
480
if (getSuccessView() == null) {
481                 throw new PortletException("successView isn't set");
482             }
483             return new ModelAndView(getSuccessView(), errors.getModel());
484         }
485     }
486
487     /**
488      * Simpler <code>onSubmitAction</code> version. Called by the default implementation
489      * of the <code>onSubmitAction</code> version with all parameters.
490      * <p>Default implementation calls <code>onSubmitAction(command)</code>.
491      * <p>Subclasses can override this to provide custom submission handling that
492      * does not need request and response.
493      * @param command form object with request parameters bound onto it
494      * @param errors Errors instance without errors
495      * @throws Exception in case of errors
496      * @see #onSubmitAction(ActionRequest, ActionResponse, Object, BindException)
497      * @see #onSubmitAction(Object)
498      * @see #onSubmitRender(Object, BindException)
499      * @see org.springframework.validation.Errors
500      */

501     protected void onSubmitAction(Object JavaDoc command, BindException errors) throws Exception JavaDoc {
502         onSubmitAction(command);
503     }
504
505     /**
506      * Simplest <code>onSubmitRender</code> version. Called by the default implementation
507      * of the <code>onSubmitRender</code> version with command and BindException parameters.
508      * <p>This implementation returns null as ModelAndView, making the calling
509      * onSubmitRender method perform its default rendering of the success view.
510      * <p>Subclasses can override this to provide custom submission handling
511      * that just depends on the command object.
512      * @param command form object with request parameters bound onto it
513      * @return the prepared model and view, or null for default (i.e. successView)
514      * @throws Exception in case of errors
515      * @see #onSubmitRender(Object, BindException)
516      * @see #onSubmitAction(Object)
517      * @see #doSubmitAction
518      * @see #setSuccessView
519      */

520     protected ModelAndView onSubmitRender(Object JavaDoc command) throws Exception JavaDoc {
521         return null;
522     }
523
524     /**
525      * Simplest <code>onSubmitAction</code> version. Called by the default implementation
526      * of the <code>onSubmitAction</code> version with command and BindException parameters.
527      * <p>This implementation calls <code>doSubmitAction</code>.
528      * <p>Subclasses can override this to provide custom submission handling
529      * that just depends on the command object.
530      * @param command form object with request parameters bound onto it
531      * @throws Exception in case of errors
532      * @see #onSubmitAction(Object, BindException)
533      * @see #onSubmitRender(Object)
534      * @see #doSubmitAction
535      */

536     protected void onSubmitAction(Object JavaDoc command) throws Exception JavaDoc {
537         doSubmitAction(command);
538     }
539
540     /**
541      * Template method for submit actions. Called by the default implementation
542      * of the simplest onSubmitAction version.
543      * <p><b>This is the preferred submit callback to implement if you want to
544      * perform an action (like storing changes to the database) and then render
545      * the success view with the command and Errors instance as model.</b>
546      * @param command form object with request parameters bound onto it
547      * @throws Exception in case of errors
548      * @see #onSubmitAction(Object)
549      * @see #onSubmitRender(Object)
550      * @see #setSuccessView
551      */

552     protected void doSubmitAction(Object JavaDoc command) throws Exception JavaDoc {
553     }
554
555 }
556
Popular Tags