From b3e2adea172482c7cb682aaa30b51f4900c8bf2a Mon Sep 17 00:00:00 2001 From: =?utf8?q?Leif=20=C3=85strand?= Date: Wed, 4 Jan 2012 19:07:34 +0200 Subject: [PATCH] Pass initial request details through the browser (#8232) By passing the details through the browser, we don't need any state mapped to a rootId that could be sent multiple times by caching in the browser Old test updated to test for this --- WebContent/VAADIN/vaadinBootstrap.js | 17 +-- src/com/vaadin/Application.java | 117 +----------------- src/com/vaadin/terminal/CombinedRequest.java | 41 ++++-- .../server/AbstractCommunicationManager.java | 6 +- .../terminal/gwt/server/BootstrapHandler.java | 32 +++-- .../application/RefreshStatePreserve.html | 15 +++ 6 files changed, 76 insertions(+), 152 deletions(-) diff --git a/WebContent/VAADIN/vaadinBootstrap.js b/WebContent/VAADIN/vaadinBootstrap.js index 1de61917b8..1f5f3fa973 100644 --- a/WebContent/VAADIN/vaadinBootstrap.js +++ b/WebContent/VAADIN/vaadinBootstrap.js @@ -100,9 +100,15 @@ url += '/'; } } - // Root id url += ((/\?/).test(url) ? "&" : "?") + "browserDetails"; - url += '&rootId=' + getConfig('rootId'); + var rootId = getConfig("rootId"); + if (rootId !== undefined) { + url += "&rootId=" + rootId; + } + + url += '&initialPath=' + encodeURIComponent(getConfig("initialPath")); + url += '&initialParams=' + encodeURIComponent(JSON.stringify(getConfig("initialParams"))); + url += '&' + vaadin.getBrowserDetailsParameters(appId); // Timestamp to avoid caching @@ -122,7 +128,6 @@ config[property] = updatedConfig[property]; } } - config.initPending = false; // Try bootstrapping again, this time without fetching missing info bootstrapApp(false); @@ -143,8 +148,7 @@ apps[appId] = app; if (!window.name) { - var rootId = getConfig('rootId'); - window.name = appId + '-' + rootId; + window.name = appId + '-' + Math.random(); } var bootstrapApp = function(mayDefer) { @@ -155,12 +159,11 @@ var widgetsetBase = getConfig('widgetsetBase'); var widgetset = getConfig('widgetset'); - var initPending = getConfig('initPending'); if (widgetset && widgetsetBase) { loadWidgetset(widgetsetBase, widgetset); } - if (initPending) { + if (getConfig('uidl') === undefined) { if (mayDefer) { fetchRootConfig(); } else { diff --git a/src/com/vaadin/Application.java b/src/com/vaadin/Application.java index 8e88a677f6..e426ea3085 100644 --- a/src/com/vaadin/Application.java +++ b/src/com/vaadin/Application.java @@ -325,55 +325,6 @@ public class Application implements Terminal.ErrorListener, Serializable { } } - /** - * Helper class to keep track of the information from an initial request if - * another request is required before the Root can be - * initialized. - * - * The saved information is then used together with information from the - * second request to create a {@link CombinedRequest} containing relevant - * parts from each request. - */ - private static class PendingRootRequest implements Serializable { - - private final Map parameterMap; - private final String pathInfo; - - /** - * Creates a new pending request from an initial request. This is done - * by saving the parameterMap and the pathInfo from the provided wrapped - * request. - * - * @param request - * the initial request from which the required data is - * extracted - */ - public PendingRootRequest(WrappedRequest request) { - // Create a defensive copy in case the Map instance is reused - parameterMap = new HashMap( - request.getParameterMap()); - pathInfo = request.getRequestPathInfo(); - } - - /** - * Creates a new request by combining information from the initial - * request with information from the provided second request. - * - * @param secondRequest - * the second request, should contain the information - * required for providing {@link BrowserDetails} - * @return a request providing a combined view of the information from - * the two original requests - * - * @see CombinedRequest#CombinedRequest(WrappedRequest, Map, String) - */ - public CombinedRequest getCombinedRequest( - final WrappedRequest secondRequest) { - return new CombinedRequest(secondRequest, - Collections.unmodifiableMap(parameterMap), pathInfo); - } - } - private final static Logger logger = Logger.getLogger(Application.class .getName()); @@ -452,12 +403,6 @@ public class Application implements Terminal.ErrorListener, Serializable { private boolean productionMode = true; - /** - * Keeps track of requests for which a root should be created once more - * information is available. - */ - private Map pendingRoots = new HashMap(); - private final Map retainOnRefreshRoots = new HashMap(); /** @@ -2128,59 +2073,6 @@ public class Application implements Terminal.ErrorListener, Serializable { return productionMode; } - /** - * Registers a request that will lead to a root being created in a - * subsequent request. When the initial request does not contain all the - * information required to initialize a {@link Root}, some information from - * the initial request is still needed when processing a subsequent request - * containing the rest of the required information. By registering the - * initial request, it can be combined with the subsequent request using the - * root id returned by this method. - * - * @param request - * the initial request from which information is required when - * the subsequent request is processed - * @return the root id that should be used to associate the passed request - * with future requests related to the same Root - * - * @see #getCombinedRequest(WrappedRequest) - * @see #getRoot(WrappedRequest) - * - * @since 7.0 - */ - public int registerPendingRoot(WrappedRequest request) { - int rootId = nextRootId++; - pendingRoots.put(Integer.valueOf(rootId), new PendingRootRequest( - request)); - return rootId; - } - - /** - * Gets a request containing some aspects from the original request and some - * aspects from the current request. This is used during the two phase - * initialization of Roots with the first request registered using - * {@link #registerPendingRoot(WrappedRequest)} - * - * @param request - * the second request, should be sent from the bootstrap - * javascript - * @return a request containing some aspects of the initial request and some - * aspects from the current request - * - * @see #registerPendingRoot(WrappedRequest) - * - * @since 7.0 - */ - public CombinedRequest getCombinedRequest(WrappedRequest request) { - PendingRootRequest pendingRootRequest = pendingRoots - .get(getRootId(request)); - if (pendingRootRequest == null) { - return null; - } else { - return pendingRootRequest.getCombinedRequest(request); - } - } - /** * Finds the {@link Root} to which a particular request belongs. If the * request originates from an existing Root, that root is returned. In other @@ -2220,11 +2112,6 @@ public class Application implements Terminal.ErrorListener, Serializable { boolean hasBrowserDetails = browserDetails != null && browserDetails.getUriFragment() != null; - if (hasBrowserDetails) { - // Don't wait for a second request any more - pendingRoots.remove(rootId); - } - root = roots.get(rootId); if (root == null && isRootPreserved()) { @@ -2269,9 +2156,7 @@ public class Application implements Terminal.ErrorListener, Serializable { boolean initRequiresBrowserDetails = isRootPreserved() || !root.getClass() .isAnnotationPresent(EagerInit.class); - if (initRequiresBrowserDetails && !hasBrowserDetails) { - pendingRoots.put(rootId, new PendingRootRequest(request)); - } else { + if (!initRequiresBrowserDetails || hasBrowserDetails) { root.doInit(request); // Remember that this root has been initialized diff --git a/src/com/vaadin/terminal/CombinedRequest.java b/src/com/vaadin/terminal/CombinedRequest.java index 88e976120a..ccef6d8963 100644 --- a/src/com/vaadin/terminal/CombinedRequest.java +++ b/src/com/vaadin/terminal/CombinedRequest.java @@ -7,10 +7,15 @@ package com.vaadin.terminal; import java.io.IOException; import java.io.InputStream; import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; import java.util.Locale; import java.util.Map; import com.vaadin.Application; +import com.vaadin.external.json.JSONArray; +import com.vaadin.external.json.JSONException; +import com.vaadin.external.json.JSONObject; import com.vaadin.terminal.gwt.server.WebApplicationContext; import com.vaadin.terminal.gwt.server.WebBrowser; @@ -26,8 +31,7 @@ import com.vaadin.terminal.gwt.server.WebBrowser; public class CombinedRequest implements WrappedRequest { private final WrappedRequest secondRequest; - private final Map parameterMap; - private final String pathInfo; + private Map parameterMap; /** * Creates a new combined request based on the second request and some @@ -36,20 +40,31 @@ public class CombinedRequest implements WrappedRequest { * @param secondRequest * the second request which will be used as the foundation of the * combined request - * @param parameterMap - * the parameter map from the first request - * @param pathInfo - * the path info from string the first request + * @throws JSONException + * if the initialParams parameter can not be decoded */ - public CombinedRequest(WrappedRequest secondRequest, - Map parameterMap, String pathInfo) { + public CombinedRequest(WrappedRequest secondRequest) throws JSONException { this.secondRequest = secondRequest; - this.parameterMap = parameterMap; - this.pathInfo = pathInfo; + + 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); + } public String getParameter(String parameter) { - String[] strings = parameterMap.get(parameter); + String[] strings = getParameterMap().get(parameter); if (strings == null || strings.length == 0) { return null; } else { @@ -58,7 +73,7 @@ public class CombinedRequest implements WrappedRequest { } public Map getParameterMap() { - return Collections.unmodifiableMap(parameterMap); + return parameterMap; } public int getContentLength() { @@ -78,7 +93,7 @@ public class CombinedRequest implements WrappedRequest { } public String getRequestPathInfo() { - return pathInfo; + return secondRequest.getParameter("initialPath"); } public int getSessionMaxInactiveInterval() { diff --git a/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java b/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java index 70d7436e1d..ca79dcfc44 100644 --- a/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java +++ b/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java @@ -1986,11 +1986,9 @@ public abstract class AbstractCommunicationManager implements // shortly, and we should send the initial UIDL boolean sendUIDL = Root.getCurrentRoot() == null; - // TODO Handle npe if id has not been registered - CombinedRequest combinedRequest = application - .getCombinedRequest(request); - try { + CombinedRequest combinedRequest = new CombinedRequest(request); + Root root = application.getRootForRequest(combinedRequest); response.setContentType("application/json; charset=UTF-8"); diff --git a/src/com/vaadin/terminal/gwt/server/BootstrapHandler.java b/src/com/vaadin/terminal/gwt/server/BootstrapHandler.java index 6bdcc0cff5..f3cd585b5a 100644 --- a/src/com/vaadin/terminal/gwt/server/BootstrapHandler.java +++ b/src/com/vaadin/terminal/gwt/server/BootstrapHandler.java @@ -9,6 +9,7 @@ import java.io.IOException; import java.io.OutputStreamWriter; import java.io.Serializable; import java.io.Writer; +import java.util.Map; import javax.servlet.http.HttpServletResponse; @@ -31,7 +32,7 @@ public abstract class BootstrapHandler implements RequestHandler { private final WrappedResponse response; private final WrappedRequest request; private final Application application; - private final int rootId; + private final Integer rootId; private Writer writer; private Root root; @@ -42,7 +43,7 @@ public abstract class BootstrapHandler implements RequestHandler { private boolean rootFetched = false; public BootstrapContext(WrappedResponse response, - WrappedRequest request, Application application, int rootId) { + WrappedRequest request, Application application, Integer rootId) { this.response = response; this.request = request; this.application = application; @@ -70,7 +71,7 @@ public abstract class BootstrapHandler implements RequestHandler { return writer; } - public int getRootId() { + public Integer getRootId() { return rootId; } @@ -116,7 +117,7 @@ public abstract class BootstrapHandler implements RequestHandler { throws IOException { // TODO Should all urls be handled here? - int rootId; + Integer rootId = null; try { Root root = application.getRootForRequest(request); if (root == null) { @@ -124,9 +125,9 @@ public abstract class BootstrapHandler implements RequestHandler { return true; } - rootId = root.getRootId(); + rootId = Integer.valueOf(root.getRootId()); } catch (RootRequiresMoreInformationException e) { - rootId = application.registerPendingRoot(request); + // Just keep going without rootId } try { @@ -139,7 +140,7 @@ public abstract class BootstrapHandler implements RequestHandler { } protected final void writeBootstrapPage(WrappedRequest request, - WrappedResponse response, Application application, int rootId) + WrappedResponse response, Application application, Integer rootId) throws IOException, JSONException { BootstrapContext context = createContext(request, response, @@ -170,7 +171,7 @@ public abstract class BootstrapHandler implements RequestHandler { } public BootstrapContext createContext(WrappedRequest request, - WrappedResponse response, Application application, int rootId) { + WrappedResponse response, Application application, Integer rootId) { BootstrapContext context = new BootstrapContext(response, request, application, rootId); return context; @@ -350,11 +351,13 @@ public abstract class BootstrapHandler implements RequestHandler { protected JSONObject getApplicationParameters(BootstrapContext context) throws JSONException, PaintException { Application application = context.getApplication(); - int rootId = context.getRootId(); + Integer rootId = context.getRootId(); JSONObject appConfig = new JSONObject(); - appConfig.put(ApplicationConnection.ROOT_ID_PARAMETER, rootId); + if (rootId != null) { + appConfig.put(ApplicationConnection.ROOT_ID_PARAMETER, rootId); + } if (context.getThemeName() != null) { appConfig.put("themeUri", @@ -368,8 +371,13 @@ public abstract class BootstrapHandler implements RequestHandler { appConfig.put("widgetset", context.getWidgetsetName()); - if (application.isRootInitPending(rootId)) { - appConfig.put("initPending", true); + if (rootId == null || application.isRootInitPending(rootId.intValue())) { + appConfig.put("initialPath", context.getRequest() + .getRequestPathInfo()); + + Map parameterMap = context.getRequest() + .getParameterMap(); + appConfig.put("initialParams", parameterMap); } else { // write the initial UIDL into the config appConfig.put("uidl", diff --git a/tests/testbench/com/vaadin/tests/application/RefreshStatePreserve.html b/tests/testbench/com/vaadin/tests/application/RefreshStatePreserve.html index b242606e78..d48ab220b5 100644 --- a/tests/testbench/com/vaadin/tests/application/RefreshStatePreserve.html +++ b/tests/testbench/com/vaadin/tests/application/RefreshStatePreserve.html @@ -31,6 +31,21 @@ vaadin=runcomvaadintestsapplicationRefreshStatePreserve::/VVerticalLayout[0]/ChildComponentContainer[1]/VLabel[0] Root id: 0 + + runScript + history.back() + + + + pause + + 1000 + + + assertText + vaadin=runcomvaadintestsapplicationRefreshStatePreserve::/VVerticalLayout[0]/ChildComponentContainer[1]/VLabel[0] + Root id: 0 + -- 2.39.5