001: /*
002: * Copyright 2002-2007 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.springframework.web.servlet.mvc;
018:
019: import java.util.Map;
020:
021: import javax.servlet.ServletException;
022: import javax.servlet.http.HttpServletRequest;
023: import javax.servlet.http.HttpServletResponse;
024:
025: import org.springframework.validation.BindException;
026: import org.springframework.validation.Errors;
027: import org.springframework.web.servlet.ModelAndView;
028:
029: /**
030: * <p>Concrete FormController implementation that provides configurable
031: * form and success views, and an onSubmit chain for convenient overriding.
032: * Automatically resubmits to the form view in case of validation errors,
033: * and renders the success view in case of a valid submission.</p>
034: *
035: * <p>The workflow of this Controller does not differ much from the one described
036: * in the {@link AbstractFormController AbstractFormController}. The difference
037: * is that you do not need to implement {@link #showForm showForm} and
038: * {@link #processFormSubmission processFormSubmission}: A form view and a
039: * success view can be configured declaratively.</p>
040: *
041: * <p><b><a name="workflow">Workflow
042: * (<a href="AbstractFormController.html#workflow">in addition to the superclass</a>):</b><br>
043: * <ol>
044: * <li>Call to {@link #processFormSubmission processFormSubmission} which inspects
045: * the {@link org.springframework.validation.Errors Errors} object to see if
046: * any errors have occurred during binding and validation.</li>
047: * <li>If errors occured, the controller will return the configured formView,
048: * showing the form again (possibly rendering according error messages).</li>
049: * <li>If {@link #isFormChangeRequest isFormChangeRequest} is overridden and returns
050: * true for the given request, the controller will return the formView too.
051: * In that case, the controller will also suppress validation. Before returning the formView,
052: * the controller will invoke {@link #onFormChange}, giving sub-classes a chance
053: * to make modification to the command object.
054: * This is intended for requests that change the structure of the form,
055: * which should not cause validation and show the form in any case.</li>
056: * <li>If no errors occurred, the controller will call
057: * {@link #onSubmit(HttpServletRequest, HttpServletResponse, Object, BindException) onSubmit}
058: * using all parameters, which in case of the default implementation delegates to
059: * {@link #onSubmit(Object, BindException) onSubmit} with just the command object.
060: * The default implementation of the latter method will return the configured
061: * <code>successView</code>. Consider implementing {@link #doSubmitAction} doSubmitAction
062: * for simply performing a submit action and rendering the success view.</li>
063: * </ol>
064: * </p>
065: *
066: * <p>The submit behavior can be customized by overriding one of the
067: * {@link #onSubmit onSubmit} methods. Submit actions can also perform
068: * custom validation if necessary (typically database-driven checks), calling
069: * {@link #showForm(HttpServletRequest, HttpServletResponse, BindException) showForm}
070: * in case of validation errors to show the form view again.</p>
071: *
072: * <p><b><a name="config">Exposed configuration properties</a>
073: * (<a href="AbstractFormController.html#config">and those defined by superclass</a>):</b><br>
074: * <table border="1">
075: * <tr>
076: * <td><b>name</b></td>
077: * <td><b>default</b></td>
078: * <td><b>description</b></td>
079: * </tr>
080: * <tr>
081: * <td>formView</td>
082: * <td><i>null</i></td>
083: * <td>Indicates what view to use when the user asks for a new form
084: * or when validation errors have occurred on form submission.</td>
085: * </tr>
086: * <tr>
087: * <td>successView</td>
088: * <td><i>null</i></td>
089: * <td>Indicates what view to use when successful form submissions have
090: * occurred. Such a success view could e.g. display a submission summary.
091: * More sophisticated actions can be implemented by overriding one of
092: * the {@link #onSubmit(Object) onSubmit()} methods.</td>
093: * </tr>
094: * <table>
095: * </p>
096: *
097: * @author Juergen Hoeller
098: * @author Rob Harrop
099: * @since 05.05.2003
100: */
101: public class SimpleFormController extends AbstractFormController {
102:
103: private String formView;
104:
105: private String successView;
106:
107: /**
108: * Create a new SimpleFormController.
109: * <p>Subclasses should set the following properties, either in the constructor
110: * or via a BeanFactory: commandName, commandClass, sessionForm, formView,
111: * successView. Note that commandClass doesn't need to be set when overriding
112: * <code>formBackingObject</code>, as this determines the class anyway.
113: * @see #setCommandClass
114: * @see #setCommandName
115: * @see #setSessionForm
116: * @see #setFormView
117: * @see #setSuccessView
118: * @see #formBackingObject
119: */
120: public SimpleFormController() {
121: // AbstractFormController sets default cache seconds to 0.
122: super ();
123: }
124:
125: /**
126: * Set the name of the view that should be used for form display.
127: */
128: public final void setFormView(String formView) {
129: this .formView = formView;
130: }
131:
132: /**
133: * Return the name of the view that should be used for form display.
134: */
135: public final String getFormView() {
136: return this .formView;
137: }
138:
139: /**
140: * Set the name of the view that should be shown on successful submit.
141: */
142: public final void setSuccessView(String successView) {
143: this .successView = successView;
144: }
145:
146: /**
147: * Return the name of the view that should be shown on successful submit.
148: */
149: public final String getSuccessView() {
150: return this .successView;
151: }
152:
153: /**
154: * This implementation shows the configured form view, delegating to the analogous
155: * {@link #showForm(HttpServletRequest, HttpServletResponse, BindException, Map)}
156: * variant with a "controlModel" argument.
157: * <p>Can be called within
158: * {@link #onSubmit(HttpServletRequest, HttpServletResponse, Object, BindException)}
159: * implementations, to redirect back to the form in case of custom validation errors
160: * (errors not determined by the validator).
161: * <p>Can be overridden in subclasses to show a custom view, writing directly
162: * to the response or preparing the response before rendering a view.
163: * <p>If calling showForm with a custom control model in subclasses, it's preferable
164: * to override the analogous showForm version with a controlModel argument
165: * (which will handle both standard form showing and custom form showing then).
166: * @see #setFormView
167: * @see #showForm(HttpServletRequest, HttpServletResponse, BindException, Map)
168: */
169: protected ModelAndView showForm(HttpServletRequest request,
170: HttpServletResponse response, BindException errors)
171: throws Exception {
172:
173: return showForm(request, response, errors, null);
174: }
175:
176: /**
177: * This implementation shows the configured form view.
178: * <p>Can be called within
179: * {@link #onSubmit(HttpServletRequest, HttpServletResponse, Object, BindException)}
180: * implementations, to redirect back to the form in case of custom validation errors
181: * (errors not determined by the validator).
182: * <p>Can be overridden in subclasses to show a custom view, writing directly
183: * to the response or preparing the response before rendering a view.
184: * @param request current HTTP request
185: * @param errors validation errors holder
186: * @param controlModel model map containing controller-specific control data
187: * (e.g. current page in wizard-style controllers or special error message)
188: * @return the prepared form view
189: * @throws Exception in case of invalid state or arguments
190: * @see #setFormView
191: */
192: protected ModelAndView showForm(HttpServletRequest request,
193: HttpServletResponse response, BindException errors,
194: Map controlModel) throws Exception {
195:
196: return showForm(request, errors, getFormView(), controlModel);
197: }
198:
199: /**
200: * Create a reference data map for the given request and command,
201: * consisting of bean name/bean instance pairs as expected by ModelAndView.
202: * <p>The default implementation delegates to {@link #referenceData(HttpServletRequest)}.
203: * Subclasses can override this to set reference data used in the view.
204: * @param request current HTTP request
205: * @param command form object with request parameters bound onto it
206: * @param errors validation errors holder
207: * @return a Map with reference data entries, or <code>null</code> if none
208: * @throws Exception in case of invalid state or arguments
209: * @see ModelAndView
210: */
211: protected Map referenceData(HttpServletRequest request,
212: Object command, Errors errors) throws Exception {
213: return referenceData(request);
214: }
215:
216: /**
217: * Create a reference data map for the given request.
218: * Called by the {@link #referenceData(HttpServletRequest, Object, Errors)}
219: * variant with all parameters.
220: * <p>The default implementation returns <code>null</code>.
221: * Subclasses can override this to set reference data used in the view.
222: * @param request current HTTP request
223: * @return a Map with reference data entries, or <code>null</code> if none
224: * @throws Exception in case of invalid state or arguments
225: * @see #referenceData(HttpServletRequest, Object, Errors)
226: * @see ModelAndView
227: */
228: protected Map referenceData(HttpServletRequest request)
229: throws Exception {
230: return null;
231: }
232:
233: /**
234: * This implementation calls
235: * {@link #showForm(HttpServletRequest, HttpServletResponse, BindException)}
236: * in case of errors, and delegates to the full
237: * {@link #onSubmit(HttpServletRequest, HttpServletResponse, Object, BindException)}'s
238: * variant else.
239: * <p>This can only be overridden to check for an action that should be executed
240: * without respect to binding errors, like a cancel action. To just handle successful
241: * submissions without binding errors, override one of the <code>onSubmit</code>
242: * methods or {@link #doSubmitAction}.
243: * @see #showForm(HttpServletRequest, HttpServletResponse, BindException)
244: * @see #onSubmit(HttpServletRequest, HttpServletResponse, Object, BindException)
245: * @see #onSubmit(Object, BindException)
246: * @see #onSubmit(Object)
247: * @see #doSubmitAction(Object)
248: */
249: protected ModelAndView processFormSubmission(
250: HttpServletRequest request, HttpServletResponse response,
251: Object command, BindException errors) throws Exception {
252:
253: if (errors.hasErrors()) {
254: if (logger.isDebugEnabled()) {
255: logger.debug("Data binding errors: "
256: + errors.getErrorCount());
257: }
258: return showForm(request, response, errors);
259: } else if (isFormChangeRequest(request, command)) {
260: logger
261: .debug("Detected form change request -> routing request to onFormChange");
262: onFormChange(request, response, command, errors);
263: return showForm(request, response, errors);
264: } else {
265: logger.debug("No errors -> processing submit");
266: return onSubmit(request, response, command, errors);
267: }
268: }
269:
270: /**
271: * This implementation delegates to {@link #isFormChangeRequest(HttpServletRequest, Object)}:
272: * A form change request changes the appearance of the form and should not get
273: * validated but just show the new form.
274: * @see #isFormChangeRequest
275: */
276: protected boolean suppressValidation(HttpServletRequest request,
277: Object command) {
278: return isFormChangeRequest(request, command);
279: }
280:
281: /**
282: * Determine whether the given request is a form change request.
283: * A form change request changes the appearance of the form
284: * and should always show the new form, without validation.
285: * <p>Gets called by {@link #suppressValidation} and {@link #processFormSubmission}.
286: * Consequently, this single method determines to suppress validation
287: * <i>and</i> to show the form view in any case.
288: * <p>The default implementation delegates to
289: * {@link #isFormChangeRequest(javax.servlet.http.HttpServletRequest)}.
290: * @param request current HTTP request
291: * @param command form object with request parameters bound onto it
292: * @return whether the given request is a form change request
293: * @see #suppressValidation
294: * @see #processFormSubmission
295: */
296: protected boolean isFormChangeRequest(HttpServletRequest request,
297: Object command) {
298: return isFormChangeRequest(request);
299: }
300:
301: /**
302: * Simpler <code>isFormChangeRequest</code> variant, called by the full
303: * variant {@link #isFormChangeRequest(HttpServletRequest, Object)}.
304: * <p>The default implementation is empty.
305: * @param request current HTTP request
306: * @return whether the given request is a form change request
307: * @see #suppressValidation
308: * @see #processFormSubmission
309: */
310: protected boolean isFormChangeRequest(HttpServletRequest request) {
311: return false;
312: }
313:
314: /**
315: * Called during form submission if
316: * {@link #isFormChangeRequest(javax.servlet.http.HttpServletRequest)}
317: * returns <code>true</code>. Allows subclasses to implement custom logic
318: * to modify the command object to directly modify data in the form.
319: * <p>The default implementation delegates to
320: * {@link #onFormChange(HttpServletRequest, HttpServletResponse, Object, BindException)}.
321: * @param request current servlet request
322: * @param response current servlet response
323: * @param command form object with request parameters bound onto it
324: * @param errors validation errors holder, allowing for additional
325: * custom validation
326: * @throws Exception in case of errors
327: * @see #isFormChangeRequest(HttpServletRequest)
328: * @see #onFormChange(HttpServletRequest, HttpServletResponse, Object)
329: */
330: protected void onFormChange(HttpServletRequest request,
331: HttpServletResponse response, Object command,
332: BindException errors) throws Exception {
333:
334: onFormChange(request, response, command);
335: }
336:
337: /**
338: * Simpler <code>onFormChange</code> variant, called by the full variant
339: * {@link #onFormChange(HttpServletRequest, HttpServletResponse, Object, BindException)}.
340: * <p>The default implementation is empty.
341: * @param request current servlet request
342: * @param response current servlet response
343: * @param command form object with request parameters bound onto it
344: * @throws Exception in case of errors
345: * @see #onFormChange(HttpServletRequest, HttpServletResponse, Object, BindException)
346: */
347: protected void onFormChange(HttpServletRequest request,
348: HttpServletResponse response, Object command)
349: throws Exception {
350: }
351:
352: /**
353: * Submit callback with all parameters. Called in case of submit without errors
354: * reported by the registered validator, or on every submit if no validator.
355: * <p>The default implementation delegates to {@link #onSubmit(Object, BindException)}.
356: * For simply performing a submit action and rendering the specified success
357: * view, consider implementing {@link #doSubmitAction} rather than an
358: * <code>onSubmit</code> variant.
359: * <p>Subclasses can override this to provide custom submission handling like storing
360: * the object to the database. Implementations can also perform custom validation and
361: * call showForm to return to the form. Do <i>not</i> implement multiple onSubmit
362: * methods: In that case, just this method will be called by the controller.
363: * <p>Call <code>errors.getModel()</code> to populate the ModelAndView model
364: * with the command and the Errors instance, under the specified command name,
365: * as expected by the "spring:bind" tag.
366: * @param request current servlet request
367: * @param response current servlet response
368: * @param command form object with request parameters bound onto it
369: * @param errors Errors instance without errors (subclass can add errors if it wants to)
370: * @return the prepared model and view, or <code>null</code>
371: * @throws Exception in case of errors
372: * @see #onSubmit(Object, BindException)
373: * @see #doSubmitAction
374: * @see #showForm
375: * @see org.springframework.validation.Errors
376: * @see org.springframework.validation.BindException#getModel
377: */
378: protected ModelAndView onSubmit(HttpServletRequest request,
379: HttpServletResponse response, Object command,
380: BindException errors) throws Exception {
381:
382: return onSubmit(command, errors);
383: }
384:
385: /**
386: * Simpler <code>onSubmit</code> variant.
387: * Called by the default implementation of the
388: * {@link #onSubmit(HttpServletRequest, HttpServletResponse, Object, BindException)}
389: * variant with all parameters.
390: * <p>The default implementation calls {@link #onSubmit(Object)}, using the
391: * returned ModelAndView if actually implemented in a subclass. Else, the
392: * default behavior will apply: rendering the success view with the command
393: * and Errors instance as model.
394: * <p>Subclasses can override this to provide custom submission handling that
395: * does not need request and response.
396: * <p>Call <code>errors.getModel()</code> to populate the ModelAndView model
397: * with the command and the Errors instance, under the specified command name,
398: * as expected by the "spring:bind" tag.
399: * @param command form object with request parameters bound onto it
400: * @param errors Errors instance without errors
401: * @return the prepared model and view
402: * @throws Exception in case of errors
403: * @see #onSubmit(HttpServletRequest, HttpServletResponse, Object, BindException)
404: * @see #onSubmit(Object)
405: * @see #setSuccessView
406: * @see org.springframework.validation.Errors
407: * @see org.springframework.validation.BindException#getModel
408: */
409: protected ModelAndView onSubmit(Object command, BindException errors)
410: throws Exception {
411: ModelAndView mv = onSubmit(command);
412: if (mv != null) {
413: // simplest onSubmit variant implemented in custom subclass
414: return mv;
415: } else {
416: // default behavior: render success view
417: if (getSuccessView() == null) {
418: throw new ServletException("successView isn't set");
419: }
420: return new ModelAndView(getSuccessView(), errors.getModel());
421: }
422: }
423:
424: /**
425: * Simplest <code>onSubmit</code> variant. Called by the default implementation
426: * of the {@link #onSubmit(Object, BindException)} variant.
427: * <p>This implementation calls {@link #doSubmitAction(Object)} and returns
428: * <code>null</code> as ModelAndView, making the calling <code>onSubmit</code>
429: * method perform its default rendering of the success view.
430: * <p>Subclasses can override this to provide custom submission handling
431: * that just depends on the command object. It's preferable to use either
432: * {@link #onSubmit(Object, BindException)} or {@link #doSubmitAction(Object)},
433: * though: Use the former when you want to build your own ModelAndView; use the
434: * latter when you want to perform an action and forward to the successView.
435: * @param command form object with request parameters bound onto it
436: * @return the prepared model and view, or <code>null</code> for default
437: * (that is, rendering the configured "successView")
438: * @throws Exception in case of errors
439: * @see #onSubmit(Object, BindException)
440: * @see #doSubmitAction
441: * @see #setSuccessView
442: */
443: protected ModelAndView onSubmit(Object command) throws Exception {
444: doSubmitAction(command);
445: return null;
446: }
447:
448: /**
449: * Template method for submit actions. Called by the default implementation
450: * of the simplest {@link #onSubmit(Object)} variant.
451: * <p><b>This is the preferred submit callback to implement if you want to
452: * perform an action (like storing changes to the database) and then render
453: * the success view with the command and Errors instance as model.</b>
454: * You don't need to care about the success ModelAndView here.
455: * @param command form object with request parameters bound onto it
456: * @throws Exception in case of errors
457: * @see #onSubmit(Object)
458: * @see #setSuccessView
459: */
460: protected void doSubmitAction(Object command) throws Exception {
461: }
462:
463: }
|