Author(s)John A. Lewis, Juergen Hoeller, Alef Arendsen, Rob Harrop, Rainer Schmitz
Form controller that auto-populates a form bean from the request.
This, either using a new bean instance per request, or using the same bean
when the sessionForm property has been set to
true.
Both form-input-views and after-submission-views have to be provided
programmatically. To provide those views using configuration properties,
use the SimpleFormController .
Subclasses need to override showForm to prepare the form view,
processFormSubmission to handle submit requests, and
renderFormSubmission to display the results of the submit.
For the latter two methods, binding errors like type mismatches will be
reported via the given "errors" holder. For additional custom form validation,
a validator (property inherited from BaseCommandController) can be used,
reporting via the same "errors" instance.
Comparing this Controller to the Struts notion of the Action
shows us that with Spring, you can use any ordinary JavaBeans or database-
backed JavaBeans without having to implement a framework-specific class
(like Struts' ActionForm). More complex properties of JavaBeans
(Dates, Locales, but also your own application-specific or compound types)
can be represented and submitted to the controller, by using the notion of
a java.beans.PropertyEditors. For more information on that
subject, see the workflow of this controller and the explanation of the
BaseCommandController .
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. (This is not quite true, the portal can also be told to cache the results of
the render for a period of time, but assume it is true for programming purposes.)
The controller receives a request for a new form (typically a
Render Request only). The render phase will proceed to display
the form as follows.
Call to formBackingObject() which by
default, returns an instance of the commandClass that has been
configured (see the properties the superclass exposes), but can also be
overridden to e.g. retrieve an object from the database (that needs to
be modified using the form).
Call to initBinder() which allows you to
register custom editors for certain fields (often properties of non-
primitive or non-String types) of the command class. This will render
appropriate Strings for those property values, e.g. locale-specific
date strings.
The PortletRequestDataBinder
gets applied to populate the new form object with initial request parameters and the
onBindOnNewForm(RenderRequest, Object, BindException) callback method is invoked.
(only if bindOnNewForm is set to true)
Make sure that the initial parameters do not include the parameter that indicates a
form submission has occurred.
Call to showForm to return a View that should be rendered
(typically the view that renders the form). This method has to be
implemented in subclasses.
The showForm() implementation will call referenceData ,
which you can implement to provide any relevant reference data you might need
when editing a form (e.g. a List of Locale objects you're going to let the
user select one from).
Model gets exposed and view gets rendered, to let the user fill in
the form.
The controller receives a form submission (typically an Action
Request). To use a different way of detecting a form submission,
override the isFormSubmission method.
The action phase will proceed to process the form submission as follows.
If sessionForm is not set, formBackingObject is called to retrieve a form object. Otherwise,
the controller tries to find the command object which is already bound
in the session. If it cannot find the object, the action phase does a
call to handleInvalidSubmit which - by default -
tries to create a new form object and resubmit the form. It then sets
a render parameter that will indicate to the render phase that this was
an invalid submit.
Still in the action phase of a valid submit, the PortletRequestDataBinder gets applied to populate
the form object with current request parameters.
Call to onBind(PortletRequest, Object, Errors)
which allows you to do custom processing after binding but before
validation (e.g. to manually bind request parameters to bean
properties, to be seen by the Validator).
If validateOnBinding is set, a registered Validator
will be invoked. The Validator will check the form object properties,
and register corresponding errors via the given Errors
object.
Call to onBindAndValidate which allows
you to do custom processing after binding and validation (e.g. to
manually bind request parameters, and to validate them outside a
Validator).
Call to processFormSubmission
to process the submission, with or without binding errors.
This method has to be implemented in subclasses and will be called
only once per form submission.
The portal will then call the render phase of processing the form
submission. This phase will be called repeatedly by the portal every
time the page is refreshed. All processing here should take this into
account. Any one-time-only actions (such as modifying a database) must
be done in the action phase.
If the action phase indicated this is an invalid submit, the render
phase calls renderInvalidSubmit which –
also by default – will render the results of the resubmitted
form. Be sure to override both handleInvalidSubmit and
renderInvalidSubmit if you want to change this overall
behavior.
Finally, call renderFormSubmission to
render the results of the submission, with or without binding errors.
This method has to be implemented in subclasses and will be called
repeatedly by the portal.
In session form mode, a submission without an existing form object in the
session is considered invalid, like in the case of a resubmit/reload by the browser.
The handleInvalidSubmit /
renderInvalidSubmit methods are invoked then,
by default trying to resubmit. This can be overridden in subclasses to show
corresponding messages or to redirect to a new form, in order to avoid duplicate
submissions. The form object in the session can be considered a transaction token
in that case.
Make sure that any URLs that take you to your form controller are Render URLs, so
that it will not try to treat the initial call as a form submission. If you use
Action URLs to link to your controller, you will need to override the
isFormSubmission method to use a different mechanism for
determining whether a form has been submitted. Make sure this method will work for
both the ActionRequest and the RenderRequest objects.
Note that views should never retrieve form beans from the session but always
from the request, as prepared by the form controller. Remember that some view
technologies like Velocity cannot even access a HTTP session.
Indicates whether to bind portlet request parameters when
creating a new form. Otherwise, the parameters will only be
bound on form submission attempts.
sessionForm
false
Indicates whether the form object should be kept in the session
when a user asks for a new form. This allows you e.g. to retrieve
an object from the database, let the user edit it, and then persist
it again. Otherwise, a new command object will be created for each
request (even when showing the form again after validation errors).
redirectAction
false
Specifies whether processFormSubmission is expected to call
ActionResponse.sendRedirect .
This is important because some methods may not be called before
ActionResponse.sendRedirect (e.g.
ActionResponse.setRenderParameter ).
Setting this flag will prevent AbstractFormController from setting render
parameters that it normally needs for the render phase.
If this is set true and sendRedirect is not called, then
processFormSubmission must call
setFormSubmit .
Otherwise, the render phase will not realize the form was submitted
and will simply display a new blank form.
renderParameters
null
An array of parameters that will be passed forward from the action
phase to the render phase if the form needs to be displayed
again. These can also be passed forward explicitly by calling
the passRenderParameters method from any action
phase method. Abstract descendants of this controller should follow
similar behavior. If there are parameters you need in
renderFormSubmission, then you need to pass those
forward from processFormSubmission. If you override the
default behavior of invalid submits and you set sessionForm to true,
then you probably will not need to set this because your parameters
are only going to be needed on the first request.
Form controller that auto-populates a form bean from the request. This, either using a new bean instance per request, or using the same bean when the
sessionFormproperty has been set totrue.This class is the base class for both framework subclasses like SimpleFormController and AbstractWizardFormController , and custom form controllers you can provide yourself.
Both form-input-views and after-submission-views have to be provided programmatically. To provide those views using configuration properties, use the SimpleFormController .
Subclasses need to override
showFormto prepare the form view,processFormSubmissionto handle submit requests, andrenderFormSubmissionto display the results of the submit. For the latter two methods, binding errors like type mismatches will be reported via the given "errors" holder. For additional custom form validation, a validator (property inherited from BaseCommandController) can be used, reporting via the same "errors" instance.Comparing this Controller to the Struts notion of the
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. (This is not quite true, the portal can also be told to cache the results of the render for a period of time, but assume it is true for programming purposes.)Actionshows us that with Spring, you can use any ordinary JavaBeans or database- backed JavaBeans without having to implement a framework-specific class (like Struts'ActionForm). More complex properties of JavaBeans (Dates, Locales, but also your own application-specific or compound types) can be represented and submitted to the controller, by using the notion of ajava.beans.PropertyEditors. For more information on that subject, see the workflow of this controller and the explanation of the BaseCommandController .Workflow (and that defined by superclass):
bindOnNewFormis set totrue) Make sure that the initial parameters do not include the parameter that indicates a form submission has occurred.sessionFormis not set, formBackingObject is called to retrieve a form object. Otherwise, the controller tries to find the command object which is already bound in the session. If it cannot find the object, the action phase does a call to handleInvalidSubmit which - by default - tries to create a new form object and resubmit the form. It then sets a render parameter that will indicate to the render phase that this was an invalid submit.validateOnBindingis set, a registered Validator will be invoked. The Validator will check the form object properties, and register corresponding errors via the given Errors object.handleInvalidSubmitandrenderInvalidSubmitif you want to change this overall behavior.In session form mode, a submission without an existing form object in the session is considered invalid, like in the case of a resubmit/reload by the browser. The handleInvalidSubmit / renderInvalidSubmit methods are invoked then, by default trying to resubmit. This can be overridden in subclasses to show corresponding messages or to redirect to a new form, in order to avoid duplicate submissions. The form object in the session can be considered a transaction token in that case.
Make sure that any URLs that take you to your form controller are Render URLs, so that it will not try to treat the initial call as a form submission. If you use Action URLs to link to your controller, you will need to override the isFormSubmission method to use a different mechanism for determining whether a form has been submitted. Make sure this method will work for both the ActionRequest and the RenderRequest objects.Note that views should never retrieve form beans from the session but always from the request, as prepared by the form controller. Remember that some view technologies like Velocity cannot even access a HTTP session.
Exposed configuration properties (and those defined by superclass):
processFormSubmissionis expected to call ActionResponse.sendRedirect . This is important because some methods may not be called before ActionResponse.sendRedirect (e.g. ActionResponse.setRenderParameter ). Setting this flag will prevent AbstractFormController from setting render parameters that it normally needs for the render phase. If this is set true andsendRedirectis not called, thenprocessFormSubmissionmust call setFormSubmit . Otherwise, the render phase will not realize the form was submitted and will simply display a new blank form.passRenderParametersmethod from any action phase method. Abstract descendants of this controller should follow similar behavior. If there are parameters you need inrenderFormSubmission, then you need to pass those forward fromprocessFormSubmission. If you override the default behavior of invalid submits and you set sessionForm to true, then you probably will not need to set this because your parameters are only going to be needed on the first request.