001: /*
002: * Copyright 2002-2007 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.springframework.web.servlet.mvc;
018:
019: import javax.servlet.http.HttpServletRequest;
020: import javax.servlet.http.HttpServletResponse;
021:
022: import org.springframework.validation.BindException;
023: import org.springframework.web.servlet.ModelAndView;
024: import org.springframework.web.util.WebUtils;
025:
026: /**
027: * <p>Extension of <code>SimpleFormController</code> that supports "cancellation"
028: * of form processing. By default, this controller looks for a given parameter in the
029: * request, identified by the <code>cancelParamKey<code>. If this parameter is present,
030: * then the controller will return the configured <code>cancelView</code>, otherwise
031: * processing is passed back to the superclass.</p>
032: *
033: * <p><b><a name="workflow">Workflow
034: * (<a href="SimpleFormController.html#workflow">in addition to the superclass</a>):</b><br>
035: * <ol>
036: * <li>Call to {@link #processFormSubmission processFormSubmission} which calls
037: * {@link #isCancelRequest} to see if the incoming request is to cancel the
038: * current form entry. By default, {@link #isCancelRequest} returns <code>true</code>
039: * if the configured <code>cancelParamKey</code> exists in the request.
040: * This behavior can be overridden in subclasses.</li>
041: * <li>If {@link #isCancelRequest} returns <code>false</code>, then the controller
042: * will delegate all processing back to {@link SimpleFormController SimpleFormController},
043: * otherwise it will call the {@link #onCancel} version with all parameters.
044: * By default, that method will delegate to the {@link #onCancel} version with just
045: * the command object, which will in turn simply return the configured
046: * <code>cancelView</code>. This behavior can be overridden in subclasses.</li>
047: * </ol>
048: * </p>
049: *
050: * <p>Thanks to Erwin Bolwidt for submitting the original prototype
051: * of such a cancellable form controller!</p>
052: *
053: * @author Rob Harrop
054: * @author Juergen Hoeller
055: * @since 1.2.3
056: * @see #setCancelParamKey
057: * @see #setCancelView
058: * @see #isCancelRequest(javax.servlet.http.HttpServletRequest)
059: * @see #onCancel(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, Object)
060: */
061: public class CancellableFormController extends SimpleFormController {
062:
063: /**
064: * Default parameter triggering the cancel action.
065: * Can be called even with validation errors on the form.
066: */
067: private static final String PARAM_CANCEL = "_cancel";
068:
069: private String cancelParamKey = PARAM_CANCEL;
070:
071: private String cancelView;
072:
073: /**
074: * Set the key of the request parameter used to identify a cancel request.
075: * Default is "_cancel".
076: * <p>The parameter is recognized both when sent as a plain parameter
077: * ("_cancel") or when triggered by an image button ("_cancel.x").
078: */
079: public final void setCancelParamKey(String cancelParamKey) {
080: this .cancelParamKey = cancelParamKey;
081: }
082:
083: /**
084: * Return the key of the request parameter used to identify a cancel request.
085: */
086: public final String getCancelParamKey() {
087: return this .cancelParamKey;
088: }
089:
090: /**
091: * Sets the name of the cancel view.
092: */
093: public final void setCancelView(String cancelView) {
094: this .cancelView = cancelView;
095: }
096:
097: /**
098: * Gets the name of the cancel view.
099: */
100: public final String getCancelView() {
101: return this .cancelView;
102: }
103:
104: /**
105: * Consider an explicit cancel request as a form submission too.
106: * @see #isCancelRequest(javax.servlet.http.HttpServletRequest)
107: */
108: protected boolean isFormSubmission(HttpServletRequest request) {
109: return super .isFormSubmission(request)
110: || isCancelRequest(request);
111: }
112:
113: /**
114: * Suppress validation for an explicit cancel request too.
115: * @see #isCancelRequest(javax.servlet.http.HttpServletRequest)
116: */
117: protected boolean suppressValidation(HttpServletRequest request,
118: Object command) {
119: return super .suppressValidation(request, command)
120: || isCancelRequest(request);
121: }
122:
123: /**
124: * This implementation first checks to see if the incoming is a cancel request,
125: * through a call to {@link #isCancelRequest}. If so, control is passed to
126: * {@link #onCancel}; otherwise, control is passed up to
127: * {@link SimpleFormController#processFormSubmission}.
128: * @see #isCancelRequest
129: * @see #onCancel(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, Object)
130: * @see SimpleFormController#processFormSubmission
131: */
132: protected ModelAndView processFormSubmission(
133: HttpServletRequest request, HttpServletResponse response,
134: Object command, BindException errors) throws Exception {
135:
136: if (isCancelRequest(request)) {
137: return onCancel(request, response, command);
138: } else {
139: return super .processFormSubmission(request, response,
140: command, errors);
141: }
142: }
143:
144: /**
145: * Determine whether the incoming request is a request to cancel the
146: * processing of the current form.
147: * <p>By default, this method returns <code>true</code> if a parameter
148: * matching the configured <code>cancelParamKey</code> is present in
149: * the request, otherwise it returns <code>false</code>. Subclasses may
150: * override this method to provide custom logic to detect a cancel request.
151: * <p>The parameter is recognized both when sent as a plain parameter
152: * ("_cancel") or when triggered by an image button ("_cancel.x").
153: * @param request current HTTP request
154: * @see #setCancelParamKey
155: * @see #PARAM_CANCEL
156: */
157: protected boolean isCancelRequest(HttpServletRequest request) {
158: return WebUtils
159: .hasSubmitParameter(request, getCancelParamKey());
160: }
161:
162: /**
163: * Callback method for handling a cancel request. Called if {@link #isCancelRequest}
164: * returns <code>true</code>.
165: * <p>Default implementation delegates to <code>onCancel(Object)</code> to return
166: * the configured <code>cancelView</code>. Subclasses may override either of the two
167: * methods to build a custom {@link ModelAndView ModelAndView} that may contain model
168: * parameters used in the cancel view.
169: * <p>If you simply want to move the user to a new view and you don't want to add
170: * additional model parameters, use {@link #setCancelView(String)} rather than
171: * overriding an <code>onCancel</code> method.
172: * @param request current servlet request
173: * @param response current servlet response
174: * @param command form object with request parameters bound onto it
175: * @return the prepared model and view, or <code>null</code>
176: * @throws Exception in case of errors
177: * @see #isCancelRequest(javax.servlet.http.HttpServletRequest)
178: * @see #onCancel(Object)
179: * @see #setCancelView
180: */
181: protected ModelAndView onCancel(HttpServletRequest request,
182: HttpServletResponse response, Object command)
183: throws Exception {
184:
185: return onCancel(command);
186: }
187:
188: /**
189: * Simple <code>onCancel</code> version. Called by the default implementation
190: * of the <code>onCancel</code> version with all parameters.
191: * <p>Default implementation returns eturns the configured <code>cancelView</code>.
192: * Subclasses may override this method to build a custom {@link ModelAndView ModelAndView}
193: * that may contain model parameters used in the cancel view.
194: * <p>If you simply want to move the user to a new view and you don't want to add
195: * additional model parameters, use {@link #setCancelView(String)} rather than
196: * overriding an <code>onCancel</code> method.
197: * @param command form object with request parameters bound onto it
198: * @return the prepared model and view, or <code>null</code>
199: * @throws Exception in case of errors
200: * @see #onCancel(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, Object)
201: * @see #setCancelView
202: */
203: protected ModelAndView onCancel(Object command) throws Exception {
204: return new ModelAndView(getCancelView());
205: }
206:
207: }
|