This commit is contained in:
Keith Donald
2009-03-10 04:17:53 +00:00
parent 03162c0a05
commit db62fedbcd
3 changed files with 83 additions and 38 deletions

View File

@@ -15,6 +15,8 @@ Bug Fixes
* Fixed bug where Errors nestedPath attribute was not respected in views rendered by Web Flow (SWF-973)
* Fixed bug where Errors nestedPath attribute was not respected when using Errors API programatically within a Validator called by Web Flow (SWF-973)
* Made MessageCodesResolver pluggable on MvcViewFactoryCreator; configure a DefaultMessageCodesResolver to resolve error message codes consistent with default Spring MVC behavior (SWF-977)
* Made FlowHandlerAdapter#sendRedirect method protected, allowing overriding of web flow system redirect behavior; for example, to redirect to a SSL channel.
* Made DefaultFlowUrlHandler consistent with Spring MVC RedirectView behavior for URL encoding of flow URL parameters; URL encoding scheme is also now pluggable.
* Introduced createDefaultFlowHandler hook in FlowHandlerMapping, allowing for customizing the Default FlowHandler implementation application wide (SWF-994)
Improvements

View File

@@ -23,6 +23,7 @@ import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.springframework.util.StringUtils;
import org.springframework.web.util.WebUtils;
import org.springframework.webflow.core.collection.AttributeMap;
/**
@@ -50,16 +51,23 @@ import org.springframework.webflow.core.collection.AttributeMap;
* execution "e1s1" of the "hotels/booking" flow.
*
* @author Keith Donald
*
*/
public class DefaultFlowUrlHandler implements FlowUrlHandler {
private static final String DEFAULT_URL_ENCODING_SCHEME = "UTF-8";
private static final String FLOW_EXECUTION_KEY_PARAMETER = "execution";
private String urlEncodingScheme = DEFAULT_URL_ENCODING_SCHEME;
private String encodingScheme;
/**
* Set the character encoding scheme for flow urls. Default is the request's encoding scheme (which is ISO-8859-1 if
* not specified otherwise).
*/
public void setEncodingScheme(String encodingScheme) {
this.encodingScheme = encodingScheme;
}
public String getFlowExecutionKey(HttpServletRequest request) {
return request.getParameter("execution");
return request.getParameter(FLOW_EXECUTION_KEY_PARAMETER);
}
public String getFlowId(HttpServletRequest request) {
@@ -90,7 +98,7 @@ public class DefaultFlowUrlHandler implements FlowUrlHandler {
StringBuffer url = new StringBuffer();
url.append(request.getRequestURI());
url.append('?');
appendQueryParameter(url, "execution", flowExecutionKey);
appendQueryParameter(url, FLOW_EXECUTION_KEY_PARAMETER, flowExecutionKey, getEncodingScheme(request));
return url.toString();
}
@@ -118,35 +126,49 @@ public class DefaultFlowUrlHandler implements FlowUrlHandler {
}
if (input != null && !input.isEmpty()) {
url.append('?');
appendQueryParameters(url, input.asMap());
appendQueryParameters(url, input.asMap(), getEncodingScheme(request));
}
return url.toString();
}
private void appendQueryParameters(StringBuffer url, Map parameters) {
// internal helpers
private String getEncodingScheme(HttpServletRequest request) {
if (encodingScheme != null) {
return encodingScheme;
} else {
String encodingScheme = request.getCharacterEncoding();
if (encodingScheme == null) {
encodingScheme = WebUtils.DEFAULT_CHARACTER_ENCODING;
}
return encodingScheme;
}
}
private void appendQueryParameters(StringBuffer url, Map parameters, String encodingScheme) {
Iterator entries = parameters.entrySet().iterator();
while (entries.hasNext()) {
Map.Entry entry = (Map.Entry) entries.next();
appendQueryParameter(url, entry.getKey(), entry.getValue());
appendQueryParameter(url, entry.getKey(), entry.getValue(), encodingScheme);
if (entries.hasNext()) {
url.append('&');
}
}
}
private void appendQueryParameter(StringBuffer url, Object key, Object value) {
String encodedKey = encode(key);
String encodedValue = encode(value);
private void appendQueryParameter(StringBuffer url, Object key, Object value, String encodingScheme) {
String encodedKey = encode(key, encodingScheme);
String encodedValue = encode(value, encodingScheme);
url.append(encodedKey).append('=').append(encodedValue);
}
private String encode(Object value) {
return value != null ? urlEncode(String.valueOf(value)) : "";
private String encode(Object value, String encodingScheme) {
return value != null ? urlEncode(value.toString(), encodingScheme) : "";
}
private String urlEncode(String value) {
private String urlEncode(String value, String encodingScheme) {
try {
return URLEncoder.encode(String.valueOf(value), urlEncodingScheme);
return URLEncoder.encode(value, encodingScheme);
} catch (UnsupportedEncodingException e) {
throw new IllegalArgumentException("Cannot url encode " + value);
}

View File

@@ -53,6 +53,8 @@ public class FlowHandlerAdapter extends WebContentGenerator implements HandlerAd
private static final Log logger = LogFactory.getLog(FlowHandlerAdapter.class);
private static final String REFERER_FLOW_EXECUTION_ATTRIBUTE = "refererExecution";
private static final String SERVLET_RELATIVE_LOCATION_PREFIX = "servletRelative:";
private static final String CONTEXT_RELATIVE_LOCATION_PREFIX = "contextRelative:";
@@ -133,6 +135,14 @@ public class FlowHandlerAdapter extends WebContentGenerator implements HandlerAd
this.ajaxHandler = ajaxHandler;
}
/**
* Whether redirect sent by this handler adapter should be compatible with HTTP 1.0 clients.
* @return true if so, false otherwise
*/
public boolean getRedirectHttp10Compatible() {
return redirectHttp10Compatible;
}
/**
* Set whether redirects sent by this handler adapter should be compatible with HTTP 1.0 clients.
* <p>
@@ -189,6 +199,10 @@ public class FlowHandlerAdapter extends WebContentGenerator implements HandlerAd
return null;
}
public long getLastModified(HttpServletRequest request, Object handler) {
return -1;
}
// subclassing hooks
/**
@@ -206,7 +220,7 @@ public class FlowHandlerAdapter extends WebContentGenerator implements HandlerAd
/**
* The default algorithm to determine the id of the flow to launch from the current request. Only called if
* {@link FlowHandler#getFlowId()} returns null. This implementation delegates to the configured
* {@link FlowUrlHandler#getFlowId(HttpServletRequest)}.
* {@link FlowUrlHandler#getFlowId(HttpServletRequest)}. Subclasses may override.
* @param request the current request
*/
protected String defaultGetFlowId(HttpServletRequest request) {
@@ -216,7 +230,7 @@ public class FlowHandlerAdapter extends WebContentGenerator implements HandlerAd
/**
* The default algorithm to create the flow execution input map. Only called if
* {@link FlowHandler#createExecutionInputMap(HttpServletRequest)} returns null. This implementation exposes all
* current request parameters as flow execution input attributes.
* current request parameters as flow execution input attributes. Subclasses may override.
* @param request the current request
*/
protected MutableAttributeMap defaultCreateFlowExecutionInputMap(HttpServletRequest request) {
@@ -243,7 +257,7 @@ public class FlowHandlerAdapter extends WebContentGenerator implements HandlerAd
* The default algorithm for handling a flow execution outcome. Only called if
* {@link FlowHandler#handleExecutionOutcome(FlowExecutionOutcome, HttpServletRequest, HttpServletResponse)} returns
* null. This implementation attempts to start a new execution of the ended flow. Any flow execution output is
* passed as input to the new execution.
* passed as input to the new execution. Subclasses may override.
* @param flowId the id of the ended flow
* @param outcome the flow execution outcome
* @param context ServletExternalContext the completed ServletExternalContext
@@ -268,6 +282,7 @@ public class FlowHandlerAdapter extends WebContentGenerator implements HandlerAd
* {@link FlowHandler#handleException(FlowException, HttpServletRequest, HttpServletResponse)} returns null. This
* implementation rethrows the exception unless it is a {@link NoSuchFlowExecutionException}. If the exception is a
* NoSuchFlowExecutionException, this implementation attempts to start a new execution of the ended or expired flow.
* Subclasses may override.
* @param flowId the id of the ended flow
* @param e the flow exception
* @param request the current request
@@ -288,6 +303,31 @@ public class FlowHandlerAdapter extends WebContentGenerator implements HandlerAd
}
}
/**
* Sends a redirect to the requested url using {@link HttpServletResponse#sendRedirect(String)}.Called to actually
* perform flow execution redirects, flow definition redirects, and external redirects. Subclasses may override to
* customize general Web Flow system redirect behavior.
* @param url the url to redirect to
* @param request the current request
* @param response the current response
* @throws IOException an exception occurred
*/
protected void sendRedirect(String url, HttpServletRequest request, HttpServletResponse response)
throws IOException {
if (ajaxHandler.isAjaxRequest(request, response)) {
ajaxHandler.sendAjaxRedirect(url, request, response, false);
} else {
if (redirectHttp10Compatible) {
// Always send status code 302.
response.sendRedirect(response.encodeRedirectURL(url));
} else {
// Correct HTTP status code is 303, in particular for POST requests.
response.setStatus(303);
response.setHeader("Location", response.encodeRedirectURL(url));
}
}
}
// internal helpers
private void handleFlowExecutionResult(FlowExecutionResult result, ServletExternalContext context,
@@ -336,7 +376,7 @@ public class FlowHandlerAdapter extends WebContentGenerator implements HandlerAd
String flowId = context.getFlowRedirectFlowId();
MutableAttributeMap input = context.getFlowRedirectFlowInput();
if (result.isPaused()) {
input.put("refererExecution", result.getPausedKey());
input.put(REFERER_FLOW_EXECUTION_ATTRIBUTE, result.getPausedKey());
}
String url = flowUrlHandler.createFlowDefinitionUrl(flowId, input, request);
if (logger.isDebugEnabled()) {
@@ -385,21 +425,6 @@ public class FlowHandlerAdapter extends WebContentGenerator implements HandlerAd
sendRedirect(url.toString(), request, response);
}
private void sendRedirect(String url, HttpServletRequest request, HttpServletResponse response) throws IOException {
if (ajaxHandler.isAjaxRequest(request, response)) {
ajaxHandler.sendAjaxRedirect(url, request, response, false);
} else {
if (redirectHttp10Compatible) {
// Always send status code 302.
response.sendRedirect(response.encodeRedirectURL(url));
} else {
// Correct HTTP status code is 303, in particular for POST requests.
response.setStatus(303);
response.setHeader("Location", response.encodeRedirectURL(url));
}
}
}
private void handleFlowException(FlowException e, HttpServletRequest request, HttpServletResponse response,
FlowHandler handler) throws IOException {
String location = handler.handleException(e, request, response);
@@ -410,10 +435,6 @@ public class FlowHandlerAdapter extends WebContentGenerator implements HandlerAd
}
}
public long getLastModified(HttpServletRequest request, Object handler) {
return -1;
}
private String getFlowId(FlowHandler handler, HttpServletRequest request) {
String flowId = handler.getFlowId();
if (flowId != null) {