* 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: I4ef9b40e3954ae16b682d743a339f4360db40d4dtags/7.0.0.beta6
@@ -81,12 +81,8 @@ | |||
log('Fetching root config'); | |||
var url = getConfig('browserDetailsUrl'); | |||
if (!url) { | |||
// No special url defined, use the default URL | |||
url = getConfig('appUri'); | |||
// Add a slash to the end, because ApplicationConiguration.loadFromDOM does so... | |||
if (url.length == 0 || url.substr(url.length-1) !== "/") { | |||
url += '/'; | |||
} | |||
// No special url defined, use the same URL that loaded this page (without the fragment) | |||
url = window.location.href.replace(/#.*/,''); | |||
} | |||
url += ((/\?/).test(url) ? "&" : "?") + "browserDetails=1"; | |||
var rootId = getConfig("rootId"); | |||
@@ -94,11 +90,11 @@ | |||
url += "&rootId=" + rootId; | |||
} | |||
var initialPath = getConfig("initialPath"); | |||
if (initialPath !== undefined) { | |||
url += '&initialPath=' + encodeURIComponent(initialPath); | |||
// Tell the UI what theme it is configured to use | |||
var theme = getConfig('theme'); | |||
if (theme !== undefined) { | |||
url += '&theme=' + encodeURIComponent(theme); | |||
} | |||
url += '&initialParams=' + encodeURIComponent(JSON.stringify(getConfig("initialParams"))); | |||
url += '&' + vaadin.getBrowserDetailsParameters(appId); | |||
@@ -145,12 +141,13 @@ | |||
} | |||
var bootstrapApp = function(mayDefer) { | |||
var themeUri = getConfig('themeUri'); | |||
var vaadinDir = getConfig('vaadinDir'); | |||
var themeUri = vaadinDir + 'themes/' + getConfig('theme') | |||
loadTheme(themeUri); | |||
var widgetsetBase = getConfig('widgetsetBase'); | |||
var widgetset = getConfig('widgetset'); | |||
loadWidgetset(widgetsetBase, widgetset); | |||
loadWidgetset(vaadinDir + 'widgetsets/', widgetset); | |||
if (getConfig('uidl') === undefined) { | |||
if (mayDefer) { |
@@ -24,7 +24,7 @@ | |||
<servlet-class>com.vaadin.server.LegacyVaadinServlet</servlet-class> | |||
<init-param> | |||
<param-name>application</param-name> | |||
<param-value>com.vaadin.tests.components.absolutelayout.AbsoluteLayoutClipping</param-value> | |||
<param-value>com.vaadin.tests.components.button.Buttons</param-value> | |||
</init-param> | |||
</servlet> | |||
<servlet> |
@@ -189,8 +189,12 @@ public class ApplicationConfiguration implements EntryPoint { | |||
private static WidgetSet widgetSet = GWT.create(WidgetSet.class); | |||
private String id; | |||
private String themeUri; | |||
private String appUri; | |||
/** | |||
* The URL to the VAADIN directory containing themes and widgetsets. Should | |||
* always end with a slash (/). | |||
*/ | |||
private String vaadinDirUrl; | |||
private String serviceUrl; | |||
private int uiId; | |||
private boolean standalone; | |||
private ErrorMessage communicationError; | |||
@@ -214,13 +218,20 @@ public class ApplicationConfiguration implements EntryPoint { | |||
private Map<Integer, Integer> componentInheritanceMap = new HashMap<Integer, Integer>(); | |||
private Map<Integer, String> tagToServerSideClassName = new HashMap<Integer, String>(); | |||
public boolean usePortletURLs() { | |||
return getPortletResourceUrl() != null; | |||
} | |||
public String getPortletResourceUrl() { | |||
return getJsoConfiguration(id).getConfigString( | |||
ApplicationConstants.PORTLET_RESOUCE_URL_BASE); | |||
/** | |||
* Checks whether path info in requests to the server-side service should be | |||
* in a request parameter (named | |||
* {@value ApplicationConstants#V_RESOURCE_PATH}) or appended to the end of | |||
* the service URL. | |||
* | |||
* @see #getServiceUrl() | |||
* | |||
* @return <code>true</code> if path info should be a request parameter; | |||
* <code>false</code> if the path info goes after the service URL | |||
*/ | |||
public boolean useServiceUrlPathParam() { | |||
return getJsoConfiguration(id).getConfigBoolean( | |||
ApplicationConstants.SERVICE_URL_PATH_AS_PARAMETER) == Boolean.TRUE; | |||
} | |||
public String getRootPanelId() { | |||
@@ -228,24 +239,28 @@ public class ApplicationConfiguration implements EntryPoint { | |||
} | |||
/** | |||
* Gets the application base URI. Using this other than as the download | |||
* action URI can cause problems in Portlet 2.0 deployments. | |||
* Gets the URL to the server-side VaadinService. If | |||
* {@link #useServiceUrlPathParam()} return <code>true</code>, the requested | |||
* path info should be in the {@value ApplicationConstants#V_RESOURCE_PATH} | |||
* query parameter; else the path info should be appended to the end of the | |||
* URL. | |||
* | |||
* @see #useServiceUrlPathParam() | |||
* | |||
* @return application base URI | |||
* @return the URL to the server-side service as a string | |||
*/ | |||
public String getApplicationUri() { | |||
return appUri; | |||
public String getServiceUrl() { | |||
return serviceUrl; | |||
} | |||
public String getThemeName() { | |||
String uri = getThemeUri(); | |||
String themeName = uri.substring(uri.lastIndexOf('/')); | |||
String themeName = getJsoConfiguration(id).getConfigString("theme"); | |||
themeName = themeName.replaceAll("[^a-zA-Z0-9]", ""); | |||
return themeName; | |||
} | |||
public String getThemeUri() { | |||
return themeUri; | |||
return vaadinDirUrl + "themes/" + getThemeName(); | |||
} | |||
public void setAppId(String appId) { | |||
@@ -306,11 +321,29 @@ public class ApplicationConfiguration implements EntryPoint { | |||
*/ | |||
private void loadFromDOM() { | |||
JsoConfiguration jsoConfiguration = getJsoConfiguration(id); | |||
appUri = jsoConfiguration.getConfigString("appUri"); | |||
if (appUri != null && !appUri.endsWith("/")) { | |||
appUri += '/'; | |||
serviceUrl = jsoConfiguration | |||
.getConfigString(ApplicationConstants.SERVICE_URL); | |||
if (serviceUrl == null || "".equals(serviceUrl)) { | |||
/* | |||
* Use the current url without query parameters and fragment as the | |||
* default value. | |||
*/ | |||
serviceUrl = Window.Location.getHref().replaceFirst("[?#].*", ""); | |||
} else { | |||
/* | |||
* Resolve potentially relative URLs to ensure they point to the | |||
* desired locations even if the base URL of the page changes later | |||
* (e.g. with pushState) | |||
*/ | |||
serviceUrl = Util.getAbsoluteUrl(serviceUrl); | |||
} | |||
// Ensure there's an ending slash (to make appending e.g. UIDL work) | |||
if (!useServiceUrlPathParam() && !serviceUrl.endsWith("/")) { | |||
serviceUrl += '/'; | |||
} | |||
themeUri = jsoConfiguration.getConfigString("themeUri"); | |||
vaadinDirUrl = Util.getAbsoluteUrl(jsoConfiguration | |||
.getConfigString(ApplicationConstants.VAADIN_DIR_URL)); | |||
uiId = jsoConfiguration.getConfigInteger(UIConstants.UI_ID_PARAMETER) | |||
.intValue(); | |||
@@ -527,16 +527,6 @@ public class ApplicationConnection { | |||
} | |||
}-*/; | |||
/** | |||
* Gets the application base URI. Using this other than as the download | |||
* action URI can cause problems in Portlet 2.0 deployments. | |||
* | |||
* @return application base URI | |||
*/ | |||
public String getAppUri() { | |||
return configuration.getApplicationUri(); | |||
}; | |||
/** | |||
* Indicates whether or not there are currently active UIDL requests. Used | |||
* internally to sequence requests properly, seldom needed in Widgets. | |||
@@ -2604,29 +2594,29 @@ public class ApplicationConnection { | |||
String relativeUrl = uidlUri | |||
.substring(ApplicationConstants.APP_PROTOCOL_PREFIX | |||
.length()); | |||
if (getConfiguration().usePortletURLs()) { | |||
ApplicationConfiguration conf = getConfiguration(); | |||
String serviceUrl = conf.getServiceUrl(); | |||
if (conf.useServiceUrlPathParam()) { | |||
// Should put path in v-resourcePath parameter and append query | |||
// params to base portlet url | |||
String[] parts = relativeUrl.split("\\?", 2); | |||
String path = parts[0]; | |||
String url = getConfiguration().getPortletResourceUrl(); | |||
// If there's a "?" followed by something, append it as a query | |||
// string to the base URL | |||
if (parts.length > 1) { | |||
String appUrlParams = parts[1]; | |||
url = addGetParameters(url, appUrlParams); | |||
serviceUrl = addGetParameters(serviceUrl, appUrlParams); | |||
} | |||
if (!path.startsWith("/")) { | |||
path = '/' + path; | |||
} | |||
String pathParam = ApplicationConstants.V_RESOURCE_PATH + "=" | |||
+ URL.encodeQueryString(path); | |||
url = addGetParameters(url, pathParam); | |||
uidlUri = url; | |||
serviceUrl = addGetParameters(serviceUrl, pathParam); | |||
uidlUri = serviceUrl; | |||
} else { | |||
uidlUri = getAppUri() + relativeUrl; | |||
uidlUri = serviceUrl + relativeUrl; | |||
} | |||
} | |||
return uidlUri; |
@@ -26,7 +26,6 @@ import com.google.gwt.core.client.Duration; | |||
import com.google.gwt.core.client.GWT; | |||
import com.google.gwt.core.client.Scheduler; | |||
import com.google.gwt.core.client.Scheduler.RepeatingCommand; | |||
import com.google.gwt.dom.client.AnchorElement; | |||
import com.google.gwt.dom.client.Document; | |||
import com.google.gwt.dom.client.Element; | |||
import com.google.gwt.dom.client.LinkElement; | |||
@@ -205,7 +204,7 @@ public class ResourceLoader { | |||
*/ | |||
public void loadScript(final String scriptUrl, | |||
final ResourceLoadListener resourceLoadListener) { | |||
final String url = getAbsoluteUrl(scriptUrl); | |||
final String url = Util.getAbsoluteUrl(scriptUrl); | |||
ResourceLoadEvent event = new ResourceLoadEvent(this, url, false); | |||
if (loadedResources.contains(url)) { | |||
if (resourceLoadListener != null) { | |||
@@ -252,12 +251,6 @@ public class ResourceLoader { | |||
} | |||
} | |||
private static String getAbsoluteUrl(String url) { | |||
AnchorElement a = Document.get().createAnchorElement(); | |||
a.setHref(url); | |||
return a.getHref(); | |||
} | |||
/** | |||
* Download a resource and notify a listener when the resource is loaded | |||
* without attempting to interpret the resource. When a resource has been | |||
@@ -278,7 +271,7 @@ public class ResourceLoader { | |||
*/ | |||
public void preloadResource(String url, | |||
ResourceLoadListener resourceLoadListener) { | |||
url = getAbsoluteUrl(url); | |||
url = Util.getAbsoluteUrl(url); | |||
ResourceLoadEvent event = new ResourceLoadEvent(this, url, true); | |||
if (loadedResources.contains(url) || preloadedResources.contains(url)) { | |||
// Already loaded or preloaded -> just fire listener | |||
@@ -363,7 +356,7 @@ public class ResourceLoader { | |||
*/ | |||
public void loadStylesheet(final String stylesheetUrl, | |||
final ResourceLoadListener resourceLoadListener) { | |||
final String url = getAbsoluteUrl(stylesheetUrl); | |||
final String url = Util.getAbsoluteUrl(stylesheetUrl); | |||
final ResourceLoadEvent event = new ResourceLoadEvent(this, url, false); | |||
if (loadedResources.contains(url)) { | |||
if (resourceLoadListener != null) { |
@@ -24,6 +24,7 @@ import java.util.List; | |||
import com.google.gwt.core.client.Scheduler; | |||
import com.google.gwt.core.client.Scheduler.ScheduledCommand; | |||
import com.google.gwt.dom.client.AnchorElement; | |||
import com.google.gwt.dom.client.Document; | |||
import com.google.gwt.dom.client.NativeEvent; | |||
import com.google.gwt.dom.client.Node; | |||
@@ -1196,4 +1197,18 @@ public class Util { | |||
} | |||
return getSimpleName(p) + " (" + p.getConnectorId() + ")"; | |||
} | |||
/** | |||
* Resolve a relative URL to an absolute URL based on the current document's | |||
* location. | |||
* | |||
* @param url | |||
* a string with the relative URL to resolve | |||
* @return the corresponding absolute URL as a string | |||
*/ | |||
public static String getAbsoluteUrl(String url) { | |||
AnchorElement a = Document.get().createAnchorElement(); | |||
a.setHref(url); | |||
return a.getHref(); | |||
} | |||
} |
@@ -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<String, String[]> parameterMap = new HashMap<String, String[]>( | |||
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. |
@@ -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<String, String[]> 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<String, String[]> map = new HashMap<String, String[]>(); | |||
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<String, String[]> 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 |
@@ -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 |
@@ -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; | |||
} |
@@ -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; | |||
} |
@@ -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; | |||
} | |||
} | |||
@Override | |||
public String getMainDivId(VaadinServiceSession session, | |||
VaadinRequest request, Class<? extends UI> 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(); | |||
} | |||
} |
@@ -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<? extends UI> uiClass); | |||
} |
@@ -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: | |||
* <ul> | |||
* <li>An application runner servlet that runs different Vaadin applications | |||
* based on an identifier.</li> | |||
* <li>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)</li> | |||
* | |||
* @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; |
@@ -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(); | |||
} | |||
} | |||
@Override | |||
public String getMainDivId(VaadinServiceSession session, | |||
VaadinRequest request, Class<? extends UI> 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; | |||
} | |||
} |
@@ -39,7 +39,9 @@ public class ApplicationConstants { | |||
@Deprecated | |||
public static final String UPDATE_VARIABLE_METHOD = "v"; | |||
public static final String PORTLET_RESOUCE_URL_BASE = "portletAppURLBase"; | |||
public static final String SERVICE_URL = "serviceUrl"; | |||
public static final String SERVICE_URL_PATH_AS_PARAMETER = "usePathParameter"; | |||
public static final String V_RESOURCE_PATH = "v-resourcePath"; | |||
@@ -52,4 +54,14 @@ public class ApplicationConstants { | |||
* changes. | |||
*/ | |||
public static final String URL_PARAMETER_REPAINT_ALL = "repaintAll"; | |||
/** | |||
* 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 | |||
* vaadinBootstrap.js. | |||
* </p> | |||
*/ | |||
public static final String VAADIN_DIR_URL = "vaadinDir"; | |||
} |
@@ -49,6 +49,8 @@ | |||
mapping problem) --> | |||
<dependency org="org.eclipse.jetty" name="jetty-server" | |||
rev="7.4.5.v20110725" conf="build-provided, ide, jetty-run->default" /> | |||
<dependency org="org.eclipse.jetty" name="jetty-servlets" | |||
rev="7.4.5.v20110725" conf="build-provided, ide, jetty-run->default" /> | |||
<!-- <dependency org="org.mortbay.jetty" name="jetty-util" --> | |||
<!-- rev="8.1.5.v20120716" conf="build,ide,jetty-run->default" /> --> | |||
<dependency org="org.eclipse.jetty" name="jetty-webapp" |
@@ -30,7 +30,6 @@ import javax.servlet.http.HttpServletRequest; | |||
import javax.servlet.http.HttpServletResponse; | |||
import com.vaadin.LegacyApplication; | |||
import com.vaadin.server.DeploymentConfiguration; | |||
import com.vaadin.server.LegacyVaadinServlet; | |||
import com.vaadin.server.ServiceException; | |||
import com.vaadin.server.SessionInitEvent; | |||
@@ -40,7 +39,6 @@ import com.vaadin.server.UIProvider; | |||
import com.vaadin.server.VaadinRequest; | |||
import com.vaadin.server.VaadinServiceSession; | |||
import com.vaadin.server.VaadinServletRequest; | |||
import com.vaadin.server.VaadinServletService; | |||
import com.vaadin.tests.components.TestBase; | |||
import com.vaadin.ui.UI; | |||
@@ -174,8 +172,9 @@ public class ApplicationRunnerServlet extends LegacyVaadinServlet { | |||
return getApplicationRunnerURIs(request).applicationClassname; | |||
} | |||
// TODO Don't need to use a data object now that there's only one field | |||
private static class URIS { | |||
String staticFilesPath; | |||
// String staticFilesPath; | |||
// String applicationURI; | |||
// String context; | |||
// String runner; | |||
@@ -201,21 +200,18 @@ public class ApplicationRunnerServlet extends LegacyVaadinServlet { | |||
private static URIS getApplicationRunnerURIs(HttpServletRequest request) { | |||
final String[] urlParts = request.getRequestURI().toString() | |||
.split("\\/"); | |||
String context = null; | |||
// String runner = null; | |||
URIS uris = new URIS(); | |||
String applicationClassname = null; | |||
String contextPath = request.getContextPath(); | |||
if (urlParts[1].equals(contextPath.replaceAll("\\/", ""))) { | |||
// class name comes after web context and runner application | |||
context = urlParts[1]; | |||
// runner = urlParts[2]; | |||
if (urlParts.length == 3) { | |||
throw new IllegalArgumentException("No application specified"); | |||
} | |||
applicationClassname = urlParts[3]; | |||
uris.staticFilesPath = "/" + context; | |||
// uris.applicationURI = "/" + context + "/" + runner + "/" | |||
// + applicationClassname; | |||
// uris.context = context; | |||
@@ -223,14 +219,12 @@ public class ApplicationRunnerServlet extends LegacyVaadinServlet { | |||
uris.applicationClassname = applicationClassname; | |||
} else { | |||
// no context | |||
context = ""; | |||
// runner = urlParts[1]; | |||
if (urlParts.length == 2) { | |||
throw new IllegalArgumentException("No application specified"); | |||
} | |||
applicationClassname = urlParts[2]; | |||
uris.staticFilesPath = "/"; | |||
// uris.applicationURI = "/" + runner + "/" + applicationClassname; | |||
// uris.context = context; | |||
// uris.runner = runner; | |||
@@ -274,47 +268,6 @@ public class ApplicationRunnerServlet extends LegacyVaadinServlet { | |||
throw new ClassNotFoundException(); | |||
} | |||
@Override | |||
protected String getRequestPathInfo(HttpServletRequest request) { | |||
String path = request.getPathInfo(); | |||
if (path == null) { | |||
return null; | |||
} | |||
path = path.substring(1 + getApplicationRunnerApplicationClassName( | |||
request).length()); | |||
return path; | |||
} | |||
@Override | |||
protected VaadinServletService createServletService( | |||
DeploymentConfiguration deploymentConfiguration) { | |||
return new VaadinServletService(this, deploymentConfiguration) { | |||
@Override | |||
public String getStaticFileLocation(VaadinRequest request) { | |||
URIS uris = getApplicationRunnerURIs(VaadinServletRequest | |||
.cast(request)); | |||
String staticFilesPath = uris.staticFilesPath; | |||
if (staticFilesPath.equals("/")) { | |||
staticFilesPath = ""; | |||
} | |||
return staticFilesPath; | |||
} | |||
}; | |||
} | |||
@Override | |||
protected VaadinServletRequest createVaadinRequest( | |||
HttpServletRequest request) { | |||
return new VaadinServletRequest(request, getService()) { | |||
@Override | |||
public String getRequestPathInfo() { | |||
return ApplicationRunnerServlet.this.getRequestPathInfo(this); | |||
} | |||
}; | |||
} | |||
private Logger getLogger() { | |||
return Logger.getLogger(ApplicationRunnerServlet.class.getName()); | |||
} |
@@ -0,0 +1,111 @@ | |||
<?xml version="1.0" encoding="UTF-8"?> | |||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> | |||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> | |||
<head profile="http://selenium-ide.openqa.org/profiles/test-case"> | |||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> | |||
<link rel="selenium.base" href="" /> | |||
<title>AddAndRemoveTabs</title> | |||
</head> | |||
<body> | |||
<table cellpadding="1" cellspacing="1" border="1"> | |||
<thead> | |||
<tr><td rowspan="1" colspan="3">AddAndRemoveTabs</td></tr> | |||
</thead><tbody> | |||
<tr> | |||
<td>open</td> | |||
<td>/run/com.vaadin.tests.integration.ProxyTest?restartApplication</td> | |||
<td></td> | |||
</tr> | |||
<tr> | |||
<td>click</td> | |||
<td>vaadin=runcomvaadintestsintegrationProxyTest::/VVerticalLayout[0]/VOrderedLayout$Slot[1]/VVerticalLayout[0]/VOrderedLayout$Slot[0]/VButton[0]/domChild[0]/domChild[0]</td> | |||
<td></td> | |||
</tr> | |||
<tr> | |||
<td>click</td> | |||
<td>vaadin=runcomvaadintestsintegrationProxyTest::/VVerticalLayout[0]/VOrderedLayout$Slot[1]/VVerticalLayout[0]/VOrderedLayout$Slot[2]/VVerticalLayout[0]/VOrderedLayout$Slot[0]/VLink[0]/domChild[0]/domChild[0]</td> | |||
<td></td> | |||
</tr> | |||
<tr> | |||
<td>pause</td> | |||
<td></td> | |||
<td>2000</td> | |||
</tr> | |||
<tr> | |||
<td>assertText</td> | |||
<td>vaadin=embed1::/VVerticalLayout[0]/VOrderedLayout$Slot[0]/VLabel[0]</td> | |||
<td>A generic test for Buttons in different configurations</td> | |||
</tr> | |||
<tr> | |||
<td>mouseClick</td> | |||
<td>vaadin=embed1::/VVerticalLayout[0]/VOrderedLayout$Slot[1]/VVerticalLayout[0]/VOrderedLayout$Slot[0]/VHorizontalLayout[0]/VOrderedLayout$Slot[0]/VCheckBox[0]/domChild[0]</td> | |||
<td>5,5</td> | |||
</tr> | |||
<tr> | |||
<td>assertCSSClass</td> | |||
<td>vaadin=embed1::/VVerticalLayout[0]/VOrderedLayout$Slot[1]/VVerticalLayout[0]/VOrderedLayout$Slot[1]/VButton[0]</td> | |||
<td>v-disabled</td> | |||
</tr> | |||
<tr> | |||
<td>open</td> | |||
<td>/run/com.vaadin.tests.integration.ProxyTest</td> | |||
<td></td> | |||
</tr> | |||
<tr> | |||
<td>click</td> | |||
<td>vaadin=runcomvaadintestsintegrationProxyTest::/VVerticalLayout[0]/VOrderedLayout$Slot[1]/VVerticalLayout[0]/VOrderedLayout$Slot[2]/VVerticalLayout[0]/VOrderedLayout$Slot[1]/VLink[0]/domChild[0]/domChild[0]</td> | |||
<td></td> | |||
</tr> | |||
<tr> | |||
<td>pause</td> | |||
<td></td> | |||
<td>2000</td> | |||
</tr> | |||
<tr> | |||
<td>mouseClick</td> | |||
<td>vaadin=embed1::/VVerticalLayout[0]/VOrderedLayout$Slot[1]/VVerticalLayout[0]/VOrderedLayout$Slot[0]/VHorizontalLayout[0]/VOrderedLayout$Slot[0]/VCheckBox[0]/domChild[0]</td> | |||
<td>7,5</td> | |||
</tr> | |||
<tr> | |||
<td>assertNotCSSClass</td> | |||
<td>vaadin=embed1::/VVerticalLayout[0]/VOrderedLayout$Slot[1]/VVerticalLayout[0]/VOrderedLayout$Slot[1]/VButton[0]</td> | |||
<td>v-disabled</td> | |||
</tr> | |||
<tr> | |||
<td>open</td> | |||
<td>/run/com.vaadin.tests.integration.ProxyTest</td> | |||
<td></td> | |||
</tr> | |||
<tr> | |||
<td>click</td> | |||
<td>vaadin=runcomvaadintestsintegrationProxyTest::/VVerticalLayout[0]/VOrderedLayout$Slot[1]/VVerticalLayout[0]/VOrderedLayout$Slot[2]/VVerticalLayout[0]/VOrderedLayout$Slot[2]/VLink[0]/domChild[0]/domChild[0]</td> | |||
<td></td> | |||
</tr> | |||
<tr> | |||
<td>pause</td> | |||
<td></td> | |||
<td>2000</td> | |||
</tr> | |||
<tr> | |||
<td>mouseClick</td> | |||
<td>vaadin=runcomvaadintestscomponentsbuttonButtons::/VVerticalLayout[0]/VOrderedLayout$Slot[1]/VVerticalLayout[0]/VOrderedLayout$Slot[0]/VHorizontalLayout[0]/VOrderedLayout$Slot[0]/VCheckBox[0]/domChild[0]</td> | |||
<td>35,7</td> | |||
</tr> | |||
<tr> | |||
<td>assertCSSClass</td> | |||
<td>vaadin=runcomvaadintestscomponentsbuttonButtons::/VVerticalLayout[0]/VOrderedLayout$Slot[1]/VVerticalLayout[0]/VOrderedLayout$Slot[1]/VButton[0]</td> | |||
<td>v-disabled</td> | |||
</tr> | |||
<tr> | |||
<td>open</td> | |||
<td>/run/com.vaadin.tests.integration.ProxyTest</td> | |||
<td></td> | |||
</tr> | |||
<tr> | |||
<td>click</td> | |||
<td>vaadin=runcomvaadintestsintegrationProxyTest::/VVerticalLayout[0]/VOrderedLayout$Slot[1]/VVerticalLayout[0]/VOrderedLayout$Slot[1]/VButton[0]/domChild[0]/domChild[0]</td> | |||
<td></td> | |||
</tr> | |||
</tbody></table> | |||
</body> | |||
</html> |
@@ -0,0 +1,155 @@ | |||
/* | |||
* Copyright 2011 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.integration; | |||
import javax.servlet.http.HttpServletRequest; | |||
import org.eclipse.jetty.server.Connector; | |||
import org.eclipse.jetty.server.Server; | |||
import org.eclipse.jetty.server.nio.SelectChannelConnector; | |||
import org.eclipse.jetty.servlet.ServletContextHandler; | |||
import org.eclipse.jetty.servlet.ServletHolder; | |||
import org.eclipse.jetty.servlets.ProxyServlet; | |||
import com.vaadin.annotations.PreserveOnRefresh; | |||
import com.vaadin.server.ExternalResource; | |||
import com.vaadin.server.VaadinRequest; | |||
import com.vaadin.server.VaadinServletService; | |||
import com.vaadin.tests.components.AbstractTestUI; | |||
import com.vaadin.ui.Button; | |||
import com.vaadin.ui.Button.ClickEvent; | |||
import com.vaadin.ui.Link; | |||
import com.vaadin.ui.VerticalLayout; | |||
@PreserveOnRefresh | |||
public class ProxyTest extends AbstractTestUI { | |||
private Server server; | |||
private final Button startButton = new Button("Start proxy", | |||
new Button.ClickListener() { | |||
@Override | |||
public void buttonClick(ClickEvent event) { | |||
startProxy(); | |||
stopButton.setEnabled(true); | |||
} | |||
}); | |||
private final Button stopButton = new Button("Stop proxy", | |||
new Button.ClickListener() { | |||
@Override | |||
public void buttonClick(ClickEvent event) { | |||
stopProxy(); | |||
startButton.setEnabled(true); | |||
} | |||
}); | |||
private VerticalLayout linkHolder = new VerticalLayout(); | |||
@Override | |||
protected void setup(VaadinRequest request) { | |||
stopButton.setDisableOnClick(true); | |||
stopButton.setEnabled(false); | |||
startButton.setDisableOnClick(true); | |||
addCleanupListener(new CleanupListener() { | |||
@Override | |||
public void cleanup(CleanupEvent event) { | |||
if (server != null && server.isRunning()) { | |||
try { | |||
server.stop(); | |||
} catch (Exception e) { | |||
throw new RuntimeException(e); | |||
} | |||
} | |||
} | |||
}); | |||
addComponent(startButton); | |||
addComponent(stopButton); | |||
addComponent(linkHolder); | |||
} | |||
private void startProxy() { | |||
HttpServletRequest request = VaadinServletService | |||
.getCurrentServletRequest(); | |||
// Set up a server | |||
server = new Server(); | |||
SelectChannelConnector connector = new SelectChannelConnector(); | |||
// Uses random available port by default, uncomment this to make local | |||
// testing easier (you can just reload old tab after restarting proxy) | |||
// connector.setPort(8889); | |||
server.setConnectors(new Connector[] { connector }); | |||
// Create root context and add the ProxyServlet.Transparent to it | |||
ServletContextHandler contextHandler = new ServletContextHandler(); | |||
server.setHandler(contextHandler); | |||
contextHandler.setContextPath("/"); | |||
ServletHolder servletHolder = contextHandler.addServlet( | |||
ProxyServlet.Transparent.class, "/*"); | |||
// Configure servlet to forward to the root of the original server | |||
servletHolder.setInitParameter( | |||
"ProxyTo", | |||
"http://" + request.getLocalAddr() + ":" | |||
+ request.getLocalPort() + "/"); | |||
// Configure servlet to strip beginning of paths | |||
servletHolder.setInitParameter("Prefix", "/proxypath/"); | |||
// Start the server | |||
try { | |||
server.start(); | |||
} catch (Exception e) { | |||
throw new RuntimeException(e); | |||
} | |||
// Add links to some proxied urls to tests | |||
String linkBase = "http://" + request.getLocalName() + ":" | |||
+ connector.getLocalPort() + "/proxypath/"; | |||
linkHolder.removeAllComponents(); | |||
linkHolder.addComponent(new Link("Open embed1 in proxy", | |||
new ExternalResource(linkBase + "embed1"))); | |||
linkHolder.addComponent(new Link("Open embed1/ in proxy", | |||
new ExternalResource(linkBase + "embed1/"))); | |||
linkHolder.addComponent(new Link("Open Buttons in proxy", | |||
new ExternalResource(linkBase | |||
+ "run/com.vaadin.tests.components.button.Buttons"))); | |||
} | |||
private void stopProxy() { | |||
linkHolder.removeAllComponents(); | |||
try { | |||
server.stop(); | |||
} catch (Exception e) { | |||
throw new RuntimeException(e); | |||
} | |||
server.destroy(); | |||
server = null; | |||
} | |||
@Override | |||
protected String getTestDescription() { | |||
return "Test UI for starting an embedded Jetty on a different port that proxies requests back to the original server using a different path."; | |||
} | |||
@Override | |||
protected Integer getTicketNumber() { | |||
return Integer.valueOf(6771); | |||
} | |||
} |