Refactor LoadingStrategy for xsiframe linker and rename it to ScriptLoadingStrategy since it is not inherently tied to the xsiframe linker.
Bring it more in line with the XhrLoadingStrategy and give them API compatibility so the xsiframe linker could use either one.
Add features, such as retrying the download if it fails (like the XhrLoadingStrategy has)
Lay the groundwork for adding prefetching, although this will need some changes higher up in the runAsynch framework to finish
Review at http://gwt-code-reviews.appspot.com/1468802
Review by: zundel@google.com
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@10383 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/SelectionScriptLinker.java b/dev/core/src/com/google/gwt/core/ext/linker/impl/SelectionScriptLinker.java
index 47bc2ec..e9bff73 100644
--- a/dev/core/src/com/google/gwt/core/ext/linker/impl/SelectionScriptLinker.java
+++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/SelectionScriptLinker.java
@@ -22,6 +22,7 @@
import com.google.gwt.core.ext.linker.Artifact;
import com.google.gwt.core.ext.linker.ArtifactSet;
import com.google.gwt.core.ext.linker.CompilationResult;
+import com.google.gwt.core.ext.linker.ConfigurationProperty;
import com.google.gwt.core.ext.linker.EmittedArtifact;
import com.google.gwt.core.ext.linker.SelectionProperty;
import com.google.gwt.core.ext.linker.SoftPermutation;
@@ -72,12 +73,18 @@
* Utility class to handle insertion of permutations code.
*/
protected static PermutationsUtil permutationsUtil = new PermutationsUtil();
-
+
/**
* File name for processMetas.js.
*/
protected static final String PROCESS_METAS_JS = "com/google/gwt/core/ext/linker/impl/processMetasOld.js";
+ /**
+ * A configuration property that can be used to have the linker load from
+ * somewhere other than {@link #FRAGMENT_SUBDIR}.
+ */
+ private static final String PROP_FRAGMENT_SUBDIR_OVERRIDE = "iframe.linker.deferredjs.subdir";
+
protected static void replaceAll(StringBuffer buf, String search,
String replace) {
int len = search.length();
@@ -266,6 +273,30 @@
protected abstract String getCompilationExtension(TreeLogger logger,
LinkerContext context) throws UnableToCompleteException;
+ /**
+ * Returns the subdirectory name to be used by getModulPrefix when requesting
+ * a runAsync module. It is specified by
+ * {@link #PROP_FRAGMENT_SUBDIR_OVERRIDE} and, aside from test cases, is
+ * always {@link #FRAGMENT_SUBDIR}.
+ */
+ protected final String getFragmentSubdir(TreeLogger logger,
+ LinkerContext context) throws UnableToCompleteException {
+ String subdir = null;
+ for (ConfigurationProperty prop : context.getConfigurationProperties()) {
+ if (prop.getName().equals(PROP_FRAGMENT_SUBDIR_OVERRIDE)) {
+ subdir = prop.getValues().get(0);
+ }
+ }
+
+ if (subdir == null) {
+ logger.log(TreeLogger.ERROR, "Could not find property "
+ + PROP_FRAGMENT_SUBDIR_OVERRIDE);
+ throw new UnableToCompleteException();
+ }
+
+ return subdir;
+ }
+
protected String getHostedFilename() {
return "";
}
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/installScriptAlreadyIncluded.js b/dev/core/src/com/google/gwt/core/ext/linker/impl/installScriptAlreadyIncluded.js
index 0e3d90f..3cd3a65 100644
--- a/dev/core/src/com/google/gwt/core/ext/linker/impl/installScriptAlreadyIncluded.js
+++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/installScriptAlreadyIncluded.js
@@ -4,9 +4,6 @@
// we do here is set up that function, which will install the contents in
// a script tag appended to the install location.
function installScript(filename) {
- // Provides the getInstallLocation() and getInstallLocationDoc() functions
- __INSTALL_LOCATION__
-
// Provides the setupWaitForBodyLoad() function
__WAIT_FOR_BODY_LOADED__
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/installScriptDirect.js b/dev/core/src/com/google/gwt/core/ext/linker/impl/installScriptDirect.js
index 9e53fe1..81a40fb 100644
--- a/dev/core/src/com/google/gwt/core/ext/linker/impl/installScriptDirect.js
+++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/installScriptDirect.js
@@ -1,9 +1,6 @@
// Installs the script directly, by simply appending a script tag with the
// src set to the correct location to the install location.
function installScript(filename) {
- // Provides the getInstallLocation() and getInstallLocationDoc() functions
- __INSTALL_LOCATION__
-
// Provides the setupWaitForBodyLoad()function
__WAIT_FOR_BODY_LOADED__
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/installScriptEarlyDownload.js b/dev/core/src/com/google/gwt/core/ext/linker/impl/installScriptEarlyDownload.js
index 19587e9..1ee7dca 100644
--- a/dev/core/src/com/google/gwt/core/ext/linker/impl/installScriptEarlyDownload.js
+++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/installScriptEarlyDownload.js
@@ -3,9 +3,6 @@
// installed into a script tag which is added to the install location (because
// the script contents will be wrapped in a call to onScriptDownloaded()).
function installScript(filename) {
- // Provides the getInstallLocation() and getInstallLocationDoc() functions
- __INSTALL_LOCATION__
-
// Provides the setupWaitForBodyLoad() function
__WAIT_FOR_BODY_LOADED__
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/runAsync.js b/dev/core/src/com/google/gwt/core/ext/linker/impl/runAsync.js
new file mode 100644
index 0000000..c2a2089
--- /dev/null
+++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/runAsync.js
@@ -0,0 +1,17 @@
+__MODULE_FUNC__.__startLoadingFragment = function(fragmentFile) {
+ return computeUrlForResource(fragmentFile);
+};
+
+__MODULE_FUNC__.__installRunAsyncCode = function(code) {
+ var docbody = getInstallLocation();
+ var script = getInstallLocationDoc().createElement('script');
+ script.language='javascript';
+ script.text = code;
+ docbody.appendChild(script);
+
+ // Unless we're in pretty mode, remove the tags to shrink the DOM a little.
+ // It should have installed its code immediately after being added.
+ __START_OBFUSCATED_ONLY__
+ docbody.removeChild(script);
+ __END_OBFUSCATED_ONLY__
+}
diff --git a/dev/core/src/com/google/gwt/core/linker/CrossSiteIframeLinker.java b/dev/core/src/com/google/gwt/core/linker/CrossSiteIframeLinker.java
index a2615b0..135b708 100644
--- a/dev/core/src/com/google/gwt/core/linker/CrossSiteIframeLinker.java
+++ b/dev/core/src/com/google/gwt/core/linker/CrossSiteIframeLinker.java
@@ -57,6 +57,22 @@
private static final String FAIL_IF_SCRIPT_TAG_PROPERTY = "xsiframe.failIfScriptTag";
@Override
+ protected String generateDeferredFragment(TreeLogger logger,
+ LinkerContext context, int fragment, String js) {
+ // TODO(unnurg): This assumes that the xsiframe linker is using the
+ // ScriptTagLoadingStrategy (since it is also xs compatible). However,
+ // it should be completely valid to use the XhrLoadingStrategy with this
+ // linker, in which case we would not want to wrap the deferred fragment
+ // in this way. Ideally, we should make a way for this code to be dependent
+ // on what strategy is being used. Otherwise, we should make a property which
+ // users can set to turn this wrapping off if they override the loading strategy.
+ return String.format("$wnd.%s.runAsyncCallback%d(%s)\n",
+ context.getModuleFunctionName(),
+ fragment,
+ JsToStringGenerationVisitor.javaScriptString(js));
+ }
+
+ @Override
public String getDescription() {
return "Cross-Site-Iframe";
}
@@ -74,21 +90,23 @@
replaceAll(ss, "__DOCUMENT_DEF__", "document");
}
- // Must do installScript before installLocation and waitForBodyLoaded
+ // Must do installScript before waitForBodyLoaded and we must do
+ // waitForBodyLoaded before isBodyLoaded
includeJs(ss, logger, getJsInstallScript(context), "__INSTALL_SCRIPT__");
- includeJs(ss, logger, getJsInstallLocation(context), "__INSTALL_LOCATION__");
-
- // Must do waitForBodyLoaded before isBodyLoaded
includeJs(ss, logger, getJsWaitForBodyLoaded(context), "__WAIT_FOR_BODY_LOADED__");
includeJs(ss, logger, getJsIsBodyLoaded(context), "__IS_BODY_LOADED__");
// Must do permutations before providers
includeJs(ss, logger, getJsPermutations(context), "__PERMUTATIONS__");
includeJs(ss, logger, getJsProperties(context), "__PROPERTIES__");
+
+ // Order doesn't matter for the rest
includeJs(ss, logger, getJsProcessMetas(context), "__PROCESS_METAS__");
+ includeJs(ss, logger, getJsInstallLocation(context), "__INSTALL_LOCATION__");
includeJs(ss, logger, getJsComputeScriptBase(context), "__COMPUTE_SCRIPT_BASE__");
includeJs(ss, logger, getJsComputeUrlForResource(context), "__COMPUTE_URL_FOR_RESOURCE__");
includeJs(ss, logger, getJsLoadExternalStylesheets(context), "__LOAD_STYLESHEETS__");
+ includeJs(ss, logger, getJsRunAsync(context), "__RUN_ASYNC__");
// This Linker does not support <script> tags in the gwt.xml
SortedSet<ScriptReference> scripts = artifacts.find(ScriptReference.class);
@@ -281,6 +299,16 @@
protected String getJsProperties(LinkerContext context) {
return "com/google/gwt/core/ext/linker/impl/properties.js";
}
+
+ /**
+ * Returns the name of the {@code JsRunAsync} script. By default,
+ * returns {@code "com/google/gwt/core/ext/linker/impl/runAsync.js"}.
+ *
+ * @param context a LinkerContext
+ */
+ protected String getJsRunAsync(LinkerContext context) {
+ return "com/google/gwt/core/ext/linker/impl/runAsync.js";
+ }
/**
* Returns the name of the {@code JsWaitForBodyLoaded} script. By default,
@@ -293,7 +321,8 @@
}
@Override
- protected String getModulePrefix(TreeLogger logger, LinkerContext context, String strongName) {
+ protected String getModulePrefix(TreeLogger logger, LinkerContext context, String strongName)
+ throws UnableToCompleteException {
TextOutput out = new DefaultTextOutput(context.isOutputCompact());
// We assume that the $wnd has been set in the same scope as this code is
@@ -314,6 +343,22 @@
out.newlineOpt();
out.print("var $doc = $wnd.document;");
+ // The functions for runAsync are set up in the bootstrap script so they
+ // can be overriden in the same way as other bootstrap code is, however
+ // they will be called from, and expected to run in the scope of the GWT code
+ // (usually an iframe) so, here we set up those pointers.
+ out.print("function __gwtStartLoadingFragment(frag) {");
+ out.newlineOpt();
+ String fragDir = getFragmentSubdir(logger, context) + '/';
+ out.print("var fragFile = '" + fragDir + "' + $strongName + '/' + frag + '" + FRAGMENT_EXTENSION + "';");
+ out.newlineOpt();
+ out.print("return __gwtModuleFunction.__startLoadingFragment(fragFile);");
+ out.newlineOpt();
+ out.print("}");
+ out.newlineOpt();
+ out.print("function __gwtInstallCode(code) {return __gwtModuleFunction.__installRunAsyncCode(code);}");
+ out.newlineOpt();
+
// Even though we call the $sendStats function in the code written in this
// linker, some of the compilation code still needs the $stats and
// $sessionId
diff --git a/dev/core/src/com/google/gwt/core/linker/CrossSiteIframeTemplate.js b/dev/core/src/com/google/gwt/core/linker/CrossSiteIframeTemplate.js
index bb8a364..5798f0f 100644
--- a/dev/core/src/com/google/gwt/core/linker/CrossSiteIframeTemplate.js
+++ b/dev/core/src/com/google/gwt/core/linker/CrossSiteIframeTemplate.js
@@ -63,6 +63,10 @@
// Exposed for devmode.js
__MODULE_FUNC__.__computePropValue = null;
+
+ // Exposed for runAsync
+ __MODULE_FUNC__.__gwtInstallCode = function() {};
+ __MODULE_FUNC__.__gwtStartLoadingFragment = function() { return null; };
// Exposed for property provider code
var __gwt_isKnownPropertyValue = function() { return false; };
@@ -77,9 +81,16 @@
* files for readability and for easy sharing between linkers. The linker
* code will inject these functions in these placeholders.
***************************************************************************/
+ // Provides the getInstallLocation() and getInstallLocationDoc() functions
+ __INSTALL_LOCATION__
+
// Provides the installScript() function.
__INSTALL_SCRIPT__
+ // Sets the __MODULE_FUNC__.__installRunAsyncCode and
+ // __MODULE_FUNC__.__startLoadingFragment functions
+ __RUN_ASYNC__
+
// Provides the processMetas() function which sets the __gwt_getMetaProperty
// __propertyErrorFunction and __MODULE_FUNC__.__errFn variables if needed
__PROCESS_METAS__
diff --git a/dev/core/src/com/google/gwt/core/linker/IFrameLinker.java b/dev/core/src/com/google/gwt/core/linker/IFrameLinker.java
index 33f3e69..4779f65 100644
--- a/dev/core/src/com/google/gwt/core/linker/IFrameLinker.java
+++ b/dev/core/src/com/google/gwt/core/linker/IFrameLinker.java
@@ -51,12 +51,6 @@
private static final String CHUNK_SIZE_PROPERTY = "iframe.linker.script.chunk.size";
/**
- * A configuration property that can be used to have the linker load from
- * somewhere other than {@link #FRAGMENT_SUBDIR}.
- */
- private static final String PROP_FRAGMENT_SUBDIR_OVERRIDE = "iframe.linker.deferredjs.subdir";
-
- /**
* Split a JavaScript string into multiple chunks, at statement boundaries.
* Insert and end-script tag and a start-script tag in between each chunk.
* This method is made default access for testing.
@@ -137,30 +131,6 @@
return ".cache.html";
}
- /**
- * Returns the subdirectory name to be used by getModulPrefix when requesting
- * a runAsync module. It is specified by
- * {@link #PROP_FRAGMENT_SUBDIR_OVERRIDE} and, aside from test cases, is
- * always {@link #FRAGMENT_SUBDIR}.
- */
- protected final String getFragmentSubdir(TreeLogger logger,
- LinkerContext context) throws UnableToCompleteException {
- String subdir = null;
- for (ConfigurationProperty prop : context.getConfigurationProperties()) {
- if (prop.getName().equals(PROP_FRAGMENT_SUBDIR_OVERRIDE)) {
- subdir = prop.getValues().get(0);
- }
- }
-
- if (subdir == null) {
- logger.log(TreeLogger.ERROR, "Could not find property "
- + PROP_FRAGMENT_SUBDIR_OVERRIDE);
- throw new UnableToCompleteException();
- }
-
- return subdir;
- }
-
@Override
protected String getHostedFilename() {
return "hosted.html";
diff --git a/tools/api-checker/config/gwt23_24userApi.conf b/tools/api-checker/config/gwt23_24userApi.conf
index 3771fca..14359f9 100644
--- a/tools/api-checker/config/gwt23_24userApi.conf
+++ b/tools/api-checker/config/gwt23_24userApi.conf
@@ -144,6 +144,13 @@
# Overloading AttachDetachException::tryCommand to attach/detach a finite set of widgets.
com.google.gwt.user.client.ui.AttachDetachException::tryCommand(Ljava/lang/Iterable;Lcom/google/gwt/user/client/ui/AttachDetachException$Command;) OVERLOADED_METHOD_CALL
+# Refactoring Loading Strategies
+com.google.gwt.core.client.impl.CrossSiteIframeLoadingStrategy MISSING
+com.google.gwt.core.client.impl.XhrLoadingStrategy.RequestData MISSING
+com.google.gwt.core.client.impl.XhrLoadingStrategy::createXhr() MISSING
+com.google.gwt.core.client.impl.XhrLoadingStrategy::onLoadError(Lcom/google/gwt/core/client/impl/XhrLoadingStrategy$RequestData;Ljava/lang/Throwable;Z) MISSING
+com.google.gwt.core.client.impl.XhrLoadingStrategy::tryLoad(Lcom/google/gwt/core/client/impl/XhrLoadingStrategy$RequestData;) MISSING
+
# Overloading AbstractHasData constructor to allow a widget or element as the root.
com.google.gwt.user.cellview.client.AbstractHasData::AbstractHasData(Lcom/google/gwt/dom/client/Element;ILcom/google/gwt/view/client/ProvidesKey;) OVERLOADED_METHOD_CALL
@@ -160,3 +167,4 @@
com.google.gwt.user.client.ui.Image::prefetch(Ljava/lang/String;) OVERLOADED_METHOD_CALL
com.google.gwt.user.client.ui.Image::setUrl(Ljava/lang/String;) OVERLOADED_METHOD_CALL
com.google.gwt.user.client.ui.Image::setUrlAndVisibleRect(Ljava/lang/String;IIII) OVERLOADED_METHOD_CALL
+
diff --git a/user/src/com/google/gwt/core/CrossSiteIframeLinker.gwt.xml b/user/src/com/google/gwt/core/CrossSiteIframeLinker.gwt.xml
index 3679d19..fec253d 100644
--- a/user/src/com/google/gwt/core/CrossSiteIframeLinker.gwt.xml
+++ b/user/src/com/google/gwt/core/CrossSiteIframeLinker.gwt.xml
@@ -19,7 +19,7 @@
<define-configuration-property name="xsiframe.failIfScriptTag" is_multi_valued="FALSE"/>
<set-configuration-property name="xsiframe.failIfScriptTag" value="TRUE"/>
- <replace-with class="com.google.gwt.core.client.impl.CrossSiteIframeLoadingStrategy">
+ <replace-with class="com.google.gwt.core.client.impl.ScriptTagLoadingStrategy">
<when-type-is
class="com.google.gwt.core.client.impl.AsyncFragmentLoader.LoadingStrategy" />
<when-linker-added name="xsiframe" />
diff --git a/user/src/com/google/gwt/core/client/impl/AsyncFragmentLoader.java b/user/src/com/google/gwt/core/client/impl/AsyncFragmentLoader.java
index 8e315b6..0b89cf9 100644
--- a/user/src/com/google/gwt/core/client/impl/AsyncFragmentLoader.java
+++ b/user/src/com/google/gwt/core/client/impl/AsyncFragmentLoader.java
@@ -37,8 +37,7 @@
*
* <p>
* Since the precise way to load code depends on the linker, linkers should
- * specify a rebind of {@link LoadingStrategy}. The default rebind is
- * {@link XhrLoadingStrategy}.
+ * specify a rebind of {@link LoadingStrategy}.
*/
public class AsyncFragmentLoader {
/**
diff --git a/user/src/com/google/gwt/core/client/impl/CrossSiteIframeLoadingStrategy.java b/user/src/com/google/gwt/core/client/impl/CrossSiteIframeLoadingStrategy.java
deleted file mode 100644
index 2cce53d..0000000
--- a/user/src/com/google/gwt/core/client/impl/CrossSiteIframeLoadingStrategy.java
+++ /dev/null
@@ -1,155 +0,0 @@
-/*
- * Copyright 2010 Google Inc.
- *
- * 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.google.gwt.core.client.impl;
-
-import com.google.gwt.core.client.GWT;
-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;
-
-/**
- * Load runAsync code using a script tag. Intended for use with the
- * {@link com.google.gwt.core.linker.CrossSiteIframeLinker}.
- */
-public class CrossSiteIframeLoadingStrategy implements LoadingStrategy {
- /**
- * A trivial JavaScript map from ints to ints.
- */
- private static final class IntToIntMap extends JavaScriptObject {
- public static IntToIntMap create() {
- return (IntToIntMap) JavaScriptObject.createArray();
- }
-
- protected IntToIntMap() {
- }
-
- /**
- * Get an entry. If there is no such entry, return 0.
- */
- public native int get(int x) /*-{
- return this[x] ? this[x] : 0;
- }-*/;
-
- public native void put(int x, int y) /*-{
- this[x] = y;
- }-*/;
- }
-
- /**
- * Clear callbacks on script objects. This is important on IE 6 and 7 to
- * prevent a memory leak. If the callbacks aren't cleared, there is a cyclical
- * chain of references between the script tag and the function callback, and
- * IE 6/7 can't garbage collect them.
- */
- private static native void clearCallbacks(JavaScriptObject script) /*-{
- var nop = new Function('');
- script.onerror = script.onload = script.onreadystatechange = nop;
- }-*/;
-
- private static native JavaScriptObject createScriptTag(String url) /*-{
- var head = document.getElementsByTagName('head').item(0);
- var script = document.createElement('script');
- script.src = url;
- return script;
- }-*/;
-
- private static native void installScriptTag(JavaScriptObject script) /*-{
- var head = document.getElementsByTagName('head').item(0);
- head.appendChild(script);
- }-*/;
-
- private static native JavaScriptObject removeTagAndCallErrorHandler(
- int fragment, JavaScriptObject tag,
- LoadTerminatedHandler loadFinishedHandler) /*-{
- return function(exception) {
- if (tag.parentNode == null) {
- // This function must have already been called.
- return;
- }
- var head = document.getElementsByTagName('head').item(0);
- @com.google.gwt.core.client.impl.CrossSiteIframeLoadingStrategy::clearCallbacks(*)(tag);
- head.removeChild(tag);
- // It seems unintuitive to call the error function every time, but
- // it appears that AsyncFragmentLoader::fragmentHasLoaded (which is
- // called by each fragment) will set the fragmentLoading variable to
- // -1 when the code in this fragment executes, so this
- // loadTerminated call will fail the (fragmentLoading == fragment) check
- // and will immediately exit, so no errors are actually fired.
- function callLoadTerminated() {
- loadFinishedHandler.@com.google.gwt.core.client.impl.AsyncFragmentLoader.LoadTerminatedHandler::loadTerminated(*)(exception);
- }
- $entry(callLoadTerminated)();
- }
- }-*/;
-
- private static native void setOnTerminated(JavaScriptObject script,
- JavaScriptObject callback) /*-{
- script.onerror = function() {
- var error = "Code download terminated, onerror called. Script src = " + script.src;
- callback(@com.google.gwt.core.client.CodeDownloadException::new(Ljava/lang/String;)(error));
- }
- script.onload = function() {
- var error = "Code download terminated, onload called. Script src = " + script.src;
- callback(@com.google.gwt.core.client.CodeDownloadException::new(Ljava/lang/String;)(error));
- }
- script.onreadystatechange = function () {
- if (script.readyState == 'loaded') {
- var error = "Code download terminated, readystate is loaded. Script src = " + script.src;
- callback(@com.google.gwt.core.client.CodeDownloadException::new(Ljava/lang/String;)(error));
- }
- if (script.readyState == 'complete') {
- var error = "Code download terminated, readystate is complete. Script src = " + script.src;
- callback(@com.google.gwt.core.client.CodeDownloadException::new(Ljava/lang/String;)(error));
- }
- }
- }-*/;
-
- private final IntToIntMap serialNumbers = IntToIntMap.create();
-
- public void startLoadingFragment(int fragment,
- LoadTerminatedHandler loadFinishedHandler) {
- JavaScriptObject tag = createScriptTag(getUrl(fragment));
- setOnTerminated(tag, removeTagAndCallErrorHandler(fragment, tag,
- loadFinishedHandler));
- installScriptTag(tag);
- }
-
- protected String getDeferredJavaScriptDirectory() {
- return "deferredjs/";
- }
-
- private int getSerial(int fragment) {
- int ser = serialNumbers.get(fragment);
- serialNumbers.put(fragment, ser + 1);
- return ser;
- }
-
- /**
- * The URL to retrieve a fragment of code from. NOTE: this function is not
- * stable. It tweaks the URL with each call so that browsers are not tempted
- * to cache a download failure.
- */
- private String getUrl(int fragment) {
- // Not appending serial=N to the first attempt improves proxy caching
- // and makes it easier to write HTML5 AppCache manifests.
- int serial = getSerial(fragment);
- String parameters = ((serial == 0) ? "" : ("?serial=" + serial));
- return GWT.getModuleBaseURL() + getDeferredJavaScriptDirectory()
- + GWT.getPermutationStrongName() + "/" + fragment + ".cache.js"
- + parameters;
- }
-}
diff --git a/user/src/com/google/gwt/core/client/impl/LoadingStrategyBase.java b/user/src/com/google/gwt/core/client/impl/LoadingStrategyBase.java
new file mode 100644
index 0000000..2cf9495
--- /dev/null
+++ b/user/src/com/google/gwt/core/client/impl/LoadingStrategyBase.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * 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.google.gwt.core.client.impl;
+
+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;
+
+/**
+ * Base for a standard loading strategy used in a web browser. Subclasses
+ * provide an implementation of DownloadStrategy, thereby controlling how
+ * the download of the code will be done, while the base class controls how
+ * to interact with the linker, handle download failures, etc.
+ *
+ * The linker it is used with should provide JavaScript-level functions to
+ * indicate how to handle downloading and installing code.
+ *
+ * Linkers should always provide a function
+ * <code>__gwtStartLoadingFragment</code>. This function is called by
+ * this class with two arguments: an integer fragment number that needs
+ * to be downloaded, and a one-argument loadFinished function. If the load
+ * fails, that function should be called with a descriptive exception as the
+ * argument. If the load succeeds, that function may also be called, so long as
+ * it isn't called until the downloaded code has been installed.
+ *
+ * If the mechanism for loading the contents of fragments is provided by the
+ * linker, the <code>__gwtStartLoadingFragment</code> function should return
+ * <code>null</code> or <code>undefined</code>, and the linker should handle
+ * installing the code as well. Note that in this case, all the code in this
+ * class is pretty much moot.
+ *
+ * Alternatively, the function can return a URL designating from where the code
+ * for the requested fragment can be downloaded. In that case, the linker should
+ * also provide a function <code>__gwtInstallCode</code> for actually installing
+ * the code once it is downloaded. That function will be passed the loaded code
+ * once it has been downloaded.
+ */
+public class LoadingStrategyBase implements LoadingStrategy {
+ /**
+ * Subclasses will need to implement this and pass it in in the constructor.
+ * This is how they control how the download will be done (XHR, Script tag, etc.)
+ * If the download succeeds, the DownloadStrategy should call it's
+ * RequestData's tryInstall() function, and if it fails, it should call it's
+ * RequestData's onLoadError() function.
+ */
+ interface DownloadStrategy {
+ void tryDownload(final RequestData request);
+ }
+
+ /**
+ * Since LoadingStrategy must support concurrent requests, we keep most of the
+ * relevant info in the RequestData, and pass it around. Once created, a
+ * RequestData interacts primarily with it's DownloadStrategy, which will
+ * attempt call out to the RequestData's tryInstall function if the download
+ * succeeds, or it's onLoadError if the download fails.
+ */
+ protected static class RequestData {
+ private static final int MAX_LOG_LENGTH = 200;
+
+ private DownloadStrategy downloadStrategy;
+ private LoadTerminatedHandler errorHandler = null;
+ private int fragment;
+ private int maxRetryCount;
+ private String originalUrl;
+ private int retryCount;
+ private String url;
+
+ public RequestData(String url, LoadTerminatedHandler errorHandler,
+ int fragment, DownloadStrategy downloadStrategy, int maxRetryCount) {
+ this.url = url;
+ this.originalUrl = url;
+ this.errorHandler = errorHandler;
+ this.maxRetryCount = maxRetryCount;
+ this.retryCount = 0;
+ this.fragment = fragment;
+ this.downloadStrategy = downloadStrategy;
+ }
+
+ public int getFragment() { return fragment; }
+
+ public String getUrl() { return url; }
+
+ public void onLoadError(Throwable e, boolean mayRetry) {
+ if (mayRetry) {
+ retryCount++;
+ if (retryCount <= maxRetryCount) {
+ url = originalUrl + "?serial=" + retryCount;
+ downloadStrategy.tryDownload(this);
+ return;
+ }
+ }
+ errorHandler.loadTerminated(e);
+ }
+
+ public void tryDownload() {
+ downloadStrategy.tryDownload(this);
+ }
+
+ public void tryInstall(String code) {
+ try {
+ gwtInstallCode(code);
+ } catch (RuntimeException e) {
+ String textIntro = code;
+ if (textIntro != null && textIntro.length() > MAX_LOG_LENGTH) {
+ textIntro = textIntro.substring(0, MAX_LOG_LENGTH) + "...";
+ }
+ onLoadError(new HttpInstallFailure(url, textIntro, e), false);
+ }
+ }
+ }
+
+ /**
+ * 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;
+
+ /**
+ * Call the linker-supplied <code>__gwtInstallCode</code> method. This method
+ * will attempt to install the code, and throw a runtime exception if it fails,
+ * which will get caught by the RequestData.tryInstall() function.
+ */
+ protected static native void gwtInstallCode(String text) /*-{
+ __gwtInstallCode(text);
+ }-*/;
+
+ /**
+ * Call the linker-supplied __gwtStartLoadingFragment function. It should
+ * either start the download and return null or undefined, or it should
+ * return a URL that should be downloaded to get the code. If it starts the
+ * download itself, it can synchronously load it, e.g. from cache, if that
+ * makes sense.
+ */
+ protected static native String gwtStartLoadingFragment(int fragment,
+ LoadTerminatedHandler loadErrorHandler) /*-{
+ function loadFailed(e) {
+ loadErrorHandler.@com.google.gwt.core.client.impl.AsyncFragmentLoader$LoadTerminatedHandler::loadTerminated(Ljava/lang/Throwable;)(e);
+ }
+ return __gwtStartLoadingFragment(fragment, $entry(loadFailed));
+ }-*/;
+
+ private DownloadStrategy downloadStrategy;
+
+ /**
+ * Subclasses should create a DownloadStrategy and pass it into this constructor.
+ */
+ public LoadingStrategyBase(DownloadStrategy downloadStrategy) {
+ this.downloadStrategy = downloadStrategy;
+ }
+
+ protected int getMaxRetryCount() { return MAX_RETRY_COUNT; }
+
+ @Override
+ public void startLoadingFragment(int fragment,
+ final LoadTerminatedHandler loadErrorHandler) {
+ String url = gwtStartLoadingFragment(fragment, loadErrorHandler);
+ if (url == null) {
+ // The linker is going to handle this fetch - nothing more to do
+ return;
+ }
+ RequestData request = new RequestData(url, loadErrorHandler,
+ fragment, downloadStrategy, getMaxRetryCount());
+ request.tryDownload();
+ }
+}
diff --git a/user/src/com/google/gwt/core/client/impl/ScriptTagLoadingStrategy.java b/user/src/com/google/gwt/core/client/impl/ScriptTagLoadingStrategy.java
new file mode 100644
index 0000000..6b2b97e
--- /dev/null
+++ b/user/src/com/google/gwt/core/client/impl/ScriptTagLoadingStrategy.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * 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.google.gwt.core.client.impl;
+
+import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.core.client.impl.AsyncFragmentLoader.HttpDownloadFailure;
+
+/**
+ * A download strategy that uses a JSONP style script tag mechanism. and is
+ * therefore cross site compatible. Note that if this strategy is used, the
+ * deferred fragments must be wrapped in a callback called runAsyncCallbackX()
+ * where X is the fragment number.
+ *
+ * This is the default strategy for the CrossSiteIframeLinker.
+ *
+ * TODO(unnurg): Try to use the ScriptInjector here
+ */
+
+public class ScriptTagLoadingStrategy extends LoadingStrategyBase {
+
+ /**
+ * Uses a JSONP style script tag mechanism to download the code.
+ */
+ protected static class ScriptTagDownloadStrategy implements DownloadStrategy {
+ @Override
+ public void tryDownload(final RequestData request) {
+ int fragment = request.getFragment();
+ JavaScriptObject scriptTag = createScriptTag(request.getUrl());
+ setOnSuccess(fragment, onSuccess(fragment, scriptTag, request));
+ setOnFailure(scriptTag, onFailure(fragment, scriptTag, request));
+ installScriptTag(scriptTag);
+ }
+ }
+
+ protected static void callOnLoadError(RequestData request) {
+ request.onLoadError(new HttpDownloadFailure(request.getUrl(), 404,
+ "Script Tag Failure - no status available"), true);
+ }
+
+ private static native boolean clearCallbacksAndRemoveTag(
+ int fragment, JavaScriptObject scriptTag) /*-{
+ if (scriptTag.parentNode == null) {
+ // onSuccess or onFailure must have already been called.
+ return false;
+ }
+ var head = document.getElementsByTagName('head').item(0);
+ @com.google.gwt.core.client.impl.ScriptTagLoadingStrategy::clearOnSuccess(I)(fragment);
+ @com.google.gwt.core.client.impl.ScriptTagLoadingStrategy::clearOnFailure(Lcom/google/gwt/core/client/JavaScriptObject;)(
+ scriptTag);
+ head.removeChild(scriptTag);
+ return true;
+ }-*/;
+
+ private static native void clearOnFailure(JavaScriptObject scriptTag) /*-{
+ scriptTag.onerror = scriptTag.onload = scriptTag.onreadystatechange = function(){};
+ }-*/;
+
+ private static native void clearOnSuccess(int fragment) /*-{
+ delete __gwtModuleFunction['runAsyncCallback' + fragment];
+ }-*/;
+
+ private static native JavaScriptObject createScriptTag(String url) /*-{
+ var head = document.getElementsByTagName('head').item(0);
+ var scriptTag = document.createElement('script');
+ scriptTag.src = url;
+ return scriptTag;
+ }-*/;
+
+ private static native void installScriptTag(JavaScriptObject scriptTag) /*-{
+ var head = document.getElementsByTagName('head').item(0);
+ head.appendChild(scriptTag);
+ }-*/;
+
+ private static native JavaScriptObject onFailure(
+ int fragment, JavaScriptObject scriptTag, RequestData request) /*-{
+ return function() {
+ if (@com.google.gwt.core.client.impl.ScriptTagLoadingStrategy::clearCallbacksAndRemoveTag(ILcom/google/gwt/core/client/JavaScriptObject;)(
+ fragment, scriptTag)) {
+ @com.google.gwt.core.client.impl.ScriptTagLoadingStrategy::callOnLoadError(Lcom/google/gwt/core/client/impl/LoadingStrategyBase$RequestData;)(
+ request)
+ }
+ }
+ }-*/;
+
+ private static native JavaScriptObject onSuccess(int fragment,
+ JavaScriptObject scriptTag, RequestData request) /*-{
+ return function(code, instance) {
+ if (@com.google.gwt.core.client.impl.ScriptTagLoadingStrategy::clearCallbacksAndRemoveTag(ILcom/google/gwt/core/client/JavaScriptObject;)(
+ fragment, scriptTag)) {
+ request.@com.google.gwt.core.client.impl.LoadingStrategyBase.RequestData::tryInstall(Ljava/lang/String;)(
+ code);
+ }
+ }
+ }-*/;
+
+ private static native void setOnFailure(JavaScriptObject script,
+ JavaScriptObject callback) /*-{
+ script.onerror = function() {
+ callback();
+ }
+ script.onload = function() {
+ callback();
+ }
+ script.onreadystatechange = function () {
+ if (script.readyState == 'loaded' || script.readyState == 'complete') {
+ script.onreadystatechange = function () { }
+ callback();
+ }
+ }
+ }-*/;
+
+ private static native void setOnSuccess(int fragment, JavaScriptObject callback) /*-{
+ __gwtModuleFunction['runAsyncCallback'+fragment] = callback;
+ }-*/;
+
+ public ScriptTagLoadingStrategy() {
+ super(new ScriptTagDownloadStrategy());
+ }
+}
diff --git a/user/src/com/google/gwt/core/client/impl/XhrLoadingStrategy.java b/user/src/com/google/gwt/core/client/impl/XhrLoadingStrategy.java
index 84abb3a..1ced92f 100644
--- a/user/src/com/google/gwt/core/client/impl/XhrLoadingStrategy.java
+++ b/user/src/com/google/gwt/core/client/impl/XhrLoadingStrategy.java
@@ -16,122 +16,45 @@
package com.google.gwt.core.client.impl;
import com.google.gwt.core.client.impl.AsyncFragmentLoader.HttpDownloadFailure;
-import com.google.gwt.core.client.impl.AsyncFragmentLoader.HttpInstallFailure;
-import com.google.gwt.core.client.impl.AsyncFragmentLoader.LoadTerminatedHandler;
-import com.google.gwt.core.client.impl.AsyncFragmentLoader.LoadingStrategy;
import com.google.gwt.xhr.client.ReadyStateChangeHandler;
import com.google.gwt.xhr.client.XMLHttpRequest;
/**
- * The standard loading strategy used in a web browser. The linker it is used
- * with should provide JavaScript-level functions to indicate how to handle
- * downloading and installing code. There is support to use XHR for the
- * download.
+ * A download strategy that uses XHRs and iss therefore not cross site compatible.
*
- * Linkers should always provide a function
- * <code>__gwtStartLoadingFragment</code>. This function is called by
- * AsyncFragmentLoader with two arguments: an integer fragment number that needs
- * to be downloaded, and a one-argument loadFinished function. If the load
- * fails, that function should be called with a descriptive exception as the
- * argument. If the load succeeds, that function may also be called, so long as
- * it isn't called until the downloaded code has been installed.
- *
- *
- * If the mechanism for loading the contents of fragments is provided by the
- * linker, the <code>__gwtStartLoadingFragment</code> function should return
- * <code>null</code> or <code>undefined</code>.
- *
- * Alternatively, the function can return a URL designating from where the code
- * for the requested fragment can be downloaded. In that case, the linker should
- * also provide a function <code>__gwtInstallCode</code> for actually installing
- * the code once it is downloaded. That function will be passed the loaded code
- * once it has been downloaded.
+ * This is the default strategy for the IframeLinker.
*/
-public class XhrLoadingStrategy implements LoadingStrategy {
-
- /**
- * A {@link MockableXMLHttpRequest} that is really just a vanilla
- * XMLHttpRequest. This wrapper (and thus {@code MockableXMLHttpRequest} is
- * needed because so much of {@link XMLHttpRequest} is final, which in turn
- * is because it extends {@code JavaScriptObject} and is subject to its
- * restrictions.
- *
- * It is important that these methods be simple enough to be inlined away.
- */
- static class DelegatingXMLHttpRequest implements MockableXMLHttpRequest {
- private final XMLHttpRequest delegate;
-
- public DelegatingXMLHttpRequest(XMLHttpRequest xmlHttpRequest) {
- delegate = xmlHttpRequest;
- }
-
- public void clearOnReadyStateChange() {
- delegate.clearOnReadyStateChange();
- }
-
- public int getReadyState() {
- return delegate.getReadyState();
- }
-
- public String getResponseText() {
- return delegate.getResponseText();
- }
-
- public int getStatus() {
- return delegate.getStatus();
- }
-
- public String getStatusText() {
- return delegate.getStatusText();
- }
-
- public void open(String method, String url) {
- delegate.open(method, url);
- }
-
- public void send() {
- delegate.send();
- }
-
- public void setOnReadyStateChange(ReadyStateChangeHandler handler) {
- delegate.setOnReadyStateChange(handler);
- }
-
- public void setRequestHeader(String header, String value) {
- delegate.setRequestHeader(header, value);
- }
- }
+public class XhrLoadingStrategy extends LoadingStrategyBase {
/**
- * Delegates to the real XMLHttpRequest, except in test when we make a mock
- * to jump through error/retry hoops.
+ * Uses XHR's to download the code.
*/
- interface MockableXMLHttpRequest {
- void clearOnReadyStateChange();
- int getReadyState();
- String getResponseText();
- int getStatus();
- String getStatusText();
- void open(String method, String url);
- void send();
- void setOnReadyStateChange(ReadyStateChangeHandler handler);
- void setRequestHeader(String header, String value);
- }
+ protected static class XhrDownloadStrategy implements DownloadStrategy {
+ @Override
+ public void tryDownload(final RequestData request) {
+ final XMLHttpRequest xhr = XMLHttpRequest.create();
- /**
- * Since LoadingStrategy must support concurrent requests, including figuring
- * which is which in the onLoadError handling, we need to keep track of this
- * data for each outstanding request, which we index by xhr object.
- */
- protected static class RequestData {
- String url;
- int retryCount;
- LoadTerminatedHandler errorHandler = null;
-
- public RequestData(String url, LoadTerminatedHandler errorHandler) {
- this.url = url;
- this.errorHandler = errorHandler;
- this.retryCount = 0;
+ xhr.open(HTTP_GET, request.getUrl());
+
+ xhr.setOnReadyStateChange(new ReadyStateChangeHandler() {
+ public void onReadyStateChange(XMLHttpRequest ignored) {
+ if (xhr.getReadyState() == XMLHttpRequest.DONE) {
+ xhr.clearOnReadyStateChange();
+ if ((xhr.getStatus() == HTTP_STATUS_OK || xhr.getStatus() == HTTP_STATUS_NON_HTTP)
+ && xhr.getResponseText() != null
+ && xhr.getResponseText().length() != 0) {
+ request.tryInstall(xhr.getResponseText());
+ } else {
+ // If the download fails
+ request.onLoadError(
+ new HttpDownloadFailure(request.getUrl(), xhr.getStatus(),
+ xhr.getStatusText()), true);
+ }
+ }
+ }
+ });
+
+ xhr.send();
}
}
@@ -146,117 +69,8 @@
static final int HTTP_STATUS_OK = 200;
- /**
- * For error logging, max length of fragment response text to include in
- * failed-to-install exception message.
- */
- private static final int MAX_LOG_LENGTH = 200;
-
- /**
- * Number of retry attempts for a single fragment. If a fragment download
- * fails, we try again this many times before "really" failing out to user
- * error-handling code. If a fragment downloads but doesn't install, we
- * don't retry at all.
- */
- private static final int MAX_RETRY_COUNT = 3;
-
- public void startLoadingFragment(int fragment,
- final LoadTerminatedHandler loadErrorHandler) {
- String url = gwtStartLoadingFragment(fragment, loadErrorHandler);
- if (url == null) {
- // The download has already started; nothing more to do
- return;
- }
-
- RequestData request = new RequestData(url, loadErrorHandler);
- tryLoad(request);
- }
-
- /**
- * Overridable for tests.
- */
- protected MockableXMLHttpRequest createXhr() {
- return new DelegatingXMLHttpRequest(XMLHttpRequest.create());
+ public XhrLoadingStrategy() {
+ super(new XhrDownloadStrategy());
}
- /**
- * Call the linker-supplied <code>__gwtInstallCode</code> method. See the
- * {@link AsyncFragmentLoader class comment} for more details.
- */
- protected native void gwtInstallCode(String text) /*-{
- __gwtInstallCode(text);
- }-*/;
-
- /**
- * Call the linker-supplied __gwtStartLoadingFragment function. It should
- * either start the download and return null or undefined, or it should
- * return a URL that should be downloaded to get the code. If it starts the
- * download itself, it can synchronously load it, e.g. from cache, if that
- * makes sense.
- */
- protected native String gwtStartLoadingFragment(int fragment,
- LoadTerminatedHandler loadErrorHandler) /*-{
- function loadFailed(e) {
- loadErrorHandler.@com.google.gwt.core.client.impl.AsyncFragmentLoader$LoadTerminatedHandler::loadTerminated(*)(e);
- }
- return __gwtStartLoadingFragment(fragment, $entry(loadFailed));
- }-*/;
-
- /**
- * Error recovery from loading or installing code.
- * @param request the requestData of this request
- * @param e exception of the error
- * @param mayRetry {@code true} if retrying might be helpful
- */
- protected void onLoadError(RequestData request, Throwable e, boolean mayRetry) {
- if (mayRetry) {
- request.retryCount++;
- if (request.retryCount < MAX_RETRY_COUNT) {
- tryLoad(request);
- return;
- }
- }
- request.errorHandler.loadTerminated(e);
- }
-
- /**
- * Makes a single load-and-install attempt.
- */
- protected void tryLoad(final RequestData request) {
- final MockableXMLHttpRequest xhr = createXhr();
-
- xhr.open(HTTP_GET, request.url);
- if (request.retryCount > 0) {
- // disable caching if we have to retry; one cause could be bad cache
- xhr.setRequestHeader("Cache-Control", "no-cache");
- }
-
- xhr.setOnReadyStateChange(new ReadyStateChangeHandler() {
- public void onReadyStateChange(XMLHttpRequest ignored) {
- if (xhr.getReadyState() == XMLHttpRequest.DONE) {
- xhr.clearOnReadyStateChange();
- if ((xhr.getStatus() == HTTP_STATUS_OK || xhr.getStatus() == HTTP_STATUS_NON_HTTP)
- && xhr.getResponseText() != null
- && xhr.getResponseText().length() != 0) {
- try {
- gwtInstallCode(xhr.getResponseText());
- } catch (RuntimeException e) {
- String textIntro = xhr.getResponseText();
- if (textIntro != null && textIntro.length() > MAX_LOG_LENGTH) {
- textIntro = textIntro.substring(0, MAX_LOG_LENGTH) + "...";
- }
- onLoadError(request,
- new HttpInstallFailure(request.url, textIntro, e), false);
- }
- } else {
- onLoadError(request,
- new HttpDownloadFailure(request.url, xhr.getStatus(),
- xhr.getStatusText()), true);
- }
- }
- }
- });
-
- xhr.send();
- }
}
\ No newline at end of file
diff --git a/user/test/com/google/gwt/core/CoreSuite.java b/user/test/com/google/gwt/core/CoreSuite.java
index 72bdc4b..8f03695 100644
--- a/user/test/com/google/gwt/core/CoreSuite.java
+++ b/user/test/com/google/gwt/core/CoreSuite.java
@@ -24,7 +24,6 @@
import com.google.gwt.core.client.impl.AsyncFragmentLoaderTest;
import com.google.gwt.core.client.impl.SchedulerImplTest;
import com.google.gwt.core.client.impl.StackTraceCreatorTest;
-import com.google.gwt.core.client.impl.XhrLoadingStrategyTest;
import com.google.gwt.core.client.prefetch.RunAsyncCodeTest;
import com.google.gwt.dev.StrictModeTest;
import com.google.gwt.junit.tools.GWTTestSuite;
@@ -49,7 +48,6 @@
suite.addTestSuite(SchedulerTest.class);
suite.addTestSuite(StackTraceCreatorTest.class);
suite.addTestSuite(StrictModeTest.class);
- suite.addTestSuite(XhrLoadingStrategyTest.class);
suite.addTestSuite(RunAsyncCodeTest.class);
// $JUnit-END$
diff --git a/user/test/com/google/gwt/core/client/impl/XhrLoadingStrategyTest.java b/user/test/com/google/gwt/core/client/impl/XhrLoadingStrategyTest.java
deleted file mode 100644
index de9d2a8..0000000
--- a/user/test/com/google/gwt/core/client/impl/XhrLoadingStrategyTest.java
+++ /dev/null
@@ -1,270 +0,0 @@
-/*
- * Copyright 2009 Google Inc.
- *
- * 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.google.gwt.core.client.impl;
-
-import com.google.gwt.core.client.impl.AsyncFragmentLoader.LoadTerminatedHandler;
-import com.google.gwt.core.client.impl.XhrLoadingStrategy.MockableXMLHttpRequest;
-import com.google.gwt.xhr.client.ReadyStateChangeHandler;
-
-import junit.framework.TestCase;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-
-/**
- * Tests the default loading strategy and its retry behavior.
- */
-public class XhrLoadingStrategyTest extends TestCase {
-
- static class MockXhr implements MockableXMLHttpRequest {
- public static final String SUCCESSFUL_RESPONSE_TEXT = "successful response text";
- public static final String INSTALL_FAILED_RESPONSE_TEXT = "install failed response text";
-
- private ReadyStateChangeHandler handler;
- private int httpStatus;
- private int state;
- private String statusText;
- private String text;
- private HashMap<String, String> headers;
-
- public MockXhr(int status, String statusText, boolean loads,
- boolean installs, String... headers) {
- this.httpStatus = status;
- this.statusText = statusText;
- if (installs) {
- text = SUCCESSFUL_RESPONSE_TEXT;
- } else if (loads) {
- text = INSTALL_FAILED_RESPONSE_TEXT;
- } else {
- text = null;
- }
- handler = null;
- state = 0;
- assert headers.length % 2 == 0;
- this.headers = new HashMap<String, String>();
- for (int i = 0; i < headers.length; i += 2) {
- this.headers.put(headers[i], headers[i + 1]);
- }
- }
-
- public void clearOnReadyStateChange() {
- handler = null;
- }
-
- public int getReadyState() {
- return state;
- }
-
- public String getResponseText() {
- return state > 3 ? text : null;
- }
-
- public int getStatus() {
- return state > 1 ? httpStatus : 0;
- }
-
- public String getStatusText() {
- return state > 1 ? statusText : null;
- }
-
- public void open(String method, String url) {
- state = 1;
- }
-
- public void send() {
- state = 4;
- if (headers.size() != 0) {
- throw new IllegalStateException("not all expected headers set");
- }
- if (handler != null) {
- /*
- * This is brittle, but I don't have a better idea. The problem is that
- * onReadyStateChange takes a REAL XMLHttpRequest, which I can't mock
- * because it's all final. I don't want to open
- * ReadyStateChangeHandler's long-standing API to let it take a non-real
- * XMLHttpRequest, just for my wee test here, so instead I admit that
- * null works 'cause the handler won't *use* its argument.
- */
- handler.onReadyStateChange(null);
- }
- }
-
- public void setOnReadyStateChange(ReadyStateChangeHandler handler) {
- this.handler = handler;
- }
-
- public void setRequestHeader(String header, String value) {
- String val = headers.get(header);
- if (val == null) {
- throw new IllegalArgumentException("set of unexpected header " + header);
- }
- if (!val.equals(value)) {
- throw new IllegalArgumentException("set of header " + header
- + " to unexpected value " + value + ", not " + val);
- }
- headers.remove(header);
- }
- }
-
- /**
- * {@link XhrLoadingStrategy}, but without actual live XHRs.
- */
- static class MockXhrLoadingStrategy extends XhrLoadingStrategy {
- private static final String FRAGMENT_URL = "http://nowhere.net/fragment";
- private ArrayList<MockXhr> xhrs;
-
- public MockXhrLoadingStrategy(MockXhr... input) {
- xhrs = new ArrayList<MockXhr>(Arrays.asList(input));
- }
-
- public void assertDone() {
- if (xhrs.size() != 0) {
- throw new IllegalStateException("leftover createXhr() data"
- + " (too few load retries?)");
- }
- }
-
- /**
- * Test stub; install succeeds unless text says otherwise.
- */
- @Override
- protected void gwtInstallCode(String text) {
- if (MockXhr.INSTALL_FAILED_RESPONSE_TEXT.equals(text)) {
- throw new RuntimeException(text);
- }
- }
-
- /**
- * Test stub; bypass the JSNI, but we're returning a (mock) URL.
- */
- @Override
- protected String gwtStartLoadingFragment(int fragment,
- LoadTerminatedHandler LoadFinishedHandler) {
- return FRAGMENT_URL;
- }
-
- @Override
- protected MockableXMLHttpRequest createXhr() {
- if (xhrs.size() == 0) {
- throw new IllegalStateException("createXhr() underflow"
- + " (too many load retries?)");
- }
- return xhrs.remove(0);
- }
- }
-
- /**
- * Basic succeeds-on-first-try case.
- */
- public void testNoRetrySucceeds() {
- MockXhrLoadingStrategy xls = new MockXhrLoadingStrategy(new MockXhr(200,
- "200 Ok", true, true));
- xls.startLoadingFragment(1, new LoadTerminatedHandler() {
- public void loadTerminated(Throwable reason) {
- fail();
- }
- });
- xls.assertDone();
- }
-
- /**
- * Fails irrevocably on first try; doesn't retry.
- */
- public void testNoRetryFails() {
- final boolean loadFailedCalled[] = new boolean[1];
- loadFailedCalled[0] = false;
- MockXhrLoadingStrategy xls = new MockXhrLoadingStrategy(new MockXhr(200,
- "Ok", true, false));
- xls.startLoadingFragment(1, new LoadTerminatedHandler() {
- public void loadTerminated(Throwable reason) {
- loadFailedCalled[0] = true;
- }
- });
- xls.assertDone();
- if (!loadFailedCalled[0]) {
- fail("should have failed to install, but didn't");
- }
- }
-
- /**
- * Needs some retries, but succeeds.
- */
- public void testRetrySucceeds() {
- MockXhrLoadingStrategy xls = new MockXhrLoadingStrategy(new MockXhr(0,
- "Could not connect", false, false), new MockXhr(200, "Ok", true, true,
- "Cache-Control", "no-cache"));
- xls.startLoadingFragment(1, new LoadTerminatedHandler() {
- public void loadTerminated(Throwable reason) {
- fail();
- }
- });
- xls.assertDone();
- }
-
- /**
- * Needs retries, and never succeeds.
- */
- public void testRetryFails() {
- final boolean loadFailedCalled[] = new boolean[1];
- loadFailedCalled[0] = false;
- MockXhrLoadingStrategy xls = new MockXhrLoadingStrategy(new MockXhr(0,
- "Could not connect", false, false), new MockXhr(0, "Could not connect",
- false, false, "Cache-Control", "no-cache"), new MockXhr(0,
- "Could not connect", false, false, "Cache-Control", "no-cache"));
- xls.startLoadingFragment(1, new LoadTerminatedHandler() {
- public void loadTerminated(Throwable reason) {
- loadFailedCalled[0] = true;
- }
- });
- xls.assertDone();
- if (!loadFailedCalled[0]) {
- fail("should have failed to install, but didn't");
- }
- }
-
- /**
- * A bizarre case we've seen in the wild...
- */
- public void testNull200Case() {
- MockXhrLoadingStrategy xls = new MockXhrLoadingStrategy(new MockXhr(200,
- "Ok", false, false), new MockXhr(200, "Ok", false, false,
- "Cache-Control", "no-cache"), new MockXhr(200, "Ok", true, true,
- "Cache-Control", "no-cache"));
- xls.startLoadingFragment(1, new LoadTerminatedHandler() {
- public void loadTerminated(Throwable reason) {
- fail();
- }
- });
- xls.assertDone();
- }
-
- /**
- * Check some HTTP status codes....
- */
- public void testRetryCodes() {
- MockXhrLoadingStrategy xls = new MockXhrLoadingStrategy(new MockXhr(500,
- "Server Error", false, false), new MockXhr(404, "Not Found", false,
- false, "Cache-Control", "no-cache"), new MockXhr(200, "Ok", true, true,
- "Cache-Control", "no-cache"));
- xls.startLoadingFragment(1, new LoadTerminatedHandler() {
- public void loadTerminated(Throwable reason) {
- fail();
- }
- });
- xls.assertDone();
- }
-}
\ No newline at end of file
diff --git a/user/test/com/google/gwt/dev/jjs/CrossSiteIframeRunAsyncFailure.gwt.xml b/user/test/com/google/gwt/dev/jjs/CrossSiteIframeRunAsyncFailure.gwt.xml
index ef06f33..54f5ad6 100644
--- a/user/test/com/google/gwt/dev/jjs/CrossSiteIframeRunAsyncFailure.gwt.xml
+++ b/user/test/com/google/gwt/dev/jjs/CrossSiteIframeRunAsyncFailure.gwt.xml
@@ -14,12 +14,5 @@
<module>
<inherits name="com.google.gwt.dev.jjs.RunAsyncFailure" />
-
<add-linker name="xsiframe" />
-
- <replace-with
- class="com.google.gwt.user.client.runasync.CrossSiteLoadingStrategyForRunAsyncFailureTest">
- <when-type-is
- class="com.google.gwt.core.client.impl.AsyncFragmentLoader.LoadingStrategy" />
- </replace-with>
</module>
diff --git a/user/test/com/google/gwt/dev/jjs/CrossSiteIframeRunAsyncMetrics.gwt.xml b/user/test/com/google/gwt/dev/jjs/CrossSiteIframeRunAsyncMetrics.gwt.xml
deleted file mode 100644
index cc0fa30..0000000
--- a/user/test/com/google/gwt/dev/jjs/CrossSiteIframeRunAsyncMetrics.gwt.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<!-- -->
-<!-- Copyright 2010 Google Inc. -->
-<!-- Licensed under the Apache License, Version 2.0 (the "License"); you -->
-<!-- may not use this file except in compliance with the License. You may -->
-<!-- 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. License for the specific language governing permissions and -->
-<!-- limitations under the License. -->
-
-<module>
- <!-- We cannot inherit the RunAsyncMetricsIntegrationTest module because
- it adds a script tag and the xsiframe linker does not support them -->
- <inherits name="com.google.gwt.dev.jjs.CompilerSuite"/>
- <add-linker name="xsiframe" />
-
- <public path="public" />
-</module>
diff --git a/user/test/com/google/gwt/dev/jjs/CrossSiteIframeRunAsyncSuite.java b/user/test/com/google/gwt/dev/jjs/CrossSiteIframeRunAsyncSuite.java
index ff0ecfd..bd56ba7 100644
--- a/user/test/com/google/gwt/dev/jjs/CrossSiteIframeRunAsyncSuite.java
+++ b/user/test/com/google/gwt/dev/jjs/CrossSiteIframeRunAsyncSuite.java
@@ -17,7 +17,6 @@
package com.google.gwt.dev.jjs;
import com.google.gwt.dev.jjs.test.CrossSiteIframeRunAsyncFailureTest;
-import com.google.gwt.dev.jjs.test.CrossSiteIframeRunAsyncMetricsTest;
import com.google.gwt.dev.jjs.test.CrossSiteIframeRunAsyncTest;
import com.google.gwt.junit.tools.GWTTestSuite;
@@ -30,7 +29,6 @@
public static Test suite() {
GWTTestSuite suite = new GWTTestSuite("Test for runAsync with the cross-site iframe linker");
- suite.addTestSuite(CrossSiteIframeRunAsyncMetricsTest.class);
suite.addTestSuite(CrossSiteIframeRunAsyncFailureTest.class);
suite.addTestSuite(CrossSiteIframeRunAsyncTest.class);
diff --git a/user/test/com/google/gwt/dev/jjs/test/CrossSiteIframeRunAsyncFailureTest.java b/user/test/com/google/gwt/dev/jjs/test/CrossSiteIframeRunAsyncFailureTest.java
index 17befa9..a6bdaf3 100644
--- a/user/test/com/google/gwt/dev/jjs/test/CrossSiteIframeRunAsyncFailureTest.java
+++ b/user/test/com/google/gwt/dev/jjs/test/CrossSiteIframeRunAsyncFailureTest.java
@@ -28,9 +28,4 @@
public String getModuleName() {
return "com.google.gwt.dev.jjs.CrossSiteIframeRunAsyncFailure";
}
-
- @Override
- public void testHttpFailureRetries() {
- super.testHttpFailureRetries();
- }
}
diff --git a/user/test/com/google/gwt/dev/jjs/test/CrossSiteIframeRunAsyncMetricsTest.java b/user/test/com/google/gwt/dev/jjs/test/CrossSiteIframeRunAsyncMetricsTest.java
deleted file mode 100644
index 24c491c..0000000
--- a/user/test/com/google/gwt/dev/jjs/test/CrossSiteIframeRunAsyncMetricsTest.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright 2010 Google Inc.
- *
- * 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.google.gwt.dev.jjs.test;
-
-import com.google.gwt.junit.DoNotRunWith;
-import com.google.gwt.junit.Platform;
-
-/**
- * Tests runAsync lightweight metrics when the cross-site iframe linker is used.
- */
-@DoNotRunWith(Platform.Devel)
-public class CrossSiteIframeRunAsyncMetricsTest extends
- RunAsyncMetricsIntegrationTest {
- @Override
- public String getModuleName() {
- return "com.google.gwt.dev.jjs.CrossSiteIframeRunAsyncMetrics";
- }
-}
diff --git a/user/test/com/google/gwt/dev/jjs/test/CrossSiteRunAsyncFailureTest.java b/user/test/com/google/gwt/dev/jjs/test/CrossSiteRunAsyncFailureTest.java
index 9d690ba..0418a0e 100644
--- a/user/test/com/google/gwt/dev/jjs/test/CrossSiteRunAsyncFailureTest.java
+++ b/user/test/com/google/gwt/dev/jjs/test/CrossSiteRunAsyncFailureTest.java
@@ -16,7 +16,6 @@
package com.google.gwt.dev.jjs.test;
-import com.google.gwt.core.client.GWT;
import com.google.gwt.junit.DoNotRunWith;
import com.google.gwt.junit.Platform;
@@ -31,11 +30,7 @@
}
@Override
- public void testHttpFailureRetries() {
- if (!GWT.isScript()) {
- // Cross-site linker is not supported in dev mode
- return;
- }
- super.testHttpFailureRetries();
+ protected boolean supportsRetries() {
+ return false;
}
}
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 aaff5aa..947dd64 100644
--- a/user/test/com/google/gwt/dev/jjs/test/RunAsyncFailureTest.java
+++ b/user/test/com/google/gwt/dev/jjs/test/RunAsyncFailureTest.java
@@ -17,6 +17,7 @@
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.RunAsyncCallback;
+import com.google.gwt.core.client.impl.LoadingStrategyBase;
import com.google.gwt.junit.DoNotRunWith;
import com.google.gwt.junit.Platform;
import com.google.gwt.junit.client.GWTTestCase;
@@ -32,15 +33,48 @@
abstract static class MyRunAsyncCallback implements RunAsyncCallback {
private static int sToken = 0;
+ private int attempt;
+ private int expectedSuccessfulAttempt;
private int token;
- public MyRunAsyncCallback() {
+ public MyRunAsyncCallback(int attempt, int expectedSuccessfulAttempt) {
+ this.attempt = attempt;
+ this.expectedSuccessfulAttempt = expectedSuccessfulAttempt;
token = sToken++;
}
public int getToken() {
return token;
}
+
+ public boolean onSuccessHelper() {
+ int token = getToken();
+ log("onSuccess: attempt = " + attempt + ", token = " + token);
+ if (attempt == expectedSuccessfulAttempt) {
+ return true;
+ } else {
+ fail("Succeeded on attempt: " + attempt +
+ " but should have succeeded on attempt: " + expectedSuccessfulAttempt);
+ }
+ return false;
+ }
+
+ protected void onFailureHelper(Throwable caught, Timer t) {
+ // Fail the test if too many attempts have taken place.
+ if (attempt > 5) {
+ fail("Too many failures");
+ }
+
+ int token = getToken();
+ log("onFailure: attempt = " + attempt + ", token = " + token
+ + ", caught = " + caught);
+ t.schedule(100);
+ }
+
+ private native void log(String message) /*-{
+ // Enable this for testing on Safari/WebKit browsers
+ // $wnd.console.log(message);
+ }-*/;
}
private static final int RUNASYNC_TIMEOUT = 30000;
@@ -50,32 +84,88 @@
return "com.google.gwt.dev.jjs.RunAsyncFailure";
}
- // Repeated runAsync using a Timer
- private void runAsync1(final int attempt) {
- log("runAsync1: attempt = " + attempt);
- GWT.runAsync(new MyRunAsyncCallback() {
+ /**
+ * Some subclasses of this test use linkers that do not support retries, in
+ * which case the expected number of manual retries before success will always
+ * be 4.
+ */
+ protected boolean supportsRetries() {
+ return true;
+ }
+
+ // Note that each tests needs a new subclass of MyRunAsyncCallback because
+ // 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) {
- // Fail the test if too many attempts have taken place.
- if (attempt > 20) {
- fail();
- }
-
- int token = getToken();
- log("onFailure: attempt = " + attempt + ", token = " + token
- + ", caught = " + caught);
- new Timer() {
+ onFailureHelper(caught, new Timer() {
@Override
public void run() {
- runAsync1(attempt + 1);
+ runAsync1(attempt + 1, expectedSuccessfulAttempt);
}
- }.schedule(100);
+ });
}
public void onSuccess() {
- int token = getToken();
- log("onSuccess: attempt = " + attempt + ", token = " + token);
+ if (onSuccessHelper()) { finishTest(); }
+ }
+ });
+ }
+
+ // 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) {
+ onFailureHelper(caught, new Timer() {
+ @Override
+ public void run() {
+ runAsync2(attempt + 1, expectedSuccessfulAttempt);
+ }
+ });
+ }
+
+ public void onSuccess() {
+ if (onSuccessHelper()) { finishTest(); }
+ }
+ });
+ }
+
+ // 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) {
+ onFailureHelper(caught, new Timer() {
+ @Override
+ public void run() {
+ runAsync3(attempt + 1, expectedSuccessfulAttempt);
+ }
+ });
+ }
+
+ public void onSuccess() {
+ if (onSuccessHelper()) { finishTest(); }
+ }
+ });
+ }
+
+ // 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) {
+ // This call should fail since no retries are done if the code downloads
+ // successfully, but fails to install.
finishTest();
}
+ public void onSuccess() {
+ fail("Code should have failed to install!");
+ }
});
}
@@ -85,11 +175,33 @@
*/
public void testHttpFailureRetries() {
delayTestFinish(RUNASYNC_TIMEOUT);
- runAsync1(0);
+ // Default is 3, but we set it explicitly, since other tests may run first
+ LoadingStrategyBase.MAX_RETRY_COUNT = 3;
+ // 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.
+ runAsync1(0, supportsRetries() ? 1 : 4);
}
-
- private native void log(String message) /*-{
- // Enable this for testing on Safari/WebKit browsers
- // $wnd.console.log(message);
- }-*/;
+
+ public void testHttpFailureRetries2() {
+ delayTestFinish(RUNASYNC_TIMEOUT);
+ LoadingStrategyBase.MAX_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);
+ }
+
+ public void testBuiltInRetries() {
+ delayTestFinish(RUNASYNC_TIMEOUT);
+ LoadingStrategyBase.MAX_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);
+ }
+
+ public void testDownloadSuccessButInstallFailure() {
+ delayTestFinish(RUNASYNC_TIMEOUT);
+ LoadingStrategyBase.MAX_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 ce19d82..49ccb08 100644
--- a/user/test/com/google/gwt/user/server/runasync/RunAsyncFailureServlet.java
+++ b/user/test/com/google/gwt/user/server/runasync/RunAsyncFailureServlet.java
@@ -32,8 +32,9 @@
*/
public class RunAsyncFailureServlet extends HttpServlet {
- private static final boolean DEBUG = false;
- private static final HashSet<String> errorFragments = new HashSet<String>();
+ private static final boolean DEBUG = true;
+ private static final HashSet<String> downloadErrorFragments = new HashSet<String>();
+ private static final HashSet<String> installErrorFragments = new HashSet<String>();
/**
* Sequence of response codes to send back. SC_OK must be last.
@@ -41,11 +42,16 @@
private static final int[] responses = {
HttpServletResponse.SC_SERVICE_UNAVAILABLE,
HttpServletResponse.SC_GATEWAY_TIMEOUT,
- HttpServletResponse.SC_SERVICE_UNAVAILABLE,
- HttpServletResponse.SC_GATEWAY_TIMEOUT, HttpServletResponse.SC_OK};
+ HttpServletResponse.SC_NOT_FOUND,
+ HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+ HttpServletResponse.SC_OK,
+ };
static {
- errorFragments.add("2.cache.js");
+ downloadErrorFragments.add("2.cache.js");
+ downloadErrorFragments.add("3.cache.js");
+ downloadErrorFragments.add("5.cache.js");
+ installErrorFragments.add("4.cache.js");
}
private static void debug(String message) {
@@ -67,32 +73,33 @@
int response = getDesiredResponse(uri);
String fragment = uri.substring(uri.lastIndexOf('/') + 1);
- if (!errorFragments.contains(fragment)
+ if (!downloadErrorFragments.contains(fragment)
|| response == HttpServletResponse.SC_OK) {
- // Delegate the actual data fetch to the main servlet
-
- String host = req.getLocalName();
- int port = req.getLocalPort();
- String realUrl = "http://" + host + ":" + port + uri;
- debug("Fetching: " + realUrl);
-
int bytes = 0;
- try {
- URL url = new URL(realUrl);
- InputStream is = url.openStream();
- OutputStream os = resp.getOutputStream();
+ if (!installErrorFragments.contains(fragment)) {
+ // Delegate the actual data fetch to the main servlet
+ String host = req.getLocalName();
+ int port = req.getLocalPort();
+ String realUrl = "http://" + host + ":" + port + uri;
+ debug("Fetching: " + realUrl);
- byte[] data = new byte[8192];
- int nbytes;
- while ((nbytes = is.read(data)) != -1) {
- os.write(data, 0, nbytes);
- bytes += nbytes;
+ try {
+ URL url = new URL(realUrl);
+ InputStream is = url.openStream();
+ OutputStream os = resp.getOutputStream();
+
+ byte[] data = new byte[8192];
+ int nbytes;
+ while ((nbytes = is.read(data)) != -1) {
+ os.write(data, 0, nbytes);
+ bytes += nbytes;
+ }
+ is.close();
+ os.close();
+ } catch (IOException e) {
+ debug("IOException fetching real data: " + e);
+ throw e;
}
- is.close();
- os.close();
- } catch (IOException e) {
- debug("IOException fetching real data: " + e);
- throw e;
}
resp.setContentType("text/javascript");