diff options
author | Artur <artur@vaadin.com> | 2017-02-22 10:35:44 +0200 |
---|---|---|
committer | Ilia Motornyi <elmot@vaadin.com> | 2017-02-22 10:35:44 +0200 |
commit | bba4e4037bbfa7bfd5f71718806a92bb351e2fe8 (patch) | |
tree | 75c5481b87f1a477ffac07bd1b77c009586abae8 | |
parent | af1412adad6131a9603dc6854186ec896fb90f61 (diff) | |
download | vaadin-framework-bba4e4037bbfa7bfd5f71718806a92bb351e2fe8.tar.gz vaadin-framework-bba4e4037bbfa7bfd5f71718806a92bb351e2fe8.zip |
Translate "context://" to the context root of the web app
Fixes #2523
13 files changed, 299 insertions, 3 deletions
diff --git a/client/src/main/java/com/vaadin/client/ApplicationConfiguration.java b/client/src/main/java/com/vaadin/client/ApplicationConfiguration.java index e599eef61b..cde31cbd40 100644 --- a/client/src/main/java/com/vaadin/client/ApplicationConfiguration.java +++ b/client/src/main/java/com/vaadin/client/ApplicationConfiguration.java @@ -241,6 +241,7 @@ public class ApplicationConfiguration implements EntryPoint { */ private String vaadinDirUrl; private String serviceUrl; + private String contextRootUrl; private int uiId; private boolean standalone; private ErrorMessage communicationError; @@ -311,6 +312,15 @@ public class ApplicationConfiguration implements EntryPoint { } /** + * Gets the URL to the context root of the web application + * + * @return the URL to the server-side context root as a string + */ + public String getContextRootUrl() { + return contextRootUrl; + } + + /** * @return the theme name used when initializing the application * @deprecated as of 7.3. Use {@link UIConnector#getActiveTheme()} to get * the theme currently in use @@ -413,6 +423,8 @@ public class ApplicationConfiguration implements EntryPoint { serviceUrl += '/'; } + contextRootUrl = jsoConfiguration + .getConfigString(ApplicationConstants.CONTEXT_ROOT_URL); vaadinDirUrl = WidgetUtil.getAbsoluteUrl(jsoConfiguration .getConfigString(ApplicationConstants.VAADIN_DIR_URL)); uiId = jsoConfiguration.getConfigInteger(UIConstants.UI_ID_PARAMETER) @@ -897,4 +909,5 @@ public class ApplicationConfiguration implements EntryPoint { private static final Logger getLogger() { return Logger.getLogger(ApplicationConfiguration.class.getName()); } + } diff --git a/client/src/main/java/com/vaadin/client/ApplicationConnection.java b/client/src/main/java/com/vaadin/client/ApplicationConnection.java index b9288d68c6..e94195cd37 100644 --- a/client/src/main/java/com/vaadin/client/ApplicationConnection.java +++ b/client/src/main/java/com/vaadin/client/ApplicationConnection.java @@ -41,6 +41,7 @@ import com.google.gwt.user.client.Timer; import com.google.gwt.user.client.ui.HasWidgets; import com.google.gwt.user.client.ui.Widget; import com.vaadin.client.ApplicationConfiguration.ErrorMessage; +import com.vaadin.client.ApplicationConnection.ApplicationStoppedEvent; import com.vaadin.client.communication.ConnectionStateHandler; import com.vaadin.client.communication.Heartbeat; import com.vaadin.client.communication.MessageHandler; @@ -328,6 +329,11 @@ public class ApplicationConnection implements HasHandlers { protected String encodeQueryStringParameterValue(String queryString) { return URL.encodeQueryString(queryString); } + + @Override + protected String getContextRootUrl() { + return getConfiguration().getContextRootUrl(); + } }; public static class MultiStepDuration extends Duration { @@ -481,7 +487,7 @@ public class ApplicationConnection implements HasHandlers { return vi; } } - + client.getProfilingData = $entry(function() { var smh = ap.@com.vaadin.client.ApplicationConnection::getMessageHandler()(); var pd = [ @@ -496,7 +502,7 @@ public class ApplicationConnection implements HasHandlers { pd[pd.length] = smh.@com.vaadin.client.communication.MessageHandler::bootstrapTime; return pd; }); - + client.getElementByPath = $entry(function(id) { return componentLocator.@com.vaadin.client.componentlocator.ComponentLocator::getElementByPath(Ljava/lang/String;)(id); }); @@ -513,7 +519,7 @@ public class ApplicationConnection implements HasHandlers { return componentLocator.@com.vaadin.client.componentlocator.ComponentLocator::getPathForElement(Lcom/google/gwt/dom/client/Element;)(element); }); client.initializing = false; - + $wnd.vaadin.clients[TTAppId] = client; }-*/; diff --git a/server/src/main/java/com/vaadin/server/BootstrapHandler.java b/server/src/main/java/com/vaadin/server/BootstrapHandler.java index d94101152e..02a8d7ba84 100644 --- a/server/src/main/java/com/vaadin/server/BootstrapHandler.java +++ b/server/src/main/java/com/vaadin/server/BootstrapHandler.java @@ -242,6 +242,14 @@ public abstract class BootstrapHandler extends SynchronizedRequestHandler { } return encodedString; } + + @Override + protected String getContextRootUrl() { + String root = context.getApplicationParameters() + .getString(ApplicationConstants.CONTEXT_ROOT_URL); + assert root.endsWith("/"); + return root; + } } @Override @@ -691,6 +699,9 @@ public abstract class BootstrapHandler extends SynchronizedRequestHandler { appConfig.put("sessExpMsg", sessExpMsg); } + appConfig.put(ApplicationConstants.CONTEXT_ROOT_URL, + getContextRootPath(context)); + // getStaticFileLocation documented to never end with a slash // vaadinDir should always end with a slash String vaadinDir = vaadinService.getStaticFileLocation(request) @@ -722,6 +733,8 @@ public abstract class BootstrapHandler extends SynchronizedRequestHandler { return appConfig; } + protected abstract String getContextRootPath(BootstrapContext context); + protected abstract String getServiceUrl(BootstrapContext context); /** diff --git a/server/src/main/java/com/vaadin/server/VaadinServletService.java b/server/src/main/java/com/vaadin/server/VaadinServletService.java index 2adea189f7..242966f8cf 100644 --- a/server/src/main/java/com/vaadin/server/VaadinServletService.java +++ b/server/src/main/java/com/vaadin/server/VaadinServletService.java @@ -114,6 +114,32 @@ public class VaadinServletService extends VaadinService { return sb.toString(); } + /** + * Gets a relative path you can use to refer to the context root. + * + * @param request + * the request for which the location should be determined + * @return A relative path to the context root. Never ends with a slash (/). + */ + public static String getContextRootRelativePath(VaadinRequest request) { + VaadinServletRequest servletRequest = (VaadinServletRequest) request; + // Generate location from the request by finding how many "../" should + // be added to the servlet path before we get to the context root + + String servletPath = servletRequest.getServletPath(); + if (servletPath == null) { + // Not allowed by the spec but servers are servers... + servletPath = ""; + } + + String pathInfo = servletRequest.getPathInfo(); + if (pathInfo != null && !"".equals(pathInfo)) { + servletPath += pathInfo; + } + + return getCancelingRelativePath(servletPath); + } + @Override public String getConfiguredWidgetset(VaadinRequest request) { return getDeploymentConfiguration() diff --git a/server/src/main/java/com/vaadin/server/communication/PortletBootstrapHandler.java b/server/src/main/java/com/vaadin/server/communication/PortletBootstrapHandler.java index 490fbb81f0..535cd7646e 100644 --- a/server/src/main/java/com/vaadin/server/communication/PortletBootstrapHandler.java +++ b/server/src/main/java/com/vaadin/server/communication/PortletBootstrapHandler.java @@ -119,4 +119,10 @@ public class PortletBootstrapHandler extends BootstrapHandler { return parameters; } + + @Override + protected String getContextRootPath(BootstrapContext context) { + // Not really supported in portlet environments + return "."; + } } diff --git a/server/src/main/java/com/vaadin/server/communication/ServletBootstrapHandler.java b/server/src/main/java/com/vaadin/server/communication/ServletBootstrapHandler.java index 0365914ff1..28bda1b42c 100644 --- a/server/src/main/java/com/vaadin/server/communication/ServletBootstrapHandler.java +++ b/server/src/main/java/com/vaadin/server/communication/ServletBootstrapHandler.java @@ -45,4 +45,11 @@ public class ServletBootstrapHandler extends BootstrapHandler { } return themeName; } + + @Override + protected String getContextRootPath(BootstrapContext context) { + return VaadinServletService + .getContextRootRelativePath(context.getRequest()) + "/"; + } + } diff --git a/server/src/test/java/com/vaadin/server/VaadinServletServiceTest.java b/server/src/test/java/com/vaadin/server/VaadinServletServiceTest.java new file mode 100644 index 0000000000..24bb800a12 --- /dev/null +++ b/server/src/test/java/com/vaadin/server/VaadinServletServiceTest.java @@ -0,0 +1,127 @@ +package com.vaadin.server; + +import static org.easymock.EasyMock.createMock; +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.replay; + +import java.net.MalformedURLException; +import java.net.URL; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +public class VaadinServletServiceTest { + VaadinServlet servlet; + + @Before + public void setup() throws ServletException { + servlet = new VaadinServlet(); + servlet.init(new MockServletConfig()); + } + + @Test + public void testServletToContextRootRelativePath() throws Exception { + String location; + + /* SERVLETS */ + // http://dummy.host:8080/contextpath/servlet + // should return . (relative url resolving to /contextpath) + location = testLocation("http://dummy.host:8080", "/contextpath", + "/servlet", ""); + Assert.assertEquals(".", location); + + // http://dummy.host:8080/contextpath/servlet/ + // should return ./.. (relative url resolving to /contextpath) + location = testLocation("http://dummy.host:8080", "/contextpath", + "/servlet", "/"); + Assert.assertEquals("./..", location); + + // http://dummy.host:8080/servlet + // should return "." + location = testLocation("http://dummy.host:8080", "", "/servlet", ""); + Assert.assertEquals(".", location); + + // http://dummy.host/contextpath/servlet/extra/stuff + // should return ./../.. (relative url resolving to /contextpath) + location = testLocation("http://dummy.host", "/contextpath", "/servlet", + "/extra/stuff"); + Assert.assertEquals("./../..", location); + + // http://dummy.host/contextpath/servlet/extra/stuff/ + // should return ./../.. (relative url resolving to /contextpath) + location = testLocation("http://dummy.host", "/contextpath", "/servlet", + "/extra/stuff/"); + Assert.assertEquals("./../../..", location); + + // http://dummy.host/context/path/servlet/extra/stuff + // should return ./../.. (relative url resolving to /context/path) + location = testLocation("http://dummy.host", "/context/path", + "/servlet", "/extra/stuff"); + Assert.assertEquals("./../..", location); + + } + + private String testLocation(String base, String contextPath, + String servletPath, String pathInfo) throws Exception { + + HttpServletRequest request = createNonIncludeRequest(base, contextPath, + servletPath, pathInfo); + // Set request into replay mode + replay(request); + + String location = VaadinServletService.getContextRootRelativePath( + servlet.createVaadinRequest(request)); + return location; + } + + private HttpServletRequest createNonIncludeRequest(String base, + String realContextPath, String realServletPath, String pathInfo) + throws Exception { + HttpServletRequest request = createRequest(base, realContextPath, + realServletPath, pathInfo); + expect(request.getAttribute("javax.servlet.include.context_path")) + .andReturn(null).anyTimes(); + expect(request.getAttribute("javax.servlet.include.servlet_path")) + .andReturn(null).anyTimes(); + + return request; + } + + /** + * Creates a HttpServletRequest mock using the supplied parameters. + * + * @param base + * The base url, e.g. http://localhost:8080 + * @param contextPath + * The context path where the application is deployed, e.g. + * /mycontext + * @param servletPath + * The servlet path to the servlet we are testing, e.g. /myapp + * @param pathInfo + * Any text following the servlet path in the request, not + * including query parameters, e.g. /UIDL/ + * @return A mock HttpServletRequest object useful for testing + * @throws MalformedURLException + */ + private HttpServletRequest createRequest(String base, String contextPath, + String servletPath, String pathInfo) throws MalformedURLException { + URL url = new URL(base + contextPath + pathInfo); + HttpServletRequest request = createMock(HttpServletRequest.class); + expect(request.isSecure()) + .andReturn(url.getProtocol().equalsIgnoreCase("https")) + .anyTimes(); + expect(request.getServerName()).andReturn(url.getHost()).anyTimes(); + expect(request.getServerPort()).andReturn(url.getPort()).anyTimes(); + expect(request.getRequestURI()).andReturn(url.getPath()).anyTimes(); + expect(request.getContextPath()).andReturn(contextPath).anyTimes(); + expect(request.getPathInfo()).andReturn(pathInfo).anyTimes(); + expect(request.getServletPath()).andReturn(servletPath).anyTimes(); + + return request; + } + +} diff --git a/shared/src/main/java/com/vaadin/shared/ApplicationConstants.java b/shared/src/main/java/com/vaadin/shared/ApplicationConstants.java index 166e75e94f..8cf30e43b5 100644 --- a/shared/src/main/java/com/vaadin/shared/ApplicationConstants.java +++ b/shared/src/main/java/com/vaadin/shared/ApplicationConstants.java @@ -35,6 +35,10 @@ public class ApplicationConstants implements Serializable { public static final String APP_PROTOCOL_PREFIX = "app://"; public static final String VAADIN_PROTOCOL_PREFIX = "vaadin://"; + /** + * An internal protocol used for referencing the application context path. + */ + public static final String CONTEXT_PROTOCOL_PREFIX = "context://"; public static final String FONTICON_PROTOCOL_PREFIX = "fonticon://"; public static final String PUBLISHED_PROTOCOL_NAME = "published"; public static final String PUBLISHED_PROTOCOL_PREFIX = PUBLISHED_PROTOCOL_NAME @@ -73,6 +77,12 @@ public class ApplicationConstants implements Serializable { /** * Configuration parameter giving the (in some cases relative) URL to the + * web application context root. + */ + public static final String CONTEXT_ROOT_URL = "contextRootUrl"; + + /** + * Configuration parameter giving the (in some cases relative) URL to the * VAADIN folder from where themes and widgetsets are loaded. * <p> * <b>Refactor warning:</b> This value is also hardcoded in diff --git a/shared/src/main/java/com/vaadin/shared/VaadinUriResolver.java b/shared/src/main/java/com/vaadin/shared/VaadinUriResolver.java index 169b1ce768..78bf0de598 100644 --- a/shared/src/main/java/com/vaadin/shared/VaadinUriResolver.java +++ b/shared/src/main/java/com/vaadin/shared/VaadinUriResolver.java @@ -110,6 +110,13 @@ public abstract class VaadinUriResolver implements Serializable { ApplicationConstants.VAADIN_PROTOCOL_PREFIX.length()); vaadinUri = vaadinDirUri + relativeUrl; } + if (vaadinUri + .startsWith(ApplicationConstants.CONTEXT_PROTOCOL_PREFIX)) { + final String contextRoot = getContextRootUrl(); + String relativeUrl = vaadinUri.substring( + ApplicationConstants.CONTEXT_PROTOCOL_PREFIX.length()); + vaadinUri = contextRoot + relativeUrl; + } return vaadinUri; } @@ -141,6 +148,13 @@ public abstract class VaadinUriResolver implements Serializable { protected abstract String getServiceUrl(); /** + * Gets the URL pointing to the context root. + * + * @return the context root URL + */ + protected abstract String getContextRootUrl(); + + /** * Gets the URI of the directory of the current theme. * * @return the URI of the current theme directory diff --git a/uitest/src/main/java/com/vaadin/tests/applicationservlet/ContextProtocol.java b/uitest/src/main/java/com/vaadin/tests/applicationservlet/ContextProtocol.java new file mode 100644 index 0000000000..64fd47a3be --- /dev/null +++ b/uitest/src/main/java/com/vaadin/tests/applicationservlet/ContextProtocol.java @@ -0,0 +1,37 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * 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 com.vaadin.tests.applicationservlet; + +import com.vaadin.annotations.JavaScript; +import com.vaadin.annotations.Widgetset; +import com.vaadin.server.ExternalResource; +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.ui.Image; + +@JavaScript("context://statictestfiles/sayHello.js") +@Widgetset("com.vaadin.DefaultWidgetSet") +public class ContextProtocol extends AbstractTestUI { + + @Override + protected void setup(VaadinRequest request) { + Image image = new Image("Image from context root", + new ExternalResource("context://statictestfiles/image.png")); + image.setId("image"); + addComponent(image); + } + +} diff --git a/uitest/src/main/webapp/statictestfiles/image.png b/uitest/src/main/webapp/statictestfiles/image.png Binary files differnew file mode 100644 index 0000000000..f52011183c --- /dev/null +++ b/uitest/src/main/webapp/statictestfiles/image.png diff --git a/uitest/src/main/webapp/statictestfiles/sayHello.js b/uitest/src/main/webapp/statictestfiles/sayHello.js new file mode 100644 index 0000000000..b1f3e9274c --- /dev/null +++ b/uitest/src/main/webapp/statictestfiles/sayHello.js @@ -0,0 +1 @@ +window.hello="said";
\ No newline at end of file diff --git a/uitest/src/test/java/com/vaadin/tests/applicationservlet/ContextProtocolTest.java b/uitest/src/test/java/com/vaadin/tests/applicationservlet/ContextProtocolTest.java new file mode 100644 index 0000000000..a4bd0b5f85 --- /dev/null +++ b/uitest/src/test/java/com/vaadin/tests/applicationservlet/ContextProtocolTest.java @@ -0,0 +1,36 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * 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 com.vaadin.tests.applicationservlet; + +import org.junit.Assert; +import org.junit.Test; + +import com.vaadin.testbench.By; +import com.vaadin.tests.tb3.SingleBrowserTest; + +public class ContextProtocolTest extends SingleBrowserTest { + + @Test + public void contextPathCorrect() { + openTestURL(); + // Added by bootstrap + Assert.assertEquals("said", executeScript("return window.hello")); + // Added by client side + Assert.assertEquals(getBaseURL() + "/statictestfiles/image.png", + findElement(By.id("image")).getAttribute("src")); + } + +} |