From 13d5b3e98954c2ade382305f8d044b2b49fdbd0b Mon Sep 17 00:00:00 2001 From: Leif Åstrand Date: Mon, 8 Oct 2012 08:11:51 +0300 Subject: Bootstrap UI using relative URLs with servlets (#6771) * Configure widgetset using URLs relative to the requested page * Provide a Util method for getting an absolute URL from a relative URL * Test by using an embedded Jetty acting as a transparent proxy * Make /embed1 use the Buttons test to enable testing UIDL requests Change-Id: I4ef9b40e3954ae16b682d743a339f4360db40d4d --- server/src/com/vaadin/server/BootstrapHandler.java | 44 ++++--------- server/src/com/vaadin/server/CombinedRequest.java | 33 +--------- .../com/vaadin/server/CommunicationManager.java | 52 ++++----------- server/src/com/vaadin/server/GAEVaadinServlet.java | 2 +- .../vaadin/server/PortletCommunicationManager.java | 27 +++----- .../com/vaadin/server/VaadinPortletService.java | 15 ++++- server/src/com/vaadin/server/VaadinService.java | 16 +++++ server/src/com/vaadin/server/VaadinServlet.java | 20 ------ .../com/vaadin/server/VaadinServletService.java | 75 ++++++++++++++++------ 9 files changed, 122 insertions(+), 162 deletions(-) (limited to 'server') diff --git a/server/src/com/vaadin/server/BootstrapHandler.java b/server/src/com/vaadin/server/BootstrapHandler.java index c08d3bdbb1..9a0e4c2071 100644 --- a/server/src/com/vaadin/server/BootstrapHandler.java +++ b/server/src/com/vaadin/server/BootstrapHandler.java @@ -21,7 +21,6 @@ import java.io.IOException; import java.io.OutputStreamWriter; import java.io.Serializable; import java.util.ArrayList; -import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; @@ -101,7 +100,8 @@ public abstract class BootstrapHandler implements RequestHandler { public String getAppId() { if (appId == null) { - appId = getApplicationId(this); + appId = getRequest().getService().getMainDivId(getSession(), + getRequest(), getUIClass()); } return appId; } @@ -274,16 +274,6 @@ public abstract class BootstrapHandler implements RequestHandler { return null; } - /** - * Creates and returns a unique ID for the DIV where the application is to - * be rendered. - * - * @param context - * - * @return the id to use in the DOM - */ - protected abstract String getApplicationId(BootstrapContext context); - public String getWidgetsetForUI(BootstrapContext context) { VaadinRequest request = context.getRequest(); @@ -411,7 +401,7 @@ public abstract class BootstrapHandler implements RequestHandler { String themeName = context.getThemeName(); if (themeName != null) { - appConfig.put("themeUri", getThemeUri(context, themeName)); + appConfig.put("theme", themeName); } JSONObject versionInfo = new JSONObject(); @@ -420,17 +410,6 @@ public abstract class BootstrapHandler implements RequestHandler { appConfig.put("widgetset", context.getWidgetsetName()); - appConfig.put("initialPath", request.getRequestPathInfo()); - - Map parameterMap = new HashMap( - request.getParameterMap()); - - // Include theme as a fake initial param so that UI can know its theme - // for serving CustomLayout templates - parameterMap.put("theme", new String[] { themeName }); - - appConfig.put("initialParams", parameterMap); - // Use locale from session if set, else from the request Locale locale = ServletPortletHelper.findLocale(null, context.getSession(), context.getRequest()); @@ -457,11 +436,11 @@ public abstract class BootstrapHandler implements RequestHandler { appConfig.put("authErrMsg", authErrMsg); } - String staticFileLocation = vaadinService - .getStaticFileLocation(request); - String widgetsetBase = staticFileLocation + "/" - + VaadinServlet.WIDGETSET_DIRECTORY_PATH; - appConfig.put("widgetsetBase", widgetsetBase); + // getStaticFileLocation documented to never end with a slash + // vaadinDir should always end with a slash + String vaadinDir = vaadinService.getStaticFileLocation(request) + + "/VAADIN/"; + appConfig.put(ApplicationConstants.VAADIN_DIR_URL, vaadinDir); if (!session.getConfiguration().isProductionMode()) { appConfig.put("debug", true); @@ -474,12 +453,15 @@ public abstract class BootstrapHandler implements RequestHandler { appConfig.put("heartbeatInterval", vaadinService .getDeploymentConfiguration().getHeartbeatInterval()); - appConfig.put("appUri", getAppUri(context)); + String serviceUrl = getServiceUrl(context); + if (serviceUrl != null) { + appConfig.put(ApplicationConstants.SERVICE_URL, serviceUrl); + } return appConfig; } - protected abstract String getAppUri(BootstrapContext context); + protected abstract String getServiceUrl(BootstrapContext context); /** * Get the URI for the application theme. diff --git a/server/src/com/vaadin/server/CombinedRequest.java b/server/src/com/vaadin/server/CombinedRequest.java index 34ac0b6a6f..5cb5b674b6 100644 --- a/server/src/com/vaadin/server/CombinedRequest.java +++ b/server/src/com/vaadin/server/CombinedRequest.java @@ -20,15 +20,10 @@ import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.net.URISyntaxException; -import java.util.Collections; -import java.util.HashMap; -import java.util.Iterator; import java.util.Locale; import java.util.Map; -import org.json.JSONArray; import org.json.JSONException; -import org.json.JSONObject; /** * A {@link VaadinRequest} with path and parameters from one request and @@ -42,7 +37,6 @@ import org.json.JSONObject; public class CombinedRequest implements VaadinRequest { private final VaadinRequest secondRequest; - private Map parameterMap; /** * Creates a new combined request based on the second request and some @@ -56,37 +50,16 @@ public class CombinedRequest implements VaadinRequest { */ public CombinedRequest(VaadinRequest secondRequest) throws JSONException { this.secondRequest = secondRequest; - - HashMap map = new HashMap(); - JSONObject initialParams = new JSONObject( - secondRequest.getParameter("initialParams")); - for (Iterator keys = initialParams.keys(); keys.hasNext();) { - String name = (String) keys.next(); - JSONArray jsonValues = initialParams.getJSONArray(name); - String[] values = new String[jsonValues.length()]; - for (int i = 0; i < values.length; i++) { - values[i] = jsonValues.getString(i); - } - map.put(name, values); - } - - parameterMap = Collections.unmodifiableMap(map); - } @Override public String getParameter(String parameter) { - String[] strings = getParameterMap().get(parameter); - if (strings == null || strings.length == 0) { - return null; - } else { - return strings[0]; - } + return secondRequest.getParameter(parameter); } @Override public Map getParameterMap() { - return parameterMap; + return secondRequest.getParameterMap(); } @Override @@ -111,7 +84,7 @@ public class CombinedRequest implements VaadinRequest { @Override public String getRequestPathInfo() { - return secondRequest.getParameter("initialPath"); + return secondRequest.getRequestPathInfo(); } @Override diff --git a/server/src/com/vaadin/server/CommunicationManager.java b/server/src/com/vaadin/server/CommunicationManager.java index 5f61079261..6cfaf37092 100644 --- a/server/src/com/vaadin/server/CommunicationManager.java +++ b/server/src/com/vaadin/server/CommunicationManager.java @@ -17,8 +17,6 @@ package com.vaadin.server; import java.io.InputStream; -import java.net.MalformedURLException; -import java.net.URL; import javax.servlet.ServletContext; @@ -54,45 +52,19 @@ public class CommunicationManager extends AbstractCommunicationManager { protected BootstrapHandler createBootstrapHandler() { return new BootstrapHandler() { @Override - protected String getApplicationId(BootstrapContext context) { - String appUrl = getAppUri(context); - - String appId = appUrl; - if ("".equals(appUrl)) { - appId = "ROOT"; - } - appId = appId.replaceAll("[^a-zA-Z0-9]", ""); - // Add hashCode to the end, so that it is still (sort of) - // predictable, but indicates that it should not be used in CSS - // and - // such: - int hashCode = appId.hashCode(); - if (hashCode < 0) { - hashCode = -hashCode; - } - appId = appId + "-" + hashCode; - return appId; - } - - @Override - protected String getAppUri(BootstrapContext context) { - /* Fetch relative url to application */ - // don't use server and port in uri. It may cause problems with - // some - // virtual server configurations which lose the server name - URL url; - - try { - url = context.getRequest().getService() - .getApplicationUrl(context.getRequest()); - } catch (MalformedURLException e) { - throw new RuntimeException(e); - } - String appUrl = url.getPath(); - if (appUrl.endsWith("/")) { - appUrl = appUrl.substring(0, appUrl.length() - 1); + protected String getServiceUrl(BootstrapContext context) { + String pathInfo = context.getRequest().getRequestPathInfo(); + if (pathInfo == null) { + return null; + } else { + /* + * Make a relative URL to the servlet by adding one ../ for + * each path segment in pathInfo (i.e. the part of the + * requested path that comes after the servlet mapping) + */ + return VaadinServletService + .getCancelingRelativePath(pathInfo); } - return appUrl; } @Override diff --git a/server/src/com/vaadin/server/GAEVaadinServlet.java b/server/src/com/vaadin/server/GAEVaadinServlet.java index c68f25a282..d2c53c6fcb 100644 --- a/server/src/com/vaadin/server/GAEVaadinServlet.java +++ b/server/src/com/vaadin/server/GAEVaadinServlet.java @@ -348,7 +348,7 @@ public class GAEVaadinServlet extends VaadinServlet { } private boolean isCleanupRequest(HttpServletRequest request) { - String path = getRequestPathInfo(request); + String path = request.getPathInfo(); if (path != null && path.equals(CLEANUP_PATH)) { return true; } diff --git a/server/src/com/vaadin/server/PortletCommunicationManager.java b/server/src/com/vaadin/server/PortletCommunicationManager.java index 8d39eee9ac..635202a074 100644 --- a/server/src/com/vaadin/server/PortletCommunicationManager.java +++ b/server/src/com/vaadin/server/PortletCommunicationManager.java @@ -65,19 +65,11 @@ public class PortletCommunicationManager extends AbstractCommunicationManager { } @Override - protected String getApplicationId(BootstrapContext context) { - PortletRequest portletRequest = VaadinPortletRequest.cast( - context.getRequest()).getPortletRequest(); - /* - * We need to generate a unique ID because some portals already - * create a DIV with the portlet's Window ID as the DOM ID. - */ - return "v-" + portletRequest.getWindowID(); - } - - @Override - protected String getAppUri(BootstrapContext context) { - return getRenderResponse(context).createActionURL().toString(); + protected String getServiceUrl(BootstrapContext context) { + ResourceURL portletResourceUrl = getRenderResponse(context) + .createResourceURL(); + portletResourceUrl.setResourceID(VaadinPortlet.RESOURCE_URL_ID); + return portletResourceUrl.toString(); } private RenderResponse getRenderResponse(BootstrapContext context) { @@ -129,11 +121,10 @@ public class PortletCommunicationManager extends AbstractCommunicationManager { resourceURL.setResourceID("browserDetails"); parameters.put("browserDetailsUrl", resourceURL.toString()); - ResourceURL portletResourceUrl = getRenderResponse(context) - .createResourceURL(); - portletResourceUrl.setResourceID(VaadinPortlet.RESOURCE_URL_ID); - parameters.put(ApplicationConstants.PORTLET_RESOUCE_URL_BASE, - portletResourceUrl.toString()); + // Always send path info as a query parameter + parameters.put( + ApplicationConstants.SERVICE_URL_PATH_AS_PARAMETER, + true); return parameters; } diff --git a/server/src/com/vaadin/server/VaadinPortletService.java b/server/src/com/vaadin/server/VaadinPortletService.java index ac48900945..12bf45f43a 100644 --- a/server/src/com/vaadin/server/VaadinPortletService.java +++ b/server/src/com/vaadin/server/VaadinPortletService.java @@ -25,6 +25,7 @@ import javax.portlet.PortletContext; import javax.portlet.PortletRequest; import com.vaadin.server.VaadinPortlet.RequestType; +import com.vaadin.ui.UI; public class VaadinPortletService extends VaadinService { private final VaadinPortlet portlet; @@ -218,4 +219,16 @@ public class VaadinPortletService extends VaadinService { public boolean preserveUIOnRefresh(UIProvider provider, UICreateEvent event) { return true; } -} \ No newline at end of file + + @Override + public String getMainDivId(VaadinServiceSession session, + VaadinRequest request, Class uiClass) { + PortletRequest portletRequest = VaadinPortletRequest.cast(request) + .getPortletRequest(); + /* + * We need to generate a unique ID because some portals already create a + * DIV with the portlet's Window ID as the DOM ID. + */ + return "v-" + portletRequest.getWindowID(); + } +} diff --git a/server/src/com/vaadin/server/VaadinService.java b/server/src/com/vaadin/server/VaadinService.java index 28684ec8dc..d9c8b83ea4 100644 --- a/server/src/com/vaadin/server/VaadinService.java +++ b/server/src/com/vaadin/server/VaadinService.java @@ -730,4 +730,20 @@ public abstract class VaadinService implements Serializable { } } + + /** + * Creates and returns a unique ID for the DIV where the UI is to be + * rendered. + * + * @param session + * The service session to which the bootstrapped UI will belong. + * @param request + * The request for which a div id is needed + * @param uiClass + * The class of the UI that will be bootstrapped + * + * @return the id to use in the DOM + */ + public abstract String getMainDivId(VaadinServiceSession session, + VaadinRequest request, Class uiClass); } diff --git a/server/src/com/vaadin/server/VaadinServlet.java b/server/src/com/vaadin/server/VaadinServlet.java index bd724aad63..68fca7b463 100644 --- a/server/src/com/vaadin/server/VaadinServlet.java +++ b/server/src/com/vaadin/server/VaadinServlet.java @@ -1165,26 +1165,6 @@ public class VaadinServlet extends HttpServlet implements Constants { return u; } - /** - * Returns the path info; note that this _can_ be different than - * request.getPathInfo(). Examples where this might be useful: - *
    - *
  • An application runner servlet that runs different Vaadin applications - * based on an identifier.
  • - *
  • Providing a REST interface in the context root, while serving a - * Vaadin UI on a sub-URI using only one servlet (e.g. REST on - * http://example.com/foo, UI on http://example.com/foo/vaadin)
  • - * - * @param request - * @return - * - * @deprecated might be refactored or removed before 7.0.0 - */ - @Deprecated - protected String getRequestPathInfo(HttpServletRequest request) { - return request.getPathInfo(); - } - public class RequestError implements Terminal.ErrorEvent, Serializable { private final Throwable throwable; diff --git a/server/src/com/vaadin/server/VaadinServletService.java b/server/src/com/vaadin/server/VaadinServletService.java index b8af3b13fb..9992b3698d 100644 --- a/server/src/com/vaadin/server/VaadinServletService.java +++ b/server/src/com/vaadin/server/VaadinServletService.java @@ -23,6 +23,7 @@ import java.net.URL; import javax.servlet.http.HttpServletRequest; import com.vaadin.server.VaadinServlet.RequestType; +import com.vaadin.ui.UI; public class VaadinServletService extends VaadinService { private final VaadinServlet servlet; @@ -50,31 +51,35 @@ public class VaadinServletService extends VaadinService { } // the last (but most common) option is to generate default location - // from request + // from request by finding how many "../" should be added to the + // requested path before we get to the context root - // if context is specified add it to widgetsetUrl - String ctxPath = servletRequest.getContextPath(); - - // FIXME: ctxPath.length() == 0 condition is probably unnecessary - // and - // might even be wrong. - - if (ctxPath.length() == 0 - && request.getAttribute("javax.servlet.include.context_path") != null) { - // include request (e.g portlet), get context path from - // attribute - ctxPath = (String) request - .getAttribute("javax.servlet.include.context_path"); + String requestedPath = servletRequest.getServletPath(); + String pathInfo = servletRequest.getPathInfo(); + if (pathInfo != null) { + requestedPath += pathInfo; } - // Remove heading and trailing slashes from the context path - ctxPath = VaadinServlet.removeHeadingOrTrailing(ctxPath, "/"); + return getCancelingRelativePath(requestedPath); + } - if (ctxPath.equals("")) { - return ""; - } else { - return "/" + ctxPath; + /** + * Gets a relative path that cancels the provided path. This essentially + * adds one .. for each part of the path to cancel. + * + * @param pathToCancel + * the path that should be canceled + * @return a relative path that cancels out the provided path segment + */ + public static String getCancelingRelativePath(String pathToCancel) { + StringBuilder sb = new StringBuilder("."); + // Start from i = 1 to ignore first slash + for (int i = 1; i < pathToCancel.length(); i++) { + if (pathToCancel.charAt(i) == '/') { + sb.append("/.."); + } } + return sb.toString(); } @Override @@ -184,4 +189,32 @@ public class VaadinServletService extends VaadinService { public String getServiceName() { return getServlet().getServletName(); } -} \ No newline at end of file + + @Override + public String getMainDivId(VaadinServiceSession session, + VaadinRequest request, Class uiClass) { + String appId = null; + try { + URL appUrl = getServlet().getApplicationUrl( + VaadinServletRequest.cast(request)); + appId = appUrl.getPath(); + } catch (MalformedURLException e) { + // Just ignore problem here + } + + if (appId == null || "".equals(appId)) { + appId = "ROOT"; + } + appId = appId.replaceAll("[^a-zA-Z0-9]", ""); + // Add hashCode to the end, so that it is still (sort of) + // predictable, but indicates that it should not be used in CSS + // and + // such: + int hashCode = appId.hashCode(); + if (hashCode < 0) { + hashCode = -hashCode; + } + appId = appId + "-" + hashCode; + return appId; + } +} -- cgit v1.2.3