Automatically refresh GWT UI on each page load
This came out of frustration while working on the ChangeScreen2 work. For some types of UI work it can be easier to leave the Jetty server running and simply click reload in the browser to recompile the GWT code and load the new JavaScript. GWT Jetty ------------- server startup 6s 1s initial request 20s 39s no-op reload 10s 7s The real win comes from changing class structure in the GWT UI code. None of the UI classes are loaded into the Jetty server so there are no class schema compatibility issues. In the hosted mode debugger the developer must exit the debugger and restart it, bringing the edit-test cycle to significantly longer than the time it takes to run Buck. Another benefit is testing runs with a real browser, which can show different results in JSNI code than in the hosted mode debugger. Events and navigation is faster too, thanks to everything running natively in the browser. Overall this can make the UI development experience much less frustrating. Change-Id: Ib4a4ff547f60dd10ae32aaacc07f9c84b848cfdc
This commit is contained in:
parent
e65895a042
commit
f27e698226
@ -77,6 +77,12 @@ import java.util.zip.ZipFile;
|
||||
|
||||
import javax.servlet.DispatcherType;
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.FilterConfig;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
@Singleton
|
||||
public class JettyServer {
|
||||
@ -343,7 +349,7 @@ public class JettyServer {
|
||||
// need to unpack them into yet another temporary directory prior to
|
||||
// serving to clients.
|
||||
//
|
||||
app.setBaseResource(getBaseResource());
|
||||
app.setBaseResource(getBaseResource(app));
|
||||
|
||||
// HTTP front-end filter to be used as surrogate of Apache HTTP
|
||||
// reverse-proxy filtering.
|
||||
@ -397,13 +403,14 @@ public class JettyServer {
|
||||
return app;
|
||||
}
|
||||
|
||||
private Resource getBaseResource() throws IOException {
|
||||
private Resource getBaseResource(ServletContextHandler app)
|
||||
throws IOException {
|
||||
if (baseResource == null) {
|
||||
try {
|
||||
baseResource = unpackWar(GerritLauncher.getDistributionArchive());
|
||||
} catch (FileNotFoundException err) {
|
||||
if (err.getMessage() == GerritLauncher.NOT_ARCHIVED) {
|
||||
baseResource = useDeveloperBuild();
|
||||
baseResource = useDeveloperBuild(app);
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
@ -412,7 +419,13 @@ public class JettyServer {
|
||||
return baseResource;
|
||||
}
|
||||
|
||||
private Resource unpackWar(File srcwar) throws IOException {
|
||||
private static Resource unpackWar(File srcwar) throws IOException {
|
||||
File dstwar = makeWarTempDir();
|
||||
unpack(srcwar, dstwar);
|
||||
return Resource.newResource(dstwar.toURI());
|
||||
}
|
||||
|
||||
private static File makeWarTempDir() throws IOException {
|
||||
// Obtain our local temporary directory, but it comes back as a file
|
||||
// so we have to switch it to be a directory post creation.
|
||||
//
|
||||
@ -425,11 +438,13 @@ public class JettyServer {
|
||||
// a security feature. Try to resolve out any symlinks in the path.
|
||||
//
|
||||
try {
|
||||
dstwar = dstwar.getCanonicalFile();
|
||||
return dstwar.getCanonicalFile();
|
||||
} catch (IOException e) {
|
||||
dstwar = dstwar.getAbsoluteFile();
|
||||
return dstwar.getAbsoluteFile();
|
||||
}
|
||||
}
|
||||
|
||||
private static void unpack(File srcwar, File dstwar) throws IOException {
|
||||
final ZipFile zf = new ZipFile(srcwar);
|
||||
try {
|
||||
final Enumeration<? extends ZipEntry> e = zf.entries();
|
||||
@ -466,11 +481,9 @@ public class JettyServer {
|
||||
} finally {
|
||||
zf.close();
|
||||
}
|
||||
|
||||
return Resource.newResource(dstwar.toURI());
|
||||
}
|
||||
|
||||
private void mkdir(final File dir) throws IOException {
|
||||
private static void mkdir(File dir) throws IOException {
|
||||
if (!dir.isDirectory()) {
|
||||
mkdir(dir.getParentFile());
|
||||
if (!dir.mkdir())
|
||||
@ -479,7 +492,8 @@ public class JettyServer {
|
||||
}
|
||||
}
|
||||
|
||||
private Resource useDeveloperBuild() throws IOException {
|
||||
private Resource useDeveloperBuild(ServletContextHandler app)
|
||||
throws IOException {
|
||||
// Find ourselves in the CLASSPATH. We should be a loose class file.
|
||||
//
|
||||
URL u = getClass().getResource(getClass().getSimpleName() + ".class");
|
||||
@ -510,12 +524,44 @@ public class JettyServer {
|
||||
dir = dir.getParentFile(); // pop classes
|
||||
|
||||
if ("buck-out".equals(dir.getName())) {
|
||||
final File dstwar = makeWarTempDir();
|
||||
String pkg = "gerrit-gwtui";
|
||||
String target = targetForBrowser(System.getProperty("gerrit.browser"));
|
||||
File gen = new File(dir, "gen");
|
||||
final File gen = new File(dir, "gen");
|
||||
String out = new File(new File(gen, pkg), target).getAbsolutePath();
|
||||
build(dir.getParentFile(), gen, "//" + pkg + ":" + target);
|
||||
return unpackWar(new File(out + ".zip"));
|
||||
final File zip = new File(out + ".zip");
|
||||
final File root = dir.getParentFile();
|
||||
final String name = "//" + pkg + ":" + target;
|
||||
|
||||
File ui = new File(dstwar, "gerrit_ui");
|
||||
File p = new File(ui, "permutations");
|
||||
mkdir(ui);
|
||||
p.createNewFile();
|
||||
p.deleteOnExit();
|
||||
|
||||
app.addFilter(new FilterHolder(new Filter() {
|
||||
private long last;
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest request, ServletResponse res,
|
||||
FilterChain chain) throws IOException, ServletException {
|
||||
HttpServletRequest req = (HttpServletRequest) request;
|
||||
build(root, gen, name);
|
||||
if (last != zip.lastModified()) {
|
||||
last = zip.lastModified();
|
||||
unpack(zip, dstwar);
|
||||
}
|
||||
chain.doFilter(req, res);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(FilterConfig config) {
|
||||
}
|
||||
@Override
|
||||
public void destroy() {
|
||||
}
|
||||
}), "/", EnumSet.of(DispatcherType.REQUEST));
|
||||
return Resource.newResource(dstwar.toURI());
|
||||
} else if ("target".equals(dir.getName())) {
|
||||
return useMavenDeveloperBuild(dir);
|
||||
} else {
|
||||
|
Loading…
Reference in New Issue
Block a user