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
config[property] = updatedConfig[property];
}
}
- config.initPending = false;
// Try bootstrapping again, this time without fetching missing info
bootstrapApp(false);
apps[appId] = app;
if (!window.name) {
- var rootId = getConfig('rootId');
- window.name = appId + '-' + rootId;
+ window.name = appId + '-' + Math.random();
}
var bootstrapApp = function(mayDefer) {
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 {
}
}
- /**
- * Helper class to keep track of the information from an initial request if
- * another request is required before the <code>Root</code> 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<String, String[]> 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<String, String[]>(
- 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());
private boolean productionMode = true;
- /**
- * Keeps track of requests for which a root should be created once more
- * information is available.
- */
- private Map<Integer, PendingRootRequest> pendingRoots = new HashMap<Integer, PendingRootRequest>();
-
private final Map<String, Integer> retainOnRefreshRoots = new HashMap<String, Integer>();
/**
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
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()) {
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
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;
public class CombinedRequest implements WrappedRequest {
private final WrappedRequest secondRequest;
- private final Map<String, String[]> parameterMap;
- private final String pathInfo;
+ private Map<String, String[]> parameterMap;
/**
* Creates a new combined request based on the second request and some
* @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<String, String[]> parameterMap, String pathInfo) {
+ public CombinedRequest(WrappedRequest secondRequest) throws JSONException {
this.secondRequest = secondRequest;
- this.parameterMap = parameterMap;
- this.pathInfo = pathInfo;
+
+ 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);
+
}
public String getParameter(String parameter) {
- String[] strings = parameterMap.get(parameter);
+ String[] strings = getParameterMap().get(parameter);
if (strings == null || strings.length == 0) {
return null;
} else {
}
public Map<String, String[]> getParameterMap() {
- return Collections.unmodifiableMap(parameterMap);
+ return parameterMap;
}
public int getContentLength() {
}
public String getRequestPathInfo() {
- return pathInfo;
+ return secondRequest.getParameter("initialPath");
}
public int getSessionMaxInactiveInterval() {
// 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");
import java.io.OutputStreamWriter;
import java.io.Serializable;
import java.io.Writer;
+import java.util.Map;
import javax.servlet.http.HttpServletResponse;
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;
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;
return writer;
}
- public int getRootId() {
+ public Integer getRootId() {
return rootId;
}
throws IOException {
// TODO Should all urls be handled here?
- int rootId;
+ Integer rootId = null;
try {
Root root = application.getRootForRequest(request);
if (root == null) {
return true;
}
- rootId = root.getRootId();
+ rootId = Integer.valueOf(root.getRootId());
} catch (RootRequiresMoreInformationException e) {
- rootId = application.registerPendingRoot(request);
+ // Just keep going without rootId
}
try {
}
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,
}
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;
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",
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<String, String[]> parameterMap = context.getRequest()
+ .getParameterMap();
+ appConfig.put("initialParams", parameterMap);
} else {
// write the initial UIDL into the config
appConfig.put("uidl",
<td>vaadin=runcomvaadintestsapplicationRefreshStatePreserve::/VVerticalLayout[0]/ChildComponentContainer[1]/VLabel[0]</td>
<td>Root id: 0</td>
</tr>
+<tr>
+ <td>runScript</td>
+ <td>history.back()</td>
+ <td></td>
+</tr>
+<tr>
+ <td>pause</td>
+ <td></td>
+ <td>1000</td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>vaadin=runcomvaadintestsapplicationRefreshStatePreserve::/VVerticalLayout[0]/ChildComponentContainer[1]/VLabel[0]</td>
+ <td>Root id: 0</td>
+</tr>
</tbody></table>
</body>