Source Code Cross Referenced for AbstractFormController.java in  » J2EE » spring-framework-2.0.6 » org » springframework » web » portlet » mvc » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » J2EE » spring framework 2.0.6 » org.springframework.web.portlet.mvc 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         * Copyright 2002-2007 the original author or authors.
0003:         *
0004:         * Licensed under the Apache License, Version 2.0 (the "License");
0005:         * you may not use this file except in compliance with the License.
0006:         * You may obtain a copy of the License at
0007:         *
0008:         *      http://www.apache.org/licenses/LICENSE-2.0
0009:         *
0010:         * Unless required by applicable law or agreed to in writing, software
0011:         * distributed under the License is distributed on an "AS IS" BASIS,
0012:         * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0013:         * See the License for the specific language governing permissions and
0014:         * limitations under the License.
0015:         */
0016:
0017:        package org.springframework.web.portlet.mvc;
0018:
0019:        import java.util.Arrays;
0020:        import java.util.Map;
0021:
0022:        import javax.portlet.ActionRequest;
0023:        import javax.portlet.ActionResponse;
0024:        import javax.portlet.PortletException;
0025:        import javax.portlet.PortletRequest;
0026:        import javax.portlet.PortletSession;
0027:        import javax.portlet.RenderRequest;
0028:        import javax.portlet.RenderResponse;
0029:
0030:        import org.springframework.validation.BindException;
0031:        import org.springframework.validation.Errors;
0032:        import org.springframework.web.portlet.ModelAndView;
0033:        import org.springframework.web.portlet.bind.PortletRequestDataBinder;
0034:        import org.springframework.web.portlet.handler.PortletSessionRequiredException;
0035:
0036:        /**
0037:         * <p>Form controller that auto-populates a form bean from the request.
0038:         * This, either using a new bean instance per request, or using the same bean
0039:         * when the <code>sessionForm</code> property has been set to
0040:         * <code>true</code>.</p>
0041:         *
0042:         * <p>This class is the base class for both framework subclasses like
0043:         * {@link SimpleFormController SimpleFormController} and
0044:         * {@link AbstractWizardFormController AbstractWizardFormController}, and
0045:         * custom form controllers you can provide yourself.</p>
0046:         *
0047:         * <p>Both form-input-views and after-submission-views have to be provided
0048:         * programmatically. To provide those views using configuration properties,
0049:         * use the {@link SimpleFormController SimpleFormController}.</p>
0050:         *
0051:         * <p>Subclasses need to override <code>showForm</code> to prepare the form view,
0052:         * <code>processFormSubmission</code> to handle submit requests, and
0053:         * <code>renderFormSubmission</code> to display the results of the submit.
0054:         * For the latter two methods, binding errors like type mismatches will be
0055:         * reported via the given "errors" holder. For additional custom form validation,
0056:         * a validator (property inherited from BaseCommandController) can be used,
0057:         * reporting via the same "errors" instance.</p>
0058:         *
0059:         * <p>Comparing this Controller to the Struts notion of the <code>Action</code>
0060:         * shows us that with Spring, you can use any ordinary JavaBeans or database-
0061:         * backed JavaBeans without having to implement a framework-specific class
0062:         * (like Struts' <code>ActionForm</code>). More complex properties of JavaBeans
0063:         * (Dates, Locales, but also your own application-specific or compound types)
0064:         * can be represented and submitted to the controller, by using the notion of
0065:         * a <code>java.beans.PropertyEditors</code>. For more information on that
0066:         * subject, see the workflow of this controller and the explanation of the
0067:         * {@link BaseCommandController BaseCommandController}.</p>
0068:         *
0069:         * This controller is different from it's servlet counterpart in that it must take
0070:
0071:         * into account the two phases of a portlet request: the action phase and the render
0072:         * phase.  See the JSR-168 spec for more details on these two phases.
0073:         * Be especially aware that the action phase is called only once, but that the
0074:         * render phase will be called repeatedly by the portal -- it does this every time
0075:         * the page containing the portlet is updated, even if the activity is in some other
0076:         * portlet.  (This is not quite true, the portal can also be told to cache the results of
0077:         * the render for a period of time, but assume it is true for programming purposes.)
0078:         *
0079:         * <p><b><a name="workflow">Workflow
0080:         * (<a href="BaseCommandController.html#workflow">and that defined by superclass</a>):</b><br>
0081:         * <ol>
0082:         * <li><b>The controller receives a request for a new form (typically a
0083:         * Render Request only).</b>  The render phase will proceed to display
0084:         * the form as follows.</li>
0085:         * <li>Call to {@link #formBackingObject formBackingObject()} which by
0086:         * default, returns an instance of the commandClass that has been
0087:         * configured (see the properties the superclass exposes), but can also be
0088:         * overridden to e.g. retrieve an object from the database (that needs to
0089:         * be modified using the form).</li>
0090:         * <li>Call to {@link #initBinder initBinder()} which allows you to
0091:         * register custom editors for certain fields (often properties of non-
0092:         * primitive or non-String types) of the command class. This will render
0093:         * appropriate Strings for those property values, e.g. locale-specific
0094:         * date strings. </li>
0095:         * <li>The {@link PortletRequestDataBinder PortletRequestDataBinder}
0096:         * gets applied to populate the new form object with initial request parameters and the
0097:         * {@link #onBindOnNewForm(RenderRequest, Object, BindException)} callback method is invoked.
0098:         * (<i>only if <code>bindOnNewForm</code> is set to <code>true</code></i>)
0099:         * Make sure that the initial parameters do not include the parameter that indicates a
0100:         * form submission has occurred.</li>
0101:         * <li>Call to {@link #showForm(RenderRequest, RenderResponse,
0102:         * BindException) showForm} to return a View that should be rendered
0103:         * (typically the view that renders the form). This method has to be
0104:         * implemented in subclasses. </li>
0105:         * <li>The showForm() implementation will call {@link #referenceData referenceData},
0106:         * which you can implement to provide any relevant reference data you might need
0107:         * when editing a form (e.g. a List of Locale objects you're going to let the
0108:         * user select one from).</li>
0109:         * <li>Model gets exposed and view gets rendered, to let the user fill in
0110:         * the form.</li>
0111:         * <li><b>The controller receives a form submission (typically an Action
0112:         * Request).</b> To use a different way of detecting a form submission,
0113:         * override the {@link #isFormSubmission isFormSubmission} method.
0114:         * The action phase will proceed to process the form submission as follows.</li>
0115:         * <li>If <code>sessionForm</code> is not set, {@link #formBackingObject
0116:         * formBackingObject} is called to retrieve a form object. Otherwise,
0117:         * the controller tries to find the command object which is already bound
0118:         * in the session. If it cannot find the object, the action phase does a
0119:         * call to {@link #handleInvalidSubmit handleInvalidSubmit} which - by default -
0120:         * tries to create a new form object and resubmit the form.  It then sets
0121:         * a render parameter that will indicate to the render phase that this was
0122:         * an invalid submit.</li>
0123:         * <li>Still in the action phase of a valid submit, the {@link
0124:         * PortletRequestDataBinder PortletRequestDataBinder} gets applied to populate
0125:         * the form object with current request parameters.</li>
0126:         * <li>Call to {@link #onBind onBind(PortletRequest, Object, Errors)}
0127:         * which allows you to do custom processing after binding but before
0128:         * validation (e.g. to manually bind request parameters to bean
0129:         * properties, to be seen by the Validator).</li>
0130:         * <li>If <code>validateOnBinding</code> is set, a registered Validator
0131:         * will be invoked. The Validator will check the form object properties,
0132:         * and register corresponding errors via the given {@link Errors Errors}
0133:         * object.</li>
0134:         * <li>Call to {@link #onBindAndValidate onBindAndValidate} which allows
0135:         * you to do custom processing after binding and validation (e.g. to
0136:         * manually bind request parameters, and to validate them outside a
0137:         * Validator).</li>
0138:         * <li>Call to {@link #processFormSubmission processFormSubmission}
0139:         * to process the submission, with or without binding errors.
0140:         * This method has to be implemented in subclasses and will be called
0141:         * only once per form submission.</li>
0142:         * <li>The portal will then call the render phase of processing the form
0143:         * submission.  This phase will be called repeatedly by the portal every
0144:         * time the page is refreshed.  All processing here should take this into
0145:         * account.  Any one-time-only actions (such as modifying a database) must
0146:         * be done in the action phase.</li>
0147:         * <li>If the action phase indicated this is an invalid submit, the render
0148:         * phase calls {@link #renderInvalidSubmit renderInvalidSubmit} which &ndash;
0149:         * also by default &ndash; will render the results of the resubmitted
0150:         * form.  Be sure to override both <code>handleInvalidSubmit</code> and
0151:         * <code>renderInvalidSubmit</code> if you want to change this overall
0152:         * behavior.</li>
0153:         * <li>Finally, call {@link #renderFormSubmission renderFormSubmission} to
0154:         * render the results of the submission, with or without binding errors.
0155:         * This method has to be implemented in subclasses and will be called
0156:         * repeatedly by the portal.</li>
0157:         * </ol>
0158:         * </p>
0159:         *
0160:         * <p>In session form mode, a submission without an existing form object in the
0161:         * session is considered invalid, like in the case of a resubmit/reload by the browser.
0162:         * The {@link #handleInvalidSubmit handleInvalidSubmit} /
0163:         * {@link #renderInvalidSubmit renderInvalidSubmit} methods are invoked then,
0164:         * by default trying to resubmit. This can be overridden in subclasses to show
0165:         * corresponding messages or to redirect to a new form, in order to avoid duplicate
0166:         * submissions. The form object in the session can be considered a transaction token
0167:         * in that case.</p>
0168:         *
0169:         * Make sure that any URLs that take you to your form controller are Render URLs, so
0170:         * that it will not try to treat the initial call as a form submission.  If you use
0171:         * Action URLs to link to your controller, you will need to override the
0172:         * {@link #isFormSubmission isFormSubmission} method to use a different mechanism for
0173:         * determining whether a form has been submitted.  Make sure this method will work for
0174:         * both the ActionRequest and the RenderRequest objects.
0175:         *
0176:         * <p>Note that views should never retrieve form beans from the session but always
0177:         * from the request, as prepared by the form controller. Remember that some view
0178:         * technologies like Velocity cannot even access a HTTP session.</p>
0179:         *
0180:         * <p><b><a name="config">Exposed configuration properties</a>
0181:         * (<a href="BaseCommandController.html#config">and those defined by superclass</a>):</b><br>
0182:         * <table border="1">
0183:         * <tr>
0184:         * <td><b>name</b></td>
0185:         * <td><b>default</b></td>
0186:         * <td><b>description</b></td>
0187:         * </tr>
0188:         * <tr>
0189:         * <td>bindOnNewForm</td>
0190:         * <td>false</td>
0191:         * <td>Indicates whether to bind portlet request parameters when
0192:         * creating a new form. Otherwise, the parameters will only be
0193:         * bound on form submission attempts.</td>
0194:         * </tr>
0195:         * <tr>
0196:         * <td>sessionForm</td>
0197:         * <td>false</td>
0198:         * <td>Indicates whether the form object should be kept in the session
0199:         * when a user asks for a new form. This allows you e.g. to retrieve
0200:         * an object from the database, let the user edit it, and then persist
0201:         * it again. Otherwise, a new command object will be created for each
0202:         * request (even when showing the form again after validation errors).</td>
0203:         * </tr>
0204:         * <tr>
0205:         * <td>redirectAction</td>
0206:         * <td>false</td>
0207:         * <td>Specifies whether <code>processFormSubmission</code> is expected to call
0208:         * {@link ActionResponse#sendRedirect ActionResponse.sendRedirect}.
0209:         * This is important because some methods may not be called before
0210:         * {@link ActionResponse#sendRedirect ActionResponse.sendRedirect} (e.g.
0211:         * {@link ActionResponse#setRenderParameter ActionResponse.setRenderParameter}).
0212:         * Setting this flag will prevent AbstractFormController from setting render
0213:         * parameters that it normally needs for the render phase.
0214:         * If this is set true and <code>sendRedirect</code> is not called, then
0215:         * <code>processFormSubmission</code> must call
0216:         * {@link #setFormSubmit setFormSubmit}.
0217:         * Otherwise, the render phase will not realize the form was submitted
0218:         * and will simply display a new blank form.</td>
0219:         * </tr>
0220:         * <tr>
0221:         * <td>renderParameters</td>
0222:         * <td>null</td>
0223:         * <td>An array of parameters that will be passed forward from the action
0224:         * phase to the render phase if the form needs to be displayed
0225:         * again.  These can also be passed forward explicitly by calling
0226:         * the <code>passRenderParameters</code> method from any action
0227:         * phase method.  Abstract descendants of this controller should follow
0228:         * similar behavior.  If there are parameters you need in
0229:         * <code>renderFormSubmission</code>, then you need to pass those
0230:         * forward from <code>processFormSubmission</code>.  If you override the
0231:         * default behavior of invalid submits and you set sessionForm to true,
0232:         * then you probably will not need to set this because your parameters
0233:         * are only going to be needed on the first request.</td>
0234:         * </tr>
0235:         * </table>
0236:         * </p>
0237:         *
0238:         * <p>Thanks to Rainer Schmitz and Nick Lothian for their suggestions!
0239:         *
0240:         * @author John A. Lewis
0241:         * @author Juergen Hoeller
0242:         * @author Alef Arendsen
0243:         * @author Rob Harrop
0244:         * @since 2.0
0245:         * @see #showForm(RenderRequest, RenderResponse, BindException)
0246:         * @see SimpleFormController
0247:         * @see AbstractWizardFormController
0248:         */
0249:        public abstract class AbstractFormController extends
0250:                BaseCommandController {
0251:
0252:            /**
0253:             * These render parameters are used to indicate forward to the render phase
0254:             * if the form was submitted and if the submission was invalid.
0255:             */
0256:            private static final String FORM_SUBMISSION_PARAMETER = "form-submit";
0257:
0258:            private static final String INVALID_SUBMISSION_PARAMETER = "invalid-submit";
0259:
0260:            private static final String TRUE = Boolean.TRUE.toString();
0261:
0262:            private boolean bindOnNewForm = false;
0263:
0264:            private boolean sessionForm = false;
0265:
0266:            private boolean redirectAction = false;
0267:
0268:            private String[] renderParameters = null;
0269:
0270:            /**
0271:             * Create a new AbstractFormController.
0272:             * <p>Subclasses should set the following properties, either in the constructor
0273:             * or via a BeanFactory: commandName, commandClass, bindOnNewForm, sessionForm.
0274:             * Note that commandClass doesn't need to be set when overriding
0275:             * <code>formBackingObject</code>, as the latter determines the class anyway.
0276:             * <p>"cacheSeconds" is by default set to 0 (-> no caching for all form controllers).
0277:             * @see #setCommandName
0278:             * @see #setCommandClass
0279:             * @see #setBindOnNewForm
0280:             * @see #setSessionForm
0281:             * @see #formBackingObject
0282:             */
0283:            public AbstractFormController() {
0284:                setCacheSeconds(0);
0285:            }
0286:
0287:            /**
0288:             * Set if request parameters should be bound to the form object
0289:             * in case of a non-submitting request, i.e. a new form.
0290:             */
0291:            public final void setBindOnNewForm(boolean bindOnNewForm) {
0292:                this .bindOnNewForm = bindOnNewForm;
0293:            }
0294:
0295:            /**
0296:             * Return if request parameters should be bound in case of a new form.
0297:             */
0298:            public final boolean isBindOnNewForm() {
0299:                return bindOnNewForm;
0300:            }
0301:
0302:            /**
0303:             * Activate/deactivate session form mode. In session form mode,
0304:             * the form is stored in the session to keep the form object instance
0305:             * between requests, instead of creating a new one on each request.
0306:             * <p>This is necessary for either wizard-style controllers that populate a
0307:             * single form object from multiple pages, or forms that populate a persistent
0308:             * object that needs to be identical to allow for tracking changes.
0309:             */
0310:            public final void setSessionForm(boolean sessionForm) {
0311:                this .sessionForm = sessionForm;
0312:            }
0313:
0314:            /**
0315:             * Return if session form mode is activated.
0316:             */
0317:            public final boolean isSessionForm() {
0318:                return sessionForm;
0319:            }
0320:
0321:            /**
0322:             * Specify whether the action phase is expected to call
0323:             * {@link ActionResponse#sendRedirect}.
0324:             * This information is important because some methods may not be called
0325:             * before {@link ActionResponse#sendRedirect}, e.g.
0326:             * {@link ActionResponse#setRenderParameter} and
0327:             * {@link ActionResponse#setRenderParameters}.
0328:             * @param redirectAction true if ActionResponse#sendRedirect is expected to be called
0329:             * @see ActionResponse#sendRedirect
0330:             */
0331:            public void setRedirectAction(boolean redirectAction) {
0332:                this .redirectAction = redirectAction;
0333:            }
0334:
0335:            /**
0336:             * Return if {@link ActionResponse#sendRedirect} is
0337:             * expected to be called in the action phase.
0338:             */
0339:            public boolean isRedirectAction() {
0340:                return redirectAction;
0341:            }
0342:
0343:            /**
0344:             * Specify the list of parameters that should be passed forward
0345:             * from the action phase to the render phase whenever the form is
0346:             * rerendered or when {@link #passRenderParameters} is called.
0347:             * @see #passRenderParameters
0348:             */
0349:            public void setRenderParameters(String[] parameters) {
0350:                this .renderParameters = parameters;
0351:            }
0352:
0353:            /**
0354:             * Returns the list of parameters that will be passed forward
0355:             * from the action phase to the render phase whenever the form is
0356:             * rerendered or when {@link #passRenderParameters} is called.
0357:             * @return the list of parameters
0358:             * @see #passRenderParameters
0359:             */
0360:            public String[] getRenderParameters() {
0361:                return renderParameters;
0362:            }
0363:
0364:            /**
0365:             * Handles action phase of two cases: form submissions and showing a new form.
0366:             * Delegates the decision between the two to <code>isFormSubmission</code>,
0367:             * always treating requests without existing form session attribute
0368:             * as new form when using session form mode.
0369:             * @see #isFormSubmission
0370:             * @see #processFormSubmission
0371:             * @see #handleRenderRequestInternal
0372:             */
0373:            protected void handleActionRequestInternal(ActionRequest request,
0374:                    ActionResponse response) throws Exception {
0375:
0376:                // Form submission or new form to show?
0377:                if (isFormSubmission(request)) {
0378:                    // Fetch form object, bind, validate, process submission.
0379:                    try {
0380:                        Object command = getCommand(request);
0381:                        if (logger.isDebugEnabled()) {
0382:                            logger
0383:                                    .debug("Processing valid submit (redirectAction = "
0384:                                            + isRedirectAction() + ")");
0385:                        }
0386:                        if (!isRedirectAction()) {
0387:                            setFormSubmit(response);
0388:                        }
0389:                        PortletRequestDataBinder binder = bindAndValidate(
0390:                                request, command);
0391:                        BindException errors = new BindException(binder
0392:                                .getBindingResult());
0393:                        processFormSubmission(request, response, command,
0394:                                errors);
0395:                        setRenderCommandAndErrors(request, command, errors);
0396:                        return;
0397:                    } catch (PortletSessionRequiredException ex) {
0398:                        // Cannot submit a session form if no form object is in the session.
0399:                        if (logger.isDebugEnabled()) {
0400:                            logger.debug("Invalid submit detected: "
0401:                                    + ex.getMessage());
0402:                        }
0403:                        setFormSubmit(response);
0404:                        setInvalidSubmit(response);
0405:                        handleInvalidSubmit(request, response);
0406:                        return;
0407:                    }
0408:                }
0409:
0410:                else {
0411:                    logger
0412:                            .debug("Not a form submit - passing parameters to render phase");
0413:                    passRenderParameters(request, response);
0414:                    return;
0415:                }
0416:            }
0417:
0418:            /**
0419:             * Handles render phase of two cases: form submissions and showing a new form.
0420:             * Delegates the decision between the two to <code>isFormSubmission</code>,
0421:             * always treating requests without existing form session attribute
0422:             * as new form when using session form mode.
0423:             * @see #isFormSubmission
0424:             * @see #showNewForm
0425:             * @see #processFormSubmission
0426:             * @see #handleActionRequestInternal
0427:             */
0428:            protected ModelAndView handleRenderRequestInternal(
0429:                    RenderRequest request, RenderResponse response)
0430:                    throws Exception {
0431:
0432:                // Form submission or new form to show?
0433:                if (isFormSubmission(request)) {
0434:
0435:                    // If it is an invalid submit then handle it.
0436:                    if (isInvalidSubmission(request)) {
0437:                        logger
0438:                                .debug("Invalid submit - calling renderInvalidSubmit");
0439:                        return renderInvalidSubmit(request, response);
0440:                    }
0441:
0442:                    // Valid submit -> render.
0443:                    logger.debug("Valid submit - calling renderFormSubmission");
0444:                    return renderFormSubmission(request, response,
0445:                            getRenderCommand(request), getRenderErrors(request));
0446:                }
0447:
0448:                else {
0449:                    // New form to show: render form view.
0450:                    return showNewForm(request, response);
0451:                }
0452:            }
0453:
0454:            /**
0455:             * Determine if the given request represents a form submission.
0456:             * <p>Default implementation checks to see if this is an ActionRequest
0457:             * and treats all action requests as form submission. During the action
0458:             * phase it will pass forward a render parameter to indicate to the render
0459:             * phase that this is a form submission. This method can check both
0460:             * kinds of requests and indicate if this is a form submission.
0461:             * <p>Subclasses can override this to use a custom strategy, e.g. a specific
0462:             * request parameter (assumably a hidden field or submit button name). Make
0463:             * sure that the override can handle both ActionRequest and RenderRequest
0464:             * objects properly.
0465:             * @param request current request
0466:             * @return if the request represents a form submission
0467:             */
0468:            protected boolean isFormSubmission(PortletRequest request) {
0469:                return (request instanceof  ActionRequest ? true : TRUE
0470:                        .equals(request
0471:                                .getParameter(getFormSubmitParameterName())));
0472:            }
0473:
0474:            /**
0475:             * Determine if the given request represents an invalid form submission.
0476:             */
0477:            protected boolean isInvalidSubmission(PortletRequest request) {
0478:                return TRUE.equals(request
0479:                        .getParameter(getInvalidSubmitParameterName()));
0480:            }
0481:
0482:            /**
0483:             * Return the name of the render parameter that indicates this
0484:             * was a form submission.
0485:             * @return the name of the render parameter
0486:             * @see javax.portlet.RenderRequest#getParameter
0487:             */
0488:            protected String getFormSubmitParameterName() {
0489:                return FORM_SUBMISSION_PARAMETER;
0490:            }
0491:
0492:            /**
0493:             * Return the name of the render parameter that indicates this
0494:             * was an invalid form submission.
0495:             * @return the name of the render parameter
0496:             * @see javax.portlet.RenderRequest#getParameter
0497:             */
0498:            protected String getInvalidSubmitParameterName() {
0499:                return INVALID_SUBMISSION_PARAMETER;
0500:            }
0501:
0502:            /**
0503:             * Set the action response parameter that indicates this in a form submission.
0504:             * @param response the current action response
0505:             * @see #getFormSubmitParameterName()
0506:             */
0507:            protected final void setFormSubmit(ActionResponse response) {
0508:                if (logger.isDebugEnabled()) {
0509:                    logger.debug("Setting render parameter ["
0510:                            + getFormSubmitParameterName()
0511:                            + "] to indicate this is a form submission");
0512:                }
0513:                try {
0514:                    response.setRenderParameter(getFormSubmitParameterName(),
0515:                            TRUE);
0516:                } catch (IllegalStateException ex) {
0517:                    // Ignore in case sendRedirect was already set.
0518:                }
0519:            }
0520:
0521:            /**
0522:             * Set the action response parameter that indicates this in an invalid submission.
0523:             * @param response the current action response
0524:             * @see #getInvalidSubmitParameterName()
0525:             */
0526:            protected final void setInvalidSubmit(ActionResponse response) {
0527:                if (logger.isDebugEnabled()) {
0528:                    logger.debug("Setting render parameter ["
0529:                            + getInvalidSubmitParameterName()
0530:                            + "] to indicate this is an invalid submission");
0531:                }
0532:                try {
0533:                    response.setRenderParameter(
0534:                            getInvalidSubmitParameterName(), TRUE);
0535:                } catch (IllegalStateException ex) {
0536:                    // Ignore in case sendRedirect was already set.
0537:                }
0538:            }
0539:
0540:            /**
0541:             * Return the name of the PortletSession attribute that holds the form object
0542:             * for this form controller.
0543:             * <p>Default implementation delegates to the <code>getFormSessionAttributeName</code>
0544:             * version without arguments.
0545:             * @param request current HTTP request
0546:             * @return the name of the form session attribute, or null if not in session form mode
0547:             * @see #getFormSessionAttributeName
0548:             * @see javax.portlet.PortletSession#getAttribute
0549:             */
0550:            protected String getFormSessionAttributeName(PortletRequest request) {
0551:                return getFormSessionAttributeName();
0552:            }
0553:
0554:            /**
0555:             * Return the name of the PortletSession attribute that holds the form object
0556:             * for this form controller.
0557:             * <p>Default is an internal name, of no relevance to applications, as the form
0558:             * session attribute is not usually accessed directly. Can be overridden to use
0559:             * an application-specific attribute name, which allows other code to access
0560:             * the session attribute directly.
0561:             * @return the name of the form session attribute
0562:             * @see javax.portlet.PortletSession#getAttribute
0563:             */
0564:            protected String getFormSessionAttributeName() {
0565:                return getClass().getName() + ".FORM." + getCommandName();
0566:            }
0567:
0568:            /**
0569:             * Pass the specified list of action request parameters to the render phase
0570:             * by putting them into the action response object. This may not be called
0571:             * when the action will call will call
0572:             * {@link ActionResponse#sendRedirect sendRedirect}.
0573:             * @param request the current action request
0574:             * @param response the current action response
0575:             * @see ActionResponse#setRenderParameter
0576:             */
0577:            protected void passRenderParameters(ActionRequest request,
0578:                    ActionResponse response) {
0579:                if (this .renderParameters == null) {
0580:                    return;
0581:                }
0582:                try {
0583:                    for (int i = 0; i < this .renderParameters.length; i++) {
0584:                        String paramName = this .renderParameters[i];
0585:                        String paramValues[] = request
0586:                                .getParameterValues(paramName);
0587:                        if (paramValues != null) {
0588:                            if (logger.isDebugEnabled()) {
0589:                                logger
0590:                                        .debug("Passing parameter to render phase '"
0591:                                                + paramName
0592:                                                + "' = "
0593:                                                + (paramValues == null ? "NULL"
0594:                                                        : Arrays.asList(
0595:                                                                paramValues)
0596:                                                                .toString()));
0597:                            }
0598:                            response.setRenderParameter(paramName, paramValues);
0599:                        }
0600:                    }
0601:                } catch (IllegalStateException ex) {
0602:                    // Ignore in case sendRedirect was already set.
0603:                }
0604:            }
0605:
0606:            /**
0607:             * Show a new form. Prepares a backing object for the current form
0608:             * and the given request, including checking its validity.
0609:             * @param request current render request
0610:             * @param response current render response
0611:             * @return the prepared form view
0612:             * @throws Exception in case of an invalid new form object
0613:             * @see #getErrorsForNewForm
0614:             */
0615:            protected final ModelAndView showNewForm(RenderRequest request,
0616:                    RenderResponse response) throws Exception {
0617:
0618:                logger.debug("Displaying new form");
0619:                return showForm(request, response, getErrorsForNewForm(request));
0620:            }
0621:
0622:            /**
0623:             * Create a BindException instance for a new form.
0624:             * Called by <code>showNewForm</code>.
0625:             * <p>Can be used directly when intending to show a new form but with
0626:             * special errors registered on it (for example, on invalid submit).
0627:             * Usually, the resulting BindException will be passed to
0628:             * <code>showForm</code>, after registering the errors on it.
0629:             * @param request current render request
0630:             * @return the BindException instance
0631:             * @throws Exception in case of an invalid new form object
0632:             */
0633:            protected final BindException getErrorsForNewForm(
0634:                    RenderRequest request) throws Exception {
0635:                // Create form-backing object for new form
0636:                Object command = formBackingObject(request);
0637:                if (command == null) {
0638:                    throw new PortletException(
0639:                            "Form object returned by formBackingObject() must not be null");
0640:                }
0641:                if (!checkCommand(command)) {
0642:                    throw new PortletException(
0643:                            "Form object returned by formBackingObject() must match commandClass");
0644:                }
0645:
0646:                // Bind without validation, to allow for prepopulating a form, and for
0647:                // convenient error evaluation in views (on both first attempt and resubmit).
0648:                PortletRequestDataBinder binder = createBinder(request, command);
0649:                BindException errors = new BindException(binder
0650:                        .getBindingResult());
0651:
0652:                if (isBindOnNewForm()) {
0653:                    if (logger.isDebugEnabled()) {
0654:                        logger.debug("Binding to new form");
0655:                    }
0656:                    binder.bind(request);
0657:                    onBindOnNewForm(request, command, errors);
0658:                }
0659:
0660:                // Return BindException object that resulted from binding.
0661:                return errors;
0662:            }
0663:
0664:            /**
0665:             * Callback for custom post-processing in terms of binding for a new form.
0666:             * Called when preparing a new form if <code>bindOnNewForm</code> is <code>true</code>.
0667:             * <p>Default implementation delegates to <code>onBindOnNewForm(request, command)</code>.
0668:             * @param request current render request
0669:             * @param command the command object to perform further binding on
0670:             * @param errors validation errors holder, allowing for additional
0671:             * custom registration of binding errors
0672:             * @throws Exception in case of invalid state or arguments
0673:             * @see #onBindOnNewForm(RenderRequest, Object)
0674:             * @see #setBindOnNewForm
0675:             */
0676:            protected void onBindOnNewForm(RenderRequest request,
0677:                    Object command, BindException errors) throws Exception {
0678:
0679:                onBindOnNewForm(request, command);
0680:            }
0681:
0682:            /**
0683:             * Callback for custom post-processing in terms of binding for a new form.
0684:             * Called by the default implementation of the <code>onBindOnNewForm</code> version
0685:             * with all parameters, after standard binding when displaying the form view.
0686:             * Only called if <code>bindOnNewForm</code> is set to <code>true</code>.
0687:             * <p>Default implementation is empty.
0688:             * @param request current render request
0689:             * @param command the command object to perform further binding on
0690:             * @throws Exception in case of invalid state or arguments
0691:             * @see #onBindOnNewForm(RenderRequest, Object, BindException)
0692:             * @see #setBindOnNewForm(boolean)
0693:             */
0694:            protected void onBindOnNewForm(RenderRequest request, Object command)
0695:                    throws Exception {
0696:            }
0697:
0698:            /**
0699:             * Return the form object for the given request.
0700:             * <p>Calls <code>formBackingObject</code> if the object is not in the session
0701:             * @param request current request
0702:             * @return object form to bind onto
0703:             * @see #formBackingObject
0704:             */
0705:            protected final Object getCommand(PortletRequest request)
0706:                    throws Exception {
0707:                // If not in session-form mode, create a new form-backing object.
0708:                if (!isSessionForm()) {
0709:                    if (logger.isDebugEnabled()) {
0710:                        logger
0711:                                .debug("Not a session-form -- using new formBackingObject");
0712:                    }
0713:                    return formBackingObject(request);
0714:                }
0715:
0716:                // Session-form mode: retrieve form object from portlet session attribute.
0717:                PortletSession session = request.getPortletSession(false);
0718:                if (session == null) {
0719:                    throw new PortletSessionRequiredException(
0720:                            "Must have session when trying to bind (in session-form mode)");
0721:                }
0722:                String formAttrName = getFormSessionAttributeName(request);
0723:                Object sessionFormObject = session.getAttribute(formAttrName);
0724:                if (sessionFormObject == null) {
0725:                    throw new PortletSessionRequiredException(
0726:                            "Form object not found in session (in session-form mode)");
0727:                }
0728:
0729:                // Remove form object from porlet session: we might finish the form workflow
0730:                // in this request. If it turns out that we need to show the form view again,
0731:                // we'll re-bind the form object to the portlet session.
0732:                if (logger.isDebugEnabled()) {
0733:                    logger.debug("Removing form session attribute ["
0734:                            + formAttrName + "]");
0735:                }
0736:                session.removeAttribute(formAttrName);
0737:
0738:                // Check the command object to make sure its valid
0739:                if (!checkCommand(sessionFormObject)) {
0740:                    throw new PortletSessionRequiredException(
0741:                            "Object found in session does not match commandClass");
0742:                }
0743:
0744:                return sessionFormObject;
0745:            }
0746:
0747:            /**
0748:             * Retrieve a backing object for the current form from the given request.
0749:             * <p>The properties of the form object will correspond to the form field values
0750:             * in your form view. This object will be exposed in the model under the specified
0751:             * command name, to be accessed under that name in the view: for example, with
0752:             * a "spring:bind" tag. The default command name is "command".
0753:             * <p>Note that you need to activate session form mode to reuse the form-backing
0754:             * object across the entire form workflow. Else, a new instance of the command
0755:             * class will be created for each submission attempt, just using this backing
0756:             * object as template for the initial form.
0757:             * <p>Default implementation calls <code>BaseCommandController.createCommand</code>,
0758:             * creating a new empty instance of the command class.
0759:             * Subclasses can override this to provide a preinitialized backing object.
0760:             * @param request current portlet request
0761:             * @return the backing object
0762:             * @throws Exception in case of invalid state or arguments
0763:             * @see #setCommandName
0764:             * @see #setCommandClass
0765:             * @see #createCommand
0766:             */
0767:            protected Object formBackingObject(PortletRequest request)
0768:                    throws Exception {
0769:                return createCommand();
0770:            }
0771:
0772:            /**
0773:             * Prepare the form model and view, including reference and error data.
0774:             * Can show a configured form page, or generate a form view programmatically.
0775:             * <p>A typical implementation will call
0776:             * <code>showForm(request, errors, "myView")</code>
0777:             * to prepare the form view for a specific view name, returning the
0778:             * ModelAndView provided there.
0779:             * <p>For building a custom ModelAndView, call <code>errors.getModel()</code>
0780:             * to populate the ModelAndView model with the command and the Errors instance,
0781:             * under the specified command name, as expected by the "spring:bind" tag.
0782:             * You also need to include the model returned by <code>referenceData</code>.
0783:             * <p>Note: If you decide to have a "formView" property specifying the
0784:             * view name, consider using SimpleFormController.
0785:             * @param request current render request
0786:             * @param response current render response
0787:             * @param errors validation errors holder
0788:             * @return the prepared form view, or null if handled directly
0789:             * @throws Exception in case of invalid state or arguments
0790:             * @see #showForm(RenderRequest, BindException, String)
0791:             * @see org.springframework.validation.Errors
0792:             * @see org.springframework.validation.BindException#getModel
0793:             * @see #referenceData(PortletRequest, Object, Errors)
0794:             * @see SimpleFormController#setFormView
0795:             */
0796:            protected abstract ModelAndView showForm(RenderRequest request,
0797:                    RenderResponse response, BindException errors)
0798:                    throws Exception;
0799:
0800:            /**
0801:             * Prepare model and view for the given form, including reference and errors.
0802:             * <p>In session form mode: Re-puts the form object in the session when
0803:             * returning to the form, as it has been removed by getCommand.
0804:             * <p>Can be used in subclasses to redirect back to a specific form page.
0805:             * @param request current render request
0806:             * @param errors validation errors holder
0807:             * @param viewName name of the form view
0808:             * @return the prepared form view
0809:             * @throws Exception in case of invalid state or arguments
0810:             * @see #showForm(RenderRequest, BindException, String, Map)
0811:             * @see #showForm(RenderRequest, RenderResponse, BindException)
0812:             */
0813:            protected final ModelAndView showForm(RenderRequest request,
0814:                    BindException errors, String viewName) throws Exception {
0815:
0816:                return showForm(request, errors, viewName, null);
0817:            }
0818:
0819:            /**
0820:             * Prepare model and view for the given form, including reference and errors,
0821:             * adding a controller-specific control model.
0822:             * <p>In session form mode: Re-puts the form object in the session when returning
0823:             * to the form, as it has been removed by getCommand.
0824:             * <p>Can be used in subclasses to redirect back to a specific form page.
0825:             * @param request current render request
0826:             * @param errors validation errors holder
0827:             * @param viewName name of the form view
0828:             * @param controlModel model map containing controller-specific control data
0829:             * (e.g. current page in wizard-style controllers or special error message)
0830:             * @return the prepared form view
0831:             * @throws Exception in case of invalid state or arguments
0832:             * @see #showForm(RenderRequest, BindException, String)
0833:             * @see #showForm(RenderRequest, RenderResponse, BindException)
0834:             */
0835:            protected final ModelAndView showForm(RenderRequest request,
0836:                    BindException errors, String viewName, Map controlModel)
0837:                    throws Exception {
0838:
0839:                // In session form mode, re-expose form object as portlet session attribute.
0840:                // Re-binding is necessary for proper state handling in a cluster,
0841:                // to notify other nodes of changes in the form object.
0842:                if (isSessionForm()) {
0843:                    String formAttrName = getFormSessionAttributeName(request);
0844:                    if (logger.isDebugEnabled()) {
0845:                        logger.debug("Setting form session attribute ["
0846:                                + formAttrName + "] to: " + errors.getTarget());
0847:                    }
0848:                    request.getPortletSession().setAttribute(formAttrName,
0849:                            errors.getTarget());
0850:                }
0851:
0852:                // Fetch errors model as starting point, containing form object under
0853:                // "commandName", and corresponding Errors instance under internal key.
0854:                Map model = errors.getModel();
0855:
0856:                // Merge reference data into model, if any.
0857:                Map referenceData = referenceData(request, errors.getTarget(),
0858:                        errors);
0859:                if (referenceData != null) {
0860:                    model.putAll(referenceData);
0861:                }
0862:
0863:                // Merge control attributes into model, if any.
0864:                if (controlModel != null) {
0865:                    model.putAll(controlModel);
0866:                }
0867:
0868:                // Trigger rendering of the specified view, using the final model.
0869:                return new ModelAndView(viewName, model);
0870:            }
0871:
0872:            /**
0873:             * Create a reference data map for the given request, consisting of
0874:             * bean name/bean instance pairs as expected by ModelAndView.
0875:             * <p>Default implementation returns null.
0876:             * Subclasses can override this to set reference data used in the view.
0877:             * @param request current render request
0878:             * @param command form object with request parameters bound onto it
0879:             * @param errors validation errors holder
0880:             * @return a Map with reference data entries, or null if none
0881:             * @throws Exception in case of invalid state or arguments
0882:             * @see ModelAndView
0883:             */
0884:            protected Map referenceData(PortletRequest request, Object command,
0885:                    Errors errors) throws Exception {
0886:                return null;
0887:            }
0888:
0889:            /**
0890:             * Process render phase of form submission request. Called by <code>handleRequestInternal</code>
0891:             * in case of a form submission, with or without binding errors. Implementations
0892:             * need to proceed properly, typically showing a form view in case of binding
0893:             * errors or rendering the result of a submit action else.
0894:             * <p>For a success view, call <code>errors.getModel()</code> to populate the
0895:             * ModelAndView model with the command and the Errors instance, under the
0896:             * specified command name, as expected by the "spring:bind" tag. For a form view,
0897:             * simply return the ModelAndView object privded by <code>showForm</code>.
0898:             * @param request current render request
0899:             * @param response current render response
0900:             * @param command form object with request parameters bound onto it
0901:             * @param errors errors holder
0902:             * @return the prepared model and view, or null
0903:             * @throws Exception in case of errors
0904:             * @see #handleRenderRequestInternal
0905:             * @see #processFormSubmission
0906:             * @see #isFormSubmission
0907:             * @see #showForm(RenderRequest, RenderResponse, BindException)
0908:             * @see org.springframework.validation.Errors
0909:             * @see org.springframework.validation.BindException#getModel
0910:             */
0911:            protected abstract ModelAndView renderFormSubmission(
0912:                    RenderRequest request, RenderResponse response,
0913:                    Object command, BindException errors) throws Exception;
0914:
0915:            /**
0916:             * Process action phase of form submission request. Called by <code>handleRequestInternal</code>
0917:             * in case of a form submission, with or without binding errors. Implementations
0918:             * need to proceed properly, typically performing a submit action if there are no binding errors.
0919:             * <p>Subclasses can implement this to provide custom submission handling
0920:             * like triggering a custom action. They can also provide custom validation
0921:             * or proceed with the submission accordingly.
0922:             * @param request current action request
0923:             * @param response current action response
0924:             * @param command form object with request parameters bound onto it
0925:             * @param errors errors holder (subclass can add errors if it wants to)
0926:             * @throws Exception in case of errors
0927:             * @see #handleActionRequestInternal
0928:             * @see #renderFormSubmission
0929:             * @see #isFormSubmission
0930:             * @see org.springframework.validation.Errors
0931:             */
0932:            protected abstract void processFormSubmission(
0933:                    ActionRequest request, ActionResponse response,
0934:                    Object command, BindException errors) throws Exception;
0935:
0936:            /**
0937:             * Handle an invalid submit request, e.g. when in session form mode but no form object
0938:             * was found in the session (like in case of an invalid resubmit by the browser).
0939:             * <p>Default implementation simply tries to resubmit the form with a new form object.
0940:             * This should also work if the user hit the back button, changed some form data,
0941:             * and resubmitted the form.
0942:             * <p>Note: To avoid duplicate submissions, you need to override this method.
0943:             * Either show some "invalid submit" message, or call <code>showNewForm</code> for
0944:             * resetting the form (prepopulating it with the current values if "bindOnNewForm"
0945:             * is true). In this case, the form object in the session serves as transaction token.
0946:             * <pre>
0947:             * protected ModelAndView handleInvalidSubmit(RenderRequest request, RenderResponse response) throws Exception {
0948:             *   return showNewForm(request, response);
0949:             * }</pre>
0950:             * You can also show a new form but with special errors registered on it:
0951:             * <pre>
0952:             * protected ModelAndView handleInvalidSubmit(RenderRequest request, RenderResponse response) throws Exception {
0953:             *   BindException errors = getErrorsForNewForm(request);
0954:             *   errors.reject("duplicateFormSubmission", "Duplicate form submission");
0955:             *   return showForm(request, response, errors);
0956:             * }</pre>
0957:             * <p><b>WARNING:</b> If you override this method, be sure to also override the action
0958:             * phase version of this method so that it will not attempt to perform the resubmit
0959:             * action by default.
0960:             * @param request current render request
0961:             * @param response current render response
0962:             * @return a prepared view, or null if handled directly
0963:             * @throws Exception in case of errors
0964:             * @see #handleInvalidSubmit
0965:             */
0966:            protected ModelAndView renderInvalidSubmit(RenderRequest request,
0967:                    RenderResponse response) throws Exception {
0968:
0969:                return renderFormSubmission(request, response,
0970:                        getRenderCommand(request), getRenderErrors(request));
0971:            }
0972:
0973:            /**
0974:             * Handle an invalid submit request, e.g. when in session form mode but no form object
0975:             * was found in the session (like in case of an invalid resubmit by the browser).
0976:             * <p>Default implementation simply tries to resubmit the form with a new form object.
0977:             * This should also work if the user hit the back button, changed some form data,
0978:             * and resubmitted the form.
0979:             * <p>Note: To avoid duplicate submissions, you need to override this method.
0980:             * Most likely you will simply want it to do nothing here in the action phase
0981:             * and diplay an appropriate error and a new form in the render phase.
0982:             * <pre>
0983:             * protected void handleInvalidSubmit(ActionRequest request, ActionResponse response) throws Exception {
0984:             * }</pre>
0985:             * <p>If you override this method but you do need a command object and bind errors
0986:             * in the render phase, be sure to call {@link #setRenderCommandAndErrors setRenderCommandAndErrors}
0987:             * from here.
0988:             * @param request current action request
0989:             * @param response current action response
0990:             * @throws Exception in case of errors
0991:             * @see #renderInvalidSubmit
0992:             * @see #setRenderCommandAndErrors
0993:             */
0994:            protected void handleInvalidSubmit(ActionRequest request,
0995:                    ActionResponse response) throws Exception {
0996:                passRenderParameters(request, response);
0997:                Object command = formBackingObject(request);
0998:                if (command == null) {
0999:                    throw new PortletException(
1000:                            "Form object returned by formBackingObject() must not be null");
1001:                }
1002:                if (!checkCommand(command)) {
1003:                    throw new PortletException(
1004:                            "Form object returned by formBackingObject() must match commandClass");
1005:                }
1006:                PortletRequestDataBinder binder = bindAndValidate(request,
1007:                        command);
1008:                BindException errors = new BindException(binder
1009:                        .getBindingResult());
1010:                processFormSubmission(request, response, command, errors);
1011:                setRenderCommandAndErrors(request, command, errors);
1012:            }
1013:
1014:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.