Display UI build failures in browser
Instead of crashing the Jetty daemon when there is a UI build failure, keep the daemon running and display the output from Buck in the web browser. This allows for a slightly quicker fix and retry cycle, especially for minor typos in ui.xml files. Simply edit the offending source and reload, without needing to relaunch the daemon. Change-Id: Iac31374d4efd44c4c6a58ce08497fbf24dd6dfa9
This commit is contained in:
@@ -17,7 +17,10 @@ package com.google.gerrit.pgm.http.jetty;
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.escape.Escaper;
|
||||
import com.google.common.html.HtmlEscapers;
|
||||
import com.google.common.io.ByteStreams;
|
||||
import com.google.gerrit.extensions.events.LifecycleListener;
|
||||
import com.google.gerrit.launcher.GerritLauncher;
|
||||
@@ -27,6 +30,7 @@ import com.google.gerrit.server.config.GerritServerConfig;
|
||||
import com.google.gerrit.server.config.SitePaths;
|
||||
import com.google.gerrit.server.util.TimeUtil;
|
||||
import com.google.gwtexpui.linker.server.UserAgentRule;
|
||||
import com.google.gwtexpui.server.CacheHeaders;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Injector;
|
||||
import com.google.inject.Singleton;
|
||||
@@ -58,6 +62,7 @@ import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||
import org.eclipse.jetty.util.thread.QueuedThreadPool;
|
||||
import org.eclipse.jetty.util.thread.ThreadPool;
|
||||
import org.eclipse.jgit.lib.Config;
|
||||
import org.eclipse.jgit.util.RawParseUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -68,6 +73,7 @@ import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InterruptedIOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
@@ -90,6 +96,7 @@ import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
@Singleton
|
||||
public class JettyServer {
|
||||
@@ -582,10 +589,16 @@ public class JettyServer {
|
||||
FilterChain chain) throws IOException, ServletException {
|
||||
String pkg = "gerrit-gwtui";
|
||||
String target = "ui_" + rule.select((HttpServletRequest) request);
|
||||
String rule = "//" + pkg + ":" + target;
|
||||
File zip = new File(new File(gen, pkg), target + ".zip");
|
||||
|
||||
synchronized (this) {
|
||||
build(root, gen, "//" + pkg + ":" + target);
|
||||
try {
|
||||
build(root, gen, rule);
|
||||
} catch (BuildFailureException e) {
|
||||
displayFailure(rule, e.why, (HttpServletResponse) res);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!target.equals(lastTarget) || lastTime != zip.lastModified()) {
|
||||
lastTarget = target;
|
||||
@@ -597,6 +610,24 @@ public class JettyServer {
|
||||
chain.doFilter(request, res);
|
||||
}
|
||||
|
||||
private void displayFailure(String rule, byte[] why, HttpServletResponse res)
|
||||
throws IOException {
|
||||
res.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
|
||||
res.setContentType("text/html");
|
||||
res.setCharacterEncoding(Charsets.UTF_8.name());
|
||||
CacheHeaders.setNotCacheable(res);
|
||||
|
||||
Escaper html = HtmlEscapers.htmlEscaper();
|
||||
PrintWriter w = res.getWriter();
|
||||
w.write("<html><title>BUILD FAILED</title><body>");
|
||||
w.format("<h1>%s FAILED</h1>", html.escape(rule));
|
||||
w.write("<pre>");
|
||||
w.write(html.escape(RawParseUtils.decode(why)));
|
||||
w.write("</pre>");
|
||||
w.write("</body></html>");
|
||||
w.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(FilterConfig config) {
|
||||
}
|
||||
@@ -609,7 +640,7 @@ public class JettyServer {
|
||||
}
|
||||
|
||||
private static void build(File root, File gen, String target)
|
||||
throws IOException {
|
||||
throws IOException, BuildFailureException {
|
||||
log.info("buck build " + target);
|
||||
Properties properties = loadBuckProperties(gen);
|
||||
String buck = Objects.firstNonNull(properties.getProperty("buck"), "buck");
|
||||
@@ -637,9 +668,7 @@ public class JettyServer {
|
||||
throw new InterruptedIOException("interrupted waiting for " + buck);
|
||||
}
|
||||
if (status != 0) {
|
||||
System.err.write(out);
|
||||
System.err.println();
|
||||
System.exit(status);
|
||||
throw new BuildFailureException(out);
|
||||
}
|
||||
|
||||
long time = TimeUtil.nowMs() - start;
|
||||
@@ -658,4 +687,13 @@ public class JettyServer {
|
||||
}
|
||||
return properties;
|
||||
}
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
private static class BuildFailureException extends Exception {
|
||||
final byte[] why;
|
||||
|
||||
BuildFailureException(byte[] why) {
|
||||
this.why = why;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user