/* * 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 javax.servlet.http.HttpServletRequest; import org.springframework.util.StringUtils; import org.springframework.web.util.UrlPathHelper; import org.springframework.web.util.WebUtils; import org.springframework.webflow.core.collection.AttributeMap; import org.springframework.webflow.mvc.servlet.FlowController; /** * A file name based {@link FlowUrlHandler} implementation that is an alternative to the standard * {@link DefaultFlowUrlHandler}. Treats the filename of a request without the URL suffix and/or prefix as the flow id. * Used by the {@link FlowController} implementation as a default implementation to preserve compatibility with existing * Web Flow 2 applications. * * <p> * This implementation extracts the filename and removes the file extension from the request URL. The results will be * used as the flow Id that must be unique throughout the application. * * For example the URLs * * <pre> * http://someHost/someApp/someServlet/foo * http://someHost/someApp/someServlet/nestedPath/foo * http://someHost/someApp/someServlet/nestedPath/foo.html * </pre> * * will all treat the filename "foo" as the flow id. * </p> * * <strong>Note:</strong> Because this class only treats a filename as a flow id, clashes can result. For example: * * <pre> * http://localhost/springtravel/app/hotel/booking * http://localhost/springtravel/app/flight/booking * </pre> * * would both map the same flow id "booking", instead of "hotel/booking" and "flight/booking". This is an limitation of * this implementation. Consider using the standard {@link DefaultFlowUrlHandler} that uses the request URL prefix as * well to avoid these clashes. * * @author Agim Emruli * @author Jeremy Grelle * @author Nazaret Kazarian */ public class FilenameFlowUrlHandler extends DefaultFlowUrlHandler { private UrlPathHelper urlPathHelper; public FilenameFlowUrlHandler() { urlPathHelper = new UrlPathHelper(); } public String getFlowId(HttpServletRequest request) { return WebUtils.extractFilenameFromUrlPath(urlPathHelper.getLookupPathForRequest(request)); } /** * The flow definition URL for the given flowId will be inferred from the URL of the current request, re-using the * same path and file extension. * * <p> * Example - given a request originating at: * * <pre> * http://someHost/someApp/someServlet/nestedPath/foo.html * </pre> * * and a request for the flow id "bar", the new flow definition URL would be: * * <pre> * http://someHost/someApp/someServlet/nestedPath/bar.html * </pre> */ public String createFlowDefinitionUrl(String flowId, AttributeMap<?> input, HttpServletRequest request) { StringBuilder url = new StringBuilder(); String pathInfo = request.getPathInfo(); if (pathInfo != null) { url.append(request.getContextPath()); url.append(request.getServletPath()); // include the pathInfo part up until the filename url.append(pathInfo.substring(0, pathInfo.lastIndexOf("/") + 1)); url.append(flowId); int dotIndex = pathInfo.lastIndexOf('.'); if (dotIndex != -1) { url.append(pathInfo.substring(dotIndex)); } } else { String servletPath = request.getServletPath(); if (StringUtils.hasText(servletPath)) { url.append(request.getContextPath()); // include the servletPath part up to the filename int slashIndex = servletPath.lastIndexOf("/"); if (slashIndex != -1) { url.append(servletPath.substring(0, slashIndex)); } url.append('/'); url.append(flowId); int dotIndex = servletPath.lastIndexOf('.'); if (dotIndex != -1) { url.append(servletPath.substring(dotIndex)); } } else { // Leaving this for now, as DefaultFlowUrlHandler does the same thing, // but this should probably be an error case in the future. url.append('/'); url.append(flowId); } } if (input != null && !input.isEmpty()) { url.append('?'); appendQueryParameters(url, input.asMap(), getEncodingScheme(request)); } return url.toString(); } }