Make manual retries also append parameters so that failed downloads are not cached
Review by: zundel@google.com
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@10400 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/core/client/impl/LoadingStrategyBase.java b/user/src/com/google/gwt/core/client/impl/LoadingStrategyBase.java
index 2cf9495..130200c 100644
--- a/user/src/com/google/gwt/core/client/impl/LoadingStrategyBase.java
+++ b/user/src/com/google/gwt/core/client/impl/LoadingStrategyBase.java
@@ -15,6 +15,7 @@
*/
package com.google.gwt.core.client.impl;
+import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.impl.AsyncFragmentLoader.LoadTerminatedHandler;
import com.google.gwt.core.client.impl.AsyncFragmentLoader.LoadingStrategy;
import com.google.gwt.core.client.impl.AsyncFragmentLoader.HttpInstallFailure;
@@ -59,7 +60,27 @@
interface DownloadStrategy {
void tryDownload(final RequestData request);
}
+
+ /**
+ * A trivial JavaScript map from ints to ints. Used to keep a global count of
+ * how many times a user has manually retried fetching a fragment.
+ */
+ private static final class FragmentReloadTracker extends JavaScriptObject {
+ public static FragmentReloadTracker create() {
+ return (FragmentReloadTracker) JavaScriptObject.createArray();
+ }
+ protected FragmentReloadTracker() { }
+
+ public native int get(int x) /*-{
+ return this[x] ? this[x] : 0;
+ }-*/;
+
+ public native void put(int x, int y) /*-{
+ this[x] = y;
+ }-*/;
+ }
+
/**
* Since LoadingStrategy must support concurrent requests, we keep most of the
* relevant info in the RequestData, and pass it around. Once created, a
@@ -97,7 +118,8 @@
if (mayRetry) {
retryCount++;
if (retryCount <= maxRetryCount) {
- url = originalUrl + "?serial=" + retryCount;
+ char connector = originalUrl.contains("?") ? '&' : '?';
+ url = originalUrl + connector + "autoRetry=" + retryCount;
downloadStrategy.tryDownload(this);
return;
}
@@ -126,7 +148,7 @@
* The number of times that we will retry a download. Note that if the install
* fails, we do not retry, since there's no reason to expect a different result.
*/
- public static int MAX_RETRY_COUNT = 3;
+ public static int MAX_AUTO_RETRY_COUNT = 3;
/**
* Call the linker-supplied <code>__gwtInstallCode</code> method. This method
@@ -153,7 +175,8 @@
}-*/;
private DownloadStrategy downloadStrategy;
-
+ private final FragmentReloadTracker manualRetryNumbers = FragmentReloadTracker.create();
+
/**
* Subclasses should create a DownloadStrategy and pass it into this constructor.
*/
@@ -161,8 +184,6 @@
this.downloadStrategy = downloadStrategy;
}
- protected int getMaxRetryCount() { return MAX_RETRY_COUNT; }
-
@Override
public void startLoadingFragment(int fragment,
final LoadTerminatedHandler loadErrorHandler) {
@@ -171,8 +192,24 @@
// The linker is going to handle this fetch - nothing more to do
return;
}
+ // Browsers will ignore too many script tags if it has previously failed
+ // to download that url, so we add a parameter to the url if
+ // this is not the first time we've tried to download this fragment.
+ int manualRetry = getManualRetryNum(fragment);
+ if (manualRetry > 0) {
+ char connector = url.contains("?") ? '&' : '?';
+ url += connector + "manualRetry=" + manualRetry;
+ }
RequestData request = new RequestData(url, loadErrorHandler,
- fragment, downloadStrategy, getMaxRetryCount());
+ fragment, downloadStrategy, getMaxAutoRetryCount());
request.tryDownload();
}
+
+ protected int getMaxAutoRetryCount() { return MAX_AUTO_RETRY_COUNT; }
+
+ private int getManualRetryNum(int fragment) {
+ int ser = manualRetryNumbers.get(fragment);
+ manualRetryNumbers.put(fragment, ser + 1);
+ return ser;
+ }
}
diff --git a/user/test/com/google/gwt/dev/jjs/test/RunAsyncFailureTest.java b/user/test/com/google/gwt/dev/jjs/test/RunAsyncFailureTest.java
index 77267d9..65d6fae 100644
--- a/user/test/com/google/gwt/dev/jjs/test/RunAsyncFailureTest.java
+++ b/user/test/com/google/gwt/dev/jjs/test/RunAsyncFailureTest.java
@@ -100,8 +100,6 @@
// otherwise the runAsync code will cache the fragment and later tests will
// always succeed on the first try.
- // Fragment for this call (5) should be on downloadErrorFragments list in the
- // RunAsyncFailureServlet
private void runAsync1(final int attempt, final int expectedSuccessfulAttempt) {
GWT.runAsync(new MyRunAsyncCallback(attempt, expectedSuccessfulAttempt) {
public void onFailure(Throwable caught) {
@@ -119,8 +117,6 @@
});
}
- // Fragment for this call (2) should be on downloadErrorFragments list in the
- // RunAsyncFailureServlet
private void runAsync2(final int attempt, final int expectedSuccessfulAttempt) {
GWT.runAsync(new MyRunAsyncCallback(attempt, expectedSuccessfulAttempt) {
public void onFailure(Throwable caught) {
@@ -138,8 +134,6 @@
});
}
- // Fragment for this call (3) should be on downloadErrorFragments list in the
- // RunAsyncFailureServlet
private void runAsync3(final int attempt, final int expectedSuccessfulAttempt) {
GWT.runAsync(new MyRunAsyncCallback(attempt, expectedSuccessfulAttempt) {
public void onFailure(Throwable caught) {
@@ -157,8 +151,6 @@
});
}
- // Fragment for this call (4) should be on installErrorFragments list in the
- // RunAsyncFailureServlet
private void runAsync4() {
GWT.runAsync(new RunAsyncCallback() {
public void onFailure(Throwable caught) {
@@ -181,7 +173,7 @@
public void testHttpFailureRetries() {
delayTestFinish(RUNASYNC_TIMEOUT);
// Default is 3, but we set it explicitly, since other tests may run first
- LoadingStrategyBase.MAX_RETRY_COUNT = 2;
+ LoadingStrategyBase.MAX_AUTO_RETRY_COUNT = 2;
// In RunAsyncFailureServlet, the 5th time is the charm, but the code
// by default retries 3 times every time we call runAsync, so this
// should succeed on the second runAsync call, which is attempt #1.
@@ -190,7 +182,7 @@
public void testHttpFailureRetries2() {
delayTestFinish(RUNASYNC_TIMEOUT);
- LoadingStrategyBase.MAX_RETRY_COUNT = 0;
+ LoadingStrategyBase.MAX_AUTO_RETRY_COUNT = 0;
// In RunAsyncFailureServlet, the 5th time is the charm, so if we do not
// let the code do any retries, we'll need to retry 4 times before succeeding
runAsync2(0, 4);
@@ -198,7 +190,7 @@
public void testBuiltInRetries() {
delayTestFinish(RUNASYNC_TIMEOUT);
- LoadingStrategyBase.MAX_RETRY_COUNT = 4;
+ LoadingStrategyBase.MAX_AUTO_RETRY_COUNT = 4;
// In RunAsyncFailureServlet, the 5th time is the charm, so retrying 4 times
// should be enough to get it on the first try.
runAsync3(0, supportsRetries() ? 0 : 4);
@@ -206,7 +198,7 @@
public void testDownloadSuccessButInstallFailure() {
delayTestFinish(RUNASYNC_TIMEOUT);
- LoadingStrategyBase.MAX_RETRY_COUNT = 3;
+ LoadingStrategyBase.MAX_AUTO_RETRY_COUNT = 3;
runAsync4();
}
}
diff --git a/user/test/com/google/gwt/user/server/runasync/RunAsyncFailureServlet.java b/user/test/com/google/gwt/user/server/runasync/RunAsyncFailureServlet.java
index ad281de..5ecb49e 100644
--- a/user/test/com/google/gwt/user/server/runasync/RunAsyncFailureServlet.java
+++ b/user/test/com/google/gwt/user/server/runasync/RunAsyncFailureServlet.java
@@ -15,6 +15,8 @@
*/
package com.google.gwt.user.server.runasync;
+import com.google.gwt.dev.util.Util;
+
import java.io.IOException;
import java.io.OutputStream;
import java.io.InputStream;
@@ -32,16 +34,8 @@
public class RunAsyncFailureServlet extends HttpServlet {
private static final boolean DEBUG = false;
- private static final HashMap<String, RealContents> realContentsCache = new HashMap<String, RealContents>();
+ private static final HashMap<String, String> realContentsCache = new HashMap<String, String>();
- private static class RealContents {
- public RealContents(int numBytes, byte[] bytes) {
- this.numBytes = numBytes;
- this.bytes = bytes;
- }
- public int numBytes;
- public byte[] bytes;
- }
/**
* Sequence of response codes to send back. SC_OK must be last.
*/
@@ -67,19 +61,16 @@
String originalUri = req.getRequestURI();
debug("doGet Original: " + originalUri);
String uri = originalUri.replace("/runAsyncFailure", "");
-
int response = getDesiredResponse(uri);
- RealContents rc = this.getRealContents(req, uri);
- String realContentsAsString = new String(rc.bytes);
-
+ String realContents = getRealContents(req, uri);
String fragment = uri.substring(uri.lastIndexOf('/') + 1);
- if (!realContentsAsString.contains("DOWNLOAD_FAILURE_TEST")
+ if (!realContents.contains("DOWNLOAD_FAILURE_TEST")
|| response == HttpServletResponse.SC_OK) {
int bytes = 0;
- if (!realContentsAsString.contains("INSTALL_FAILURE_TEST")) {
+ if (!realContents.contains("INSTALL_FAILURE_TEST")) {
OutputStream os = resp.getOutputStream();
- os.write(rc.bytes, 0, rc.numBytes);
- bytes = rc.numBytes;
+ os.write(realContents.getBytes());
+ bytes = realContents.getBytes().length;
os.close();
}
@@ -105,7 +96,7 @@
return responses[tries % responses.length];
}
- private RealContents getRealContents(HttpServletRequest req, String uri) throws IOException {
+ private String getRealContents(HttpServletRequest req, String uri) throws IOException {
if (realContentsCache.containsKey(uri)) {
return realContentsCache.get(uri);
}
@@ -115,21 +106,13 @@
int port = req.getLocalPort();
String realUrl = "http://" + host + ":" + port + uri;
debug("Fetching: " + realUrl);
- int bytes = 0;
- byte[] data = new byte[32768];
- try {
- URL url = new URL(realUrl);
- InputStream is = url.openStream();
- bytes = is.read(data);
- is.close();
- } catch (IOException e) {
- debug("IOException fetching real data: " + e);
- throw e;
- }
+ URL url = new URL(realUrl);
+ InputStream is = url.openStream();
+ String data = Util.readStreamAsString(is);
+ is.close();
- RealContents rc = new RealContents(bytes, data);
- realContentsCache.put(uri, rc);
- return rc;
+ realContentsCache.put(uri, data);
+ return data;
}
}