Super Dev Mode: better errors when we haven't compiled yet
These pages can't work without compiler output:
- sourcemaps and source code listings
- compile error log
Also changed the module page to not link to them.
Refactoring: pass an Outbox instead of a module name
in more places.
Change-Id: I3a8b3dcf7744fa164cc670d0938b946aa86c1403
diff --git a/dev/codeserver/java/com/google/gwt/dev/codeserver/HtmlWriter.java b/dev/codeserver/java/com/google/gwt/dev/codeserver/HtmlWriter.java
index 91fa680..f2949b9 100644
--- a/dev/codeserver/java/com/google/gwt/dev/codeserver/HtmlWriter.java
+++ b/dev/codeserver/java/com/google/gwt/dev/codeserver/HtmlWriter.java
@@ -29,7 +29,7 @@
private static final Set<String> ALLOWED_TAGS =
Collections.unmodifiableSet(new HashSet<String>(Arrays.asList(
"html", "head", "title", "style",
- "body", "h1", "h2", "h3", "h4", "h5", "h6", "a", "pre", "span",
+ "body", "h1", "h2", "h3", "h4", "h5", "h6", "p", "a", "pre", "span",
"table", "tr", "td")));
private static final Set<String> ALLOWED_ATTS =
Collections.unmodifiableSet(new HashSet<String>(Arrays.asList(
diff --git a/dev/codeserver/java/com/google/gwt/dev/codeserver/JsonExporter.java b/dev/codeserver/java/com/google/gwt/dev/codeserver/JsonExporter.java
index 5f645c8..d61ca7f 100644
--- a/dev/codeserver/java/com/google/gwt/dev/codeserver/JsonExporter.java
+++ b/dev/codeserver/java/com/google/gwt/dev/codeserver/JsonExporter.java
@@ -83,6 +83,7 @@
JsonObject result = new JsonObject();
result.put("moduleName", box.getOutputModuleName()); // TODO: rename
result.put("files", exportOutputFiles(box));
+ result.put("isCompiled", !box.containsStubCompile());
return result;
}
diff --git a/dev/codeserver/java/com/google/gwt/dev/codeserver/Outbox.java b/dev/codeserver/java/com/google/gwt/dev/codeserver/Outbox.java
index 0a84805..4531395 100644
--- a/dev/codeserver/java/com/google/gwt/dev/codeserver/Outbox.java
+++ b/dev/codeserver/java/com/google/gwt/dev/codeserver/Outbox.java
@@ -150,6 +150,14 @@
}
/**
+ * Returns true if we haven't done a real compile yet, so the Outbox contains
+ * a stub that will automatically start a compile.
+ */
+ synchronized boolean containsStubCompile() {
+ return publishedJob == null;
+ }
+
+ /**
* Returns the module name that will be sent to the compiler (before renaming).
*/
String getInputModuleName() {
diff --git a/dev/codeserver/java/com/google/gwt/dev/codeserver/OutboxTable.java b/dev/codeserver/java/com/google/gwt/dev/codeserver/OutboxTable.java
index 54ce117..220a487 100644
--- a/dev/codeserver/java/com/google/gwt/dev/codeserver/OutboxTable.java
+++ b/dev/codeserver/java/com/google/gwt/dev/codeserver/OutboxTable.java
@@ -18,9 +18,11 @@
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.thirdparty.guava.common.collect.ImmutableList;
import com.google.gwt.thirdparty.guava.common.collect.Lists;
import com.google.gwt.thirdparty.guava.common.collect.Maps;
+import java.io.File;
import java.util.Collection;
import java.util.List;
import java.util.Map;
@@ -44,6 +46,10 @@
outboxes.put(outbox.getId(), outbox);
}
+ ImmutableList<Outbox> getOutboxes() {
+ return ImmutableList.copyOf(outboxes.values());
+ }
+
/**
* Retrieves an {@link Outbox} corresponding to a given module name.
* This should be the module name after renaming.
@@ -74,4 +80,19 @@
box.maybePrecompile(logger);
}
}
+
+ /**
+ * Given the name of a policy file, searches all the boxes for a file with that name.
+ * Returns null if not found.
+ */
+ File findPolicyFile(String filename) {
+ for (Outbox box : outboxes.values()) {
+ File candidate = box.getOutputFile(box.getOutputModuleName() + "/" + filename);
+ if (candidate.isFile()) {
+ return candidate;
+ }
+ }
+ // not found
+ return null;
+ }
}
diff --git a/dev/codeserver/java/com/google/gwt/dev/codeserver/PageUtil.java b/dev/codeserver/java/com/google/gwt/dev/codeserver/PageUtil.java
index c22a127..9a92d03 100644
--- a/dev/codeserver/java/com/google/gwt/dev/codeserver/PageUtil.java
+++ b/dev/codeserver/java/com/google/gwt/dev/codeserver/PageUtil.java
@@ -17,6 +17,7 @@
package com.google.gwt.dev.codeserver;
import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.TreeLogger.Type;
import com.google.gwt.dev.json.JsonObject;
import com.google.gwt.thirdparty.guava.common.base.Charsets;
import com.google.gwt.thirdparty.guava.common.io.Files;
@@ -220,4 +221,28 @@
OutputStream out = new FileOutputStream(path);
PageUtil.copyStream(in, out);
}
+
+ /**
+ * Sends an error response because something is unavailable. (Also logs it.)
+ */
+ static void sendUnavailable(HttpServletResponse response, TreeLogger logger, String message)
+ throws IOException {
+
+ response.setStatus(HttpServletResponse.SC_NOT_FOUND);
+ response.setContentType("text/html");
+ HtmlWriter out = new HtmlWriter(response.getWriter());
+ out.startTag("html").nl();
+
+ out.startTag("head").nl();
+ out.startTag("title").text("Unavailable (GWT Code Server)").endTag("title").nl();
+ out.endTag("head").nl();
+
+ out.startTag("body").nl();
+ out.startTag("p").text(message).endTag("p");
+ out.endTag("body").nl();
+
+ out.endTag("html").nl();
+
+ logger.log(Type.INFO, "Sent unavailable response: " + message);
+ }
}
diff --git a/dev/codeserver/java/com/google/gwt/dev/codeserver/SourceHandler.java b/dev/codeserver/java/com/google/gwt/dev/codeserver/SourceHandler.java
index 8ac9bfb..16e1057 100644
--- a/dev/codeserver/java/com/google/gwt/dev/codeserver/SourceHandler.java
+++ b/dev/codeserver/java/com/google/gwt/dev/codeserver/SourceHandler.java
@@ -103,8 +103,10 @@
Outbox box = outboxes.findByOutputModuleName(moduleName);
if (box == null) {
- response.sendError(HttpServletResponse.SC_NOT_FOUND);
- logger.log(TreeLogger.WARN, "unknown module; returned not found for request: " + target);
+ PageUtil.sendUnavailable(response, logger, "No such module: " + moduleName);
+ return;
+ } else if (box.containsStubCompile()) {
+ PageUtil.sendUnavailable(response, logger, "This module hasn't been compiled yet.");
return;
}
@@ -112,14 +114,14 @@
String rest = target.substring(rootDir.length());
if (rest.isEmpty()) {
- sendDirectoryListPage(moduleName, response, logger);
+ sendDirectoryListPage(box, response, logger);
} else if (rest.equals("gwtSourceMap.json")) {
// This URL is no longer used by debuggers (we use the strong name) but is used for testing.
// It's useful not to need the strong name to download the sourcemap.
// (But this only works when there is one permutation.)
sendSourceMap(moduleName, box.findSourceMapForOnePermutation(), request, response, logger);
} else if (rest.endsWith("/")) {
- sendFileListPage(moduleName, rest, response, logger);
+ sendFileListPage(box, rest, response, logger);
} else if (rest.endsWith(".java")) {
sendSourceFile(box, rest, request.getQueryString(), response, logger);
} else {
@@ -169,19 +171,17 @@
"' in " + elapsedTime + " ms");
}
- private void sendDirectoryListPage(String moduleName, HttpServletResponse response,
+ private void sendDirectoryListPage(Outbox box, HttpServletResponse response,
TreeLogger logger) throws IOException {
- Outbox box = outboxes.findByOutputModuleName(moduleName);
SourceMap map = SourceMap.load(box.findSourceMapForOnePermutation());
JsonObject json = exporter.exportSourceMapDirectoryListVars(box, map);
PageUtil.sendJsonAndHtml("config", json, "directorylist.html", response, logger);
}
- private void sendFileListPage(String moduleName, String rest, HttpServletResponse response,
+ private void sendFileListPage(Outbox box, String rest, HttpServletResponse response,
TreeLogger logger) throws IOException {
- Outbox box = outboxes.findByOutputModuleName(moduleName);
SourceMap map = SourceMap.load(box.findSourceMapForOnePermutation());
JsonObject json = exporter.exportSourceMapFileListVars(box, map, rest);
PageUtil.sendJsonAndHtml("config", json, "filelist.html", response, logger);
diff --git a/dev/codeserver/java/com/google/gwt/dev/codeserver/WebServer.java b/dev/codeserver/java/com/google/gwt/dev/codeserver/WebServer.java
index d5a1e74..979e13f 100644
--- a/dev/codeserver/java/com/google/gwt/dev/codeserver/WebServer.java
+++ b/dev/codeserver/java/com/google/gwt/dev/codeserver/WebServer.java
@@ -200,10 +200,9 @@
if (target.startsWith("/recompile/")) {
setHandled(request);
String moduleName = target.substring("/recompile/".length());
- Outbox outbox = outboxes.findByOutputModuleName(moduleName);
- if (outbox == null) {
- response.sendError(HttpServletResponse.SC_NOT_FOUND);
- logger.log(TreeLogger.WARN, "not found: " + target);
+ Outbox box = outboxes.findByOutputModuleName(moduleName);
+ if (box == null) {
+ PageUtil.sendUnavailable(response, logger, "No such module: " + moduleName);
return;
}
@@ -213,7 +212,7 @@
// cause a spurious recompile, resulting in an unexpected permutation being loaded later.
//
// It would be unsafe to allow a configuration property to be changed.
- Job job = outbox.makeJob(getBindingProperties(request), logger);
+ Job job = box.makeJob(getBindingProperties(request), logger);
runner.submit(job);
Job.Result result = job.waitForResult();
JsonObject json = jsonExporter.exportRecompileResponse(result);
@@ -224,8 +223,14 @@
if (target.startsWith("/log/")) {
setHandled(request);
String moduleName = target.substring("/log/".length());
- File file = outboxes.findByOutputModuleName(moduleName).getCompileLog();
- sendLogPage(moduleName, file, response);
+ Outbox box = outboxes.findByOutputModuleName(moduleName);
+ if (box == null) {
+ PageUtil.sendUnavailable(response, logger, "No such module: " + moduleName);
+ } else if (box.containsStubCompile()) {
+ PageUtil.sendUnavailable(response, logger, "This module hasn't been compiled yet.");
+ } else {
+ sendLogPage(box, response);
+ }
return;
}
@@ -299,12 +304,16 @@
int secondSlash = target.indexOf('/', 1);
String moduleName = target.substring(1, secondSlash);
- Outbox outbox = outboxes.findByOutputModuleName(moduleName);
+ Outbox box = outboxes.findByOutputModuleName(moduleName);
+ if (box == null) {
+ PageUtil.sendUnavailable(response, logger, "No such module: " + moduleName);
+ return;
+ }
- File file = outbox.getOutputFile(target);
+ File file = box.getOutputFile(target);
if (!file.isFile()) {
// perhaps it's compressed
- file = outbox.getOutputFile(target + ".gz");
+ file = box.getOutputFile(target + ".gz");
if (!file.isFile()) {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
logger.log(TreeLogger.WARN, "not found: " + file.toString());
@@ -334,10 +343,10 @@
throws IOException {
Outbox box = outboxes.findByOutputModuleName(moduleName);
if (box == null) {
- response.sendError(HttpServletResponse.SC_NOT_FOUND);
- logger.log(TreeLogger.WARN, "module not found: " + moduleName);
+ PageUtil.sendUnavailable(response, logger, "No such module: " + moduleName);
return;
}
+
JsonObject json = jsonExporter.exportModulePageVars(box);
PageUtil.sendJsonAndHtml("config", json, "modulepage.html", response, logger);
}
@@ -356,11 +365,10 @@
out.startTag("h1").text("Policy Files").endTag("h1").nl();
- for (String moduleName : outboxes.getOutputModuleNames()) {
- Outbox module = outboxes.findByOutputModuleName(moduleName);
- File manifest = module.getExtraFile("rpcPolicyManifest/manifest.txt");
+ for (Outbox box : outboxes.getOutboxes()) {
+ File manifest = box.getExtraFile("rpcPolicyManifest/manifest.txt");
if (manifest.isFile()) {
- out.startTag("h2").text(moduleName).endTag("h2").nl();
+ out.startTag("h2").text(box.getOutputModuleName()).endTag("h2").nl();
out.startTag("table").nl();
String text = PageUtil.loadFile(manifest);
@@ -377,7 +385,7 @@
String serviceName = fields[0];
String policyFileName = fields[1];
- String serviceUrl = SourceHandler.SOURCEMAP_PATH + moduleName + "/" +
+ String serviceUrl = SourceHandler.SOURCEMAP_PATH + box.getOutputModuleName() + "/" +
serviceName.replace('.', '/') + ".java";
String policyUrl = "/policies/" + policyFileName;
@@ -403,28 +411,26 @@
private void sendPolicyFile(String target, HttpServletResponse response, TreeLogger logger)
throws IOException {
+
int secondSlash = target.indexOf('/', 1);
if (secondSlash < 1) {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
}
+
String rest = target.substring(secondSlash + 1);
if (rest.contains("/") || !rest.endsWith(".gwt.rpc")) {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
}
- for (String moduleName : outboxes.getOutputModuleNames()) {
- Outbox module = outboxes.findByOutputModuleName(moduleName);
- File policy = module.getOutputFile(moduleName + "/" + rest);
- if (policy.isFile()) {
- PageUtil.sendFile("text/plain", policy, response);
- return;
- }
+ File policy = outboxes.findPolicyFile(rest);
+ if (policy == null) {
+ PageUtil.sendUnavailable(response, logger, "Policy file not found: " + rest);
+ return;
}
- logger.log(TreeLogger.Type.WARN, "policy file not found: " + rest);
- response.sendError(HttpServletResponse.SC_NOT_FOUND);
+ PageUtil.sendFile("text/plain", policy, response);
}
private void sendJsonResult(JsonObject json, HttpServletRequest request,
@@ -458,8 +464,9 @@
/**
* Sends the log file as html with errors highlighted in red.
*/
- private void sendLogPage(String moduleName, File file, HttpServletResponse response)
+ private void sendLogPage(Outbox box, HttpServletResponse response)
throws IOException {
+ File file = box.getCompileLog();
BufferedReader reader = new BufferedReader(new FileReader(file));
response.setStatus(HttpServletResponse.SC_OK);
@@ -469,7 +476,7 @@
HtmlWriter out = new HtmlWriter(response.getWriter());
out.startTag("html").nl();
out.startTag("head").nl();
- out.startTag("title").text(moduleName + " compile log").endTag("title").nl();
+ out.startTag("title").text(box.getOutputModuleName() + " compile log").endTag("title").nl();
out.startTag("style").nl();
out.text(".error { color: red; font-weight: bold; }").nl();
out.endTag("style").nl();
diff --git a/dev/codeserver/java/com/google/gwt/dev/codeserver/modulepage.html b/dev/codeserver/java/com/google/gwt/dev/codeserver/modulepage.html
index 29a8647..1a67396 100644
--- a/dev/codeserver/java/com/google/gwt/dev/codeserver/modulepage.html
+++ b/dev/codeserver/java/com/google/gwt/dev/codeserver/modulepage.html
@@ -21,13 +21,26 @@
document.title = title;
setTextContent(document.getElementById("title"), title);
- document.getElementById("logLink")
- .setAttribute("href", "../log/" + config.moduleName);
- document.getElementById("srcLink")
- .setAttribute("href", "../sourcemaps/" + config.moduleName + "/");
+ if (config.isCompiled) {
+ clearClassName(".show-if-compiled");
+ document.getElementById("logLink")
+ .setAttribute("href", "../log/" + config.moduleName);
+ document.getElementById("srcLink")
+ .setAttribute("href", "../sourcemaps/" + config.moduleName + "/");
+ } else {
+ clearClassName(".show-if-not-compiled");
+ }
+
updateFileList(config, document.getElementById("files"));
}
+ function clearClassName(query) {
+ var elts = document.querySelectorAll(query);
+ for (var i = 0; i < elts.length; i++) {
+ elts[i].className = "";
+ }
+ }
+
function updateFileList(config, resultElement) {
for (var i = 0; i < config.files.length; i++) {
var file = config.files[i];
@@ -63,12 +76,23 @@
}
</script>
+ <style type="text/css">
+ .show-if-compiled, .show-if-not-compiled {
+ display: none;
+ }
+ </style>
</head>
<body>
<h1 id="title">Loading...</h1>
+<div class="show-if-not-compiled">
+<p>This module hasn't been compiled yet.</p>
+</div>
+
+<div class="show-if-compiled">
<p><a id="logLink">Messages</a> from the last time this module was compiled.</p>
<p><a id="srcLink">Source</a> from the last time this module was compiled.</p>
+</div>
<h2>Output Files</h2>
<p>(From the last successful compile.)</p>