/* * Copyright 2004-2012 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.webflow.context.servlet; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.Iterator; 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; /** * The default FlowUrlHandler implementation for Spring Web Flow. * <p> * Expects URLs to launch flow to be of this pattern: * </p> * * <pre> * http://<host>/[app context path]/[app servlet path]/<flow path> * </pre> * * As an example, the URL <code>http://localhost/springtravel/app/booking</code> would map to flow "booking", while the * URL <code>http://localhost/springtravel/app/hotels/booking</code> would map to flow "hotels/booking". In both these * examples, /springtravel is the context path and /app is the servlet path. The flow id is treated as the path info * component of the request URL string. * * If the path info is null, the servletPath will be used as the flow id. Also, if the servlet path ends in an extension * it will be stripped when calculating the flow id. For example, a URL of * <code>http://localhost/springtravel/hotels/booking.htm</code> would still map to flow id "hotels/booking", assuming a * context path of /springtravel, a servlet path of /hotels/booking.htm (likely mapped with a servlet-mapping of *.htm), * and a path info of null. * <p> * Expects URLs to resume flows to be of this pattern: * </p> * * <pre> * http://<host>/[app context path]/[app servlet path]/<flow path>?execution=<flow execution key> * </pre> * * As an example, the URL http://localhost/springtravel/app/hotels/booking?execution=e1s1 would attempt to resume * execution "e1s1" of the "hotels/booking" flow. * * @author Keith Donald * @author Jeremy Grelle */ public class DefaultFlowUrlHandler implements FlowUrlHandler { private static final String FLOW_EXECUTION_KEY_PARAMETER = "execution"; 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(FLOW_EXECUTION_KEY_PARAMETER); } public String getFlowId(HttpServletRequest request) { String pathInfo = request.getPathInfo(); if (pathInfo != null) { return pathInfo.substring(1); } else { String servletPath = request.getServletPath(); if (StringUtils.hasText(servletPath)) { int dotIndex = servletPath.lastIndexOf('.'); if (dotIndex != -1) { return servletPath.substring(1, dotIndex); } else { return servletPath.substring(1); } } else { String contextPath = request.getContextPath(); if (StringUtils.hasText(contextPath)) { return request.getContextPath().substring(1); } else { return null; } } } } public String createFlowExecutionUrl(String flowId, String flowExecutionKey, HttpServletRequest request) { StringBuilder url = new StringBuilder(); url.append(request.getRequestURI()); url.append('?'); appendQueryParameter(url, FLOW_EXECUTION_KEY_PARAMETER, flowExecutionKey, getEncodingScheme(request)); return url.toString(); } /** * The flow definition URL for the given flow id will be built by appending the flow id to the base app context and * servlet paths. * * <p> * Example - given a request originating at: * * <pre> * http://someHost/someApp/someServlet/nestedPath/foo * </pre> * * and a request for the flow id "nestedPath/bar", the new flow definition URL would be: * * <pre> * http://someHost/someApp/someServlet/nestedPath/bar * </pre> */ public String createFlowDefinitionUrl(String flowId, AttributeMap<?> input, HttpServletRequest request) { StringBuilder url = new StringBuilder(); if (request.getPathInfo() != null) { url.append(request.getContextPath()); url.append(request.getServletPath()); url.append('/'); url.append(flowId); } else { String servletPath = request.getServletPath(); if (StringUtils.hasText(servletPath)) { url.append(request.getContextPath()); url.append('/'); url.append(flowId); int dotIndex = servletPath.lastIndexOf('.'); if (dotIndex != -1) { url.append(servletPath.substring(dotIndex)); } } else { url.append('/'); url.append(flowId); } } if (input != null && !input.isEmpty()) { url.append('?'); appendQueryParameters(url, input.asMap(), getEncodingScheme(request)); } return url.toString(); } protected String getEncodingScheme(HttpServletRequest request) { if (encodingScheme != null) { return encodingScheme; } else { String encodingScheme = request.getCharacterEncoding(); if (encodingScheme == null) { encodingScheme = WebUtils.DEFAULT_CHARACTER_ENCODING; } return encodingScheme; } } protected <T> void appendQueryParameters(StringBuilder url, Map<String, T> parameters, String encodingScheme) { Iterator<Map.Entry<String, T>> entries = parameters.entrySet().iterator(); while (entries.hasNext()) { Map.Entry<?, ?> entry = entries.next(); appendQueryParameter(url, entry.getKey(), entry.getValue(), encodingScheme); if (entries.hasNext()) { url.append('&'); } } } // internal helpers private void appendQueryParameter(StringBuilder 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, String encodingScheme) { return value != null ? urlEncode(value.toString(), encodingScheme) : ""; } private String urlEncode(String value, String encodingScheme) { try { return URLEncoder.encode(value, encodingScheme); } catch (UnsupportedEncodingException e) { throw new IllegalArgumentException("Cannot url encode " + value); } } }