All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.springframework.web.portlet.mvc.SimpleFormController Maven / Gradle / Ivy

There is a newer version: 5.3.34
Show newest version
/*
 * Copyright 2002-2006 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.web.portlet.mvc;

import java.util.Map;

import javax.portlet.ActionRequest;
import javax.portlet.ActionResponse;
import javax.portlet.PortletException;
import javax.portlet.PortletRequest;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;

import org.springframework.validation.BindException;
import org.springframework.validation.Errors;
import org.springframework.web.portlet.ModelAndView;

/**
 * 

Concrete FormController implementation that provides configurable * form and success views, and an onSubmit chain for convenient overriding. * Automatically resubmits to the form view in case of validation errors, * and renders the success view in case of a valid submission.

* *

The workflow of this Controller does not differ much from the one described * in the {@link AbstractFormController AbstractFormController}. The difference * is that you do not need to implement {@link #showForm showForm}, * {@link #processFormSubmission processFormSubmission}, and * {@link #renderFormSubmission renderFormSubmission}: A form view and a * success view can be configured declaratively.

* *

This controller is different from it's servlet counterpart in that it must take * into account the two phases of a portlet request: the action phase and the render * phase. See the JSR-168 spec for more details on these two phases. * Be especially aware that the action phase is called only once, but that the * render phase will be called repeatedly by the portal -- it does this every time * the page containing the portlet is updated, even if the activity is in some other * portlet. The main difference in the methods in this class is that the * onSubmit methods have all been split into onSubmitAction * and onSubmitRender to account for the two phases.

* *

Workflow * (in addition to the superclass):
*

    *
  1. Call to {@link #processFormSubmission processFormSubmission} which inspects * the {@link org.springframework.validation.Errors Errors} object to see if * any errors have occurred during binding and validation.
  2. *
  3. If errors occured, the controller will return the configured formView, * showing the form again (possibly rendering according error messages).
  4. *
  5. If {@link #isFormChangeRequest isFormChangeRequest} is overridden and returns * true for the given request, the controller will return the formView too. * In that case, the controller will also suppress validation. Before returning the formView, * the controller will invoke {@link #onFormChange}, giving sub-classes a chance * to make modification to the command object. * This is intended for requests that change the structure of the form, * which should not cause validation and show the form in any case.
  6. *
  7. If no errors occurred, the controller will call * {@link #onSubmitAction(ActionRequest, ActionResponse, Object, BindException) onSubmitAction} * during the action phase and then {@link #onSubmitRender(RenderRequest, RenderResponse, * Object, BindException) onSubmitRender} during the render phase, which in case of the * default implementation delegate to {@link #onSubmitAction(Object, BindException) * onSubmitAction} and {@link #onSubmitRender(Object, BindException) onSubmitRender} * with just the command object. * The default implementation of the latter method will return the configured * successView. Consider just implementing {@link #doSubmitAction doSubmitAction} * for simply performing a submit action during the action phase and then rendering * the success view during the render phase.
  8. *
*

* *

The submit behavior can be customized by overriding one of the * {@link #onSubmitAction onSubmitAction} or {@link #onSubmitRender onSubmitRender} * methods. Submit actions can also perform custom validation if necessary * (typically database-driven checks), calling {@link #showForm(RenderRequest, * RenderResponse, BindException) showForm} in case of validation errors to show * the form view again. You do not have to override both the onSubmitAction and * onSubmitRender methods at a given level unless you truly have custom logic to * perform in both.

* *

WARNING: Make sure that any one-time system updates (such as database * updates or file writes) are performed in either an {@link #onSubmitAction onSubmitAction} * method or the {@link #doSubmitAction doSubmitAction} method. Logic in the * {@link #onSubmitRender onSubmitRender} methods may be executed repeatedly by * the portal whenever the page containing the portlet is updated.

* *

Exposed configuration properties * (and those defined by superclass):
*

* * * * * * * * * * * * * * * *
namedefaultdescription
formViewnullIndicates what view to use when the user asks for a new form * or when validation errors have occurred on form submission.
successViewnullIndicates what view to use when successful form submissions have * occurred. Such a success view could e.g. display a submission summary. * More sophisticated actions can be implemented by overriding one of * the {@link #onSubmitRender(Object) onSubmitRender()} methods.
*

* *

Parameters indicated with setPassRenderParameters will be * preserved if the form has errors or if a form change request occurs. * If there are render parameters you need in onSubmitRender, * then you need to pass those forward from onSubmitAction. * * @author John A. Lewis * @author Juergen Hoeller * @author Rob Harrop * @author Nick Lothian * @author Rainer Schmitz * @since 2.0 */ public class SimpleFormController extends AbstractFormController { private String formView; private String successView; /** * Create a new SimpleFormController. *

Subclasses should set the following properties, either in the constructor * or via a BeanFactory: commandName, commandClass, sessionForm, formView, * successView. Note that commandClass doesn't need to be set when overriding * formBackingObject, as this determines the class anyway. * @see #setCommandClass(Class) * @see #setCommandName(String) * @see #setSessionForm(boolean) * @see #setFormView * @see #setSuccessView * @see #formBackingObject(PortletRequest) */ public SimpleFormController() { // AbstractFormController sets default cache seconds to 0. super(); } /** * Set the name of the view that should be used for form display. */ public final void setFormView(String formView) { this.formView = formView; } /** * Return the name of the view that should be used for form display. */ public final String getFormView() { return this.formView; } /** * Set the name of the view that should be shown on successful submit. */ public final void setSuccessView(String successView) { this.successView = successView; } /** * Return the name of the view that should be shown on successful submit. */ public final String getSuccessView() { return this.successView; } /** * This implementation shows the configured form view, delegating to the * analogous showForm version with a controlModel argument. *

Can be called within onSubmit implementations, to redirect back to the form * in case of custom validation errors (i.e. not determined by the validator). *

Can be overridden in subclasses to show a custom view, writing directly * to the response or preparing the response before rendering a view. *

If calling showForm with a custom control model in subclasses, it's preferable * to override the analogous showForm version with a controlModel argument * (which will handle both standard form showing and custom form showing then). * @see #setFormView * @see #showForm(RenderRequest, RenderResponse, BindException, Map) */ protected ModelAndView showForm(RenderRequest request, RenderResponse response, BindException errors) throws Exception { return showForm(request, response, errors, null); } /** * This implementation shows the configured form view. *

Can be called within onSubmit implementations, to redirect back to the form * in case of custom validation errors (i.e. not determined by the validator). *

Can be overridden in subclasses to show a custom view, writing directly * to the response or preparing the response before rendering a view. * @param request current render request * @param errors validation errors holder * @param controlModel model map containing controller-specific control data * (e.g. current page in wizard-style controllers or special error message) * @return the prepared form view * @throws Exception in case of invalid state or arguments * @see #setFormView */ protected ModelAndView showForm(RenderRequest request, RenderResponse response, BindException errors, Map controlModel) throws Exception { return showForm(request, errors, getFormView(), controlModel); } /** * Create a reference data map for the given request and command, * consisting of bean name/bean instance pairs as expected by ModelAndView. *

Default implementation delegates to referenceData(request). * Subclasses can override this to set reference data used in the view. * @param request current portlet request * @param command form object with request parameters bound onto it * @param errors validation errors holder * @return a Map with reference data entries, or null if none * @throws Exception in case of invalid state or arguments * @see ModelAndView */ protected Map referenceData(PortletRequest request, Object command, Errors errors) throws Exception { return referenceData(request); } /** * Create a reference data map for the given request. * Called by referenceData version with all parameters. *

Default implementation returns null. * Subclasses can override this to set reference data used in the view. * @param request current portlet request * @return a Map with reference data entries, or null if none * @throws Exception in case of invalid state or arguments * @see #referenceData(PortletRequest, Object, Errors) * @see ModelAndView */ protected Map referenceData(PortletRequest request) throws Exception { return null; } /** * This implementation calls showForm in case of errors, * and delegates to onSubmitRender's full version else. *

This can only be overridden to check for an action that should be executed * without respect to binding errors, like a cancel action. To just handle successful * submissions without binding errors, override one of the onSubmitRender * methods. * @see #showForm(RenderRequest, RenderResponse, BindException) * @see #onSubmitRender(RenderRequest, RenderResponse, Object, BindException) * @see #onSubmitRender(Object, BindException) * @see #onSubmitRender(Object) * @see #processFormSubmission(ActionRequest, ActionResponse, Object, BindException) */ protected ModelAndView renderFormSubmission(RenderRequest request, RenderResponse response, Object command, BindException errors) throws Exception { if (errors.hasErrors() || isFormChangeRequest(request)) { return showForm(request, response, errors); } else { return onSubmitRender(request, response, command, errors); } } /** * This implementation does nothing in case of errors, * and delegates to onSubmitAction's full version else. *

This can only be overridden to check for an action that should be executed * without respect to binding errors, like a cancel action. To just handle successful * submissions without binding errors, override one of the onSubmitAction * methods or doSubmitAction. * @see #showForm * @see #onSubmitAction(ActionRequest, ActionResponse, Object, BindException) * @see #onSubmitAction(Object, BindException) * @see #onSubmitAction(Object) * @see #doSubmitAction(Object) * @see #renderFormSubmission(RenderRequest, RenderResponse, Object, BindException) */ protected void processFormSubmission( ActionRequest request, ActionResponse response, Object command, BindException errors) throws Exception { if (errors.hasErrors()) { if (logger.isDebugEnabled()) { logger.debug("Data binding errors: " + errors.getErrorCount()); } if (isRedirectAction()) { setFormSubmit(response); } passRenderParameters(request, response); } else if (isFormChangeRequest(request)) { logger.debug("Detected form change request -> routing request to onFormChange"); if (isRedirectAction()) { setFormSubmit(response); } passRenderParameters(request, response); onFormChange(request, response, command, errors); } else { logger.debug("No errors - processing submit"); onSubmitAction(request, response, command, errors); } } /** * This implementation delegates to isFormChangeRequest: * A form change request changes the appearance of the form * and should not get validated but just show the new form. * @see #isFormChangeRequest */ protected boolean suppressValidation(PortletRequest request) { return isFormChangeRequest(request); } /** * Determine whether the given request is a form change request. * A form change request changes the appearance of the form * and should always show the new form, without validation. *

Gets called by suppressValidation and processFormSubmission. * Consequently, this single method determines to suppress validation * and to show the form view in any case. * @param request current portlet request * @return whether the given request is a form change request * @see #suppressValidation * @see #processFormSubmission */ protected boolean isFormChangeRequest(PortletRequest request) { return false; } /** * Called during form submission if * {@link #isFormChangeRequest(PortletRequest)} * returns true. Allows subclasses to implement custom logic * to modify the command object to directly modify data in the form. *

Default implementation delegates to * onFormChange(request, response, command). * @param request current action request * @param response current action response * @param command form object with request parameters bound onto it * @param errors validation errors holder, allowing for additional * custom validation * @throws Exception in case of errors * @see #isFormChangeRequest(PortletRequest) * @see #onFormChange(ActionRequest, ActionResponse, Object) */ protected void onFormChange(ActionRequest request, ActionResponse response, Object command, BindException errors) throws Exception { onFormChange(request, response, command); } /** * Simpler onFormChange variant, called by the full version * onFormChange(request, response, command, errors). *

Default implementation is empty. * @param request current action request * @param response current action response * @param command form object with request parameters bound onto it * @throws Exception in case of errors * @see #onFormChange(ActionRequest, ActionResponse, Object, BindException) */ protected void onFormChange(ActionRequest request, ActionResponse response, Object command) throws Exception { } /** * Submit render phase callback with all parameters. Called in case of submit without errors * reported by the registered validator, or on every submit if no validator. *

Default implementation delegates to onSubmitRender(Object, BindException). * For simply performing a submit action and rendering the specified success view, * do not implement an onSubmitRender at all. *

Subclasses can override this to provide custom rendering to display results of * the action phase. Implementations can also call showForm to return to the form * if the onSubmitAction failed custom validation. Do not implement multiple * onSubmitRender methods: In that case, * just this method will be called by the controller. *

Call errors.getModel() to populate the ModelAndView model * with the command and the Errors instance, under the specified command name, * as expected by the "spring:bind" tag. * @param request current render request * @param response current render response * @param command form object with request parameters bound onto it * @param errors Errors instance without errors (subclass can add errors if it wants to) * @return the prepared model and view * @throws Exception in case of errors * @see #onSubmitAction(ActionRequest, ActionResponse, Object, BindException) * @see #onSubmitRender(Object, BindException) * @see #doSubmitAction * @see #showForm * @see org.springframework.validation.Errors * @see org.springframework.validation.BindException#getModel */ protected ModelAndView onSubmitRender(RenderRequest request, RenderResponse response, Object command, BindException errors) throws Exception { return onSubmitRender(command, errors); } /** * Submit action phase callback with all parameters. Called in case of submit without errors * reported by the registered validator respectively on every submit if no validator. *

Default implementation delegates to onSubmitAction(Object, BindException). * For simply performing a submit action consider implementing doSubmitAction * rather than an onSubmitAction version. *

Subclasses can override this to provide custom submission handling like storing * the object to the database. Implementations can also perform custom validation and * signal the render phase to call showForm to return to the form. Do not * implement multiple onSubmitAction methods: In that case, * just this method will be called by the controller. * @param request current action request * @param response current action response * @param command form object with request parameters bound onto it * @param errors Errors instance without errors (subclass can add errors if it wants to) * @throws Exception in case of errors * @see #onSubmitRender(RenderRequest, RenderResponse, Object, BindException) * @see #onSubmitAction(Object, BindException) * @see #doSubmitAction * @see org.springframework.validation.Errors */ protected void onSubmitAction(ActionRequest request, ActionResponse response, Object command, BindException errors) throws Exception { onSubmitAction(command, errors); } /** * Simpler onSubmitRender version. Called by the default implementation * of the onSubmitRender version with all parameters. *

Default implementation calls onSubmitRender(command), using the * returned ModelAndView if actually implemented in a subclass. Else, the * default behavior will apply: rendering the success view with the command * and Errors instance as model. *

Subclasses can override this to provide custom submission handling that * does not need request and response. *

Call errors.getModel() to populate the ModelAndView model * with the command and the Errors instance, under the specified command name, * as expected by the "spring:bind" tag. * @param command form object with request parameters bound onto it * @param errors Errors instance without errors * @return the prepared model and view, or null * @throws Exception in case of errors * @see #onSubmitRender(RenderRequest, RenderResponse, Object, BindException) * @see #onSubmitRender(Object) * @see #onSubmitAction(Object, BindException) * @see #setSuccessView * @see org.springframework.validation.Errors * @see org.springframework.validation.BindException#getModel */ protected ModelAndView onSubmitRender(Object command, BindException errors) throws Exception { ModelAndView mv = onSubmitRender(command); if (mv != null) { // simplest onSubmit version implemented in custom subclass return mv; } else { // default behavior: render success view if (getSuccessView() == null) { throw new PortletException("successView isn't set"); } return new ModelAndView(getSuccessView(), errors.getModel()); } } /** * Simpler onSubmitAction version. Called by the default implementation * of the onSubmitAction version with all parameters. *

Default implementation calls onSubmitAction(command). *

Subclasses can override this to provide custom submission handling that * does not need request and response. * @param command form object with request parameters bound onto it * @param errors Errors instance without errors * @throws Exception in case of errors * @see #onSubmitAction(ActionRequest, ActionResponse, Object, BindException) * @see #onSubmitAction(Object) * @see #onSubmitRender(Object, BindException) * @see org.springframework.validation.Errors */ protected void onSubmitAction(Object command, BindException errors) throws Exception { onSubmitAction(command); } /** * Simplest onSubmitRender version. Called by the default implementation * of the onSubmitRender version with command and BindException parameters. *

This implementation returns null as ModelAndView, making the calling * onSubmitRender method perform its default rendering of the success view. *

Subclasses can override this to provide custom submission handling * that just depends on the command object. * @param command form object with request parameters bound onto it * @return the prepared model and view, or null for default (i.e. successView) * @throws Exception in case of errors * @see #onSubmitRender(Object, BindException) * @see #onSubmitAction(Object) * @see #doSubmitAction * @see #setSuccessView */ protected ModelAndView onSubmitRender(Object command) throws Exception { return null; } /** * Simplest onSubmitAction version. Called by the default implementation * of the onSubmitAction version with command and BindException parameters. *

This implementation calls doSubmitAction. *

Subclasses can override this to provide custom submission handling * that just depends on the command object. * @param command form object with request parameters bound onto it * @throws Exception in case of errors * @see #onSubmitAction(Object, BindException) * @see #onSubmitRender(Object) * @see #doSubmitAction */ protected void onSubmitAction(Object command) throws Exception { doSubmitAction(command); } /** * Template method for submit actions. Called by the default implementation * of the simplest onSubmitAction version. *

This is the preferred submit callback to implement if you want to * perform an action (like storing changes to the database) and then render * the success view with the command and Errors instance as model. * @param command form object with request parameters bound onto it * @throws Exception in case of errors * @see #onSubmitAction(Object) * @see #onSubmitRender(Object) * @see #setSuccessView */ protected void doSubmitAction(Object command) throws Exception { } }