StaticModule: Lazily initialize zip FileSystem
Java's zip FileSystem is rather memory-hungry, as it maintains the full central directory in memory, among other internal state. In the headless server case, we never actually need to serve a UI from the war, so it's not necessary to even load the zip FileSystem. Maintain a synchronized map of zip FileSystems in GerritLauncher, since each one still needs to be a singleton, but we don't want to eagerly load it just because we wanted to look up the war location. Change-Id: Id186964b2a197a8a0e874f86c72c21e06fc1388f
This commit is contained in:
@@ -41,20 +41,17 @@ public class StaticModule extends ServletModule {
|
||||
static final String CACHE = "static_content";
|
||||
|
||||
private final GerritOptions options;
|
||||
private final FileSystem warFs;
|
||||
private final Path buckOut;
|
||||
private final Path unpackedWar;
|
||||
private Paths paths;
|
||||
|
||||
public StaticModule(GerritOptions options) {
|
||||
this.options = options;
|
||||
warFs = getDistributionArchive();
|
||||
if (warFs == null) {
|
||||
buckOut = getDeveloperBuckOut();
|
||||
unpackedWar = makeWarTempDir();
|
||||
} else {
|
||||
buckOut = null;
|
||||
unpackedWar = null;
|
||||
}
|
||||
|
||||
private Paths getPaths() {
|
||||
if (paths == null) {
|
||||
paths = new Paths();
|
||||
}
|
||||
return paths;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -69,128 +66,161 @@ public class StaticModule extends ServletModule {
|
||||
}
|
||||
});
|
||||
if (options.enablePolyGerrit()) {
|
||||
servePolyGerritUi();
|
||||
install(new PolyGerritUiModule());
|
||||
} else if (options.enableDefaultUi()) {
|
||||
serveGwtUi();
|
||||
install(new GwtUiModule());
|
||||
}
|
||||
}
|
||||
|
||||
private void serveGwtUi() {
|
||||
serveRegex("^/gerrit_ui/(?!rpc/)(.*)$")
|
||||
.with(Key.get(HttpServlet.class, Names.named(GWT_UI_SERVLET)));
|
||||
if (warFs == null && buckOut != null) {
|
||||
filter("/").through(new RecompileGwtUiFilter(buckOut, unpackedWar));
|
||||
private class GwtUiModule extends ServletModule {
|
||||
@Override
|
||||
public void configureServlets() {
|
||||
serveRegex("^/gerrit_ui/(?!rpc/)(.*)$")
|
||||
.with(Key.get(HttpServlet.class, Names.named(GWT_UI_SERVLET)));
|
||||
Paths p = getPaths();
|
||||
if (p.warFs == null && p.buckOut != null) {
|
||||
filter("/").through(new RecompileGwtUiFilter(p.buckOut, p.unpackedWar));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void servePolyGerritUi() {
|
||||
serve("/").with(PolyGerritUiIndexServlet.class);
|
||||
serve("/c/*").with(PolyGerritUiIndexServlet.class);
|
||||
serve("/q/*").with(PolyGerritUiIndexServlet.class);
|
||||
serve("/x/*").with(PolyGerritUiIndexServlet.class);
|
||||
serve("/admin/*").with(PolyGerritUiIndexServlet.class);
|
||||
serve("/dashboard/*").with(PolyGerritUiIndexServlet.class);
|
||||
serve("/settings/*").with(PolyGerritUiIndexServlet.class);
|
||||
// TODO(dborowitz): These fragments conflict with the REST API namespace, so
|
||||
// they will need to use a different path.
|
||||
//serve("/groups/*").with(PolyGerritUiIndexServlet.class);
|
||||
//serve("/projects/*").with(PolyGerritUiIndexServlet.class);
|
||||
|
||||
if (warFs == null) {
|
||||
serve("/bower_components/*")
|
||||
.with(Key.get(PolyGerritUiServlet.class, Names.named(BOWER_SERVLET)));
|
||||
}
|
||||
serve("/*").with(PolyGerritUiServlet.class);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@Named(GWT_UI_SERVLET)
|
||||
HttpServlet getGwtUiServlet(@Named(CACHE) Cache<Path, Resource> cache)
|
||||
throws IOException {
|
||||
if (warFs != null) {
|
||||
return new WarGwtUiServlet(cache, warFs);
|
||||
} else {
|
||||
return new DeveloperGwtUiServlet(cache, unpackedWar);
|
||||
}
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
PolyGerritUiIndexServlet getPolyGerritUiIndexServlet(
|
||||
@Named(CACHE) Cache<Path, Resource> cache) {
|
||||
return new PolyGerritUiIndexServlet(cache, polyGerritBasePath());
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
PolyGerritUiServlet getPolyGerritUiServlet(
|
||||
@Named(CACHE) Cache<Path, Resource> cache) {
|
||||
return new PolyGerritUiServlet(cache, polyGerritBasePath());
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@Named(BOWER_SERVLET)
|
||||
PolyGerritUiServlet getPolyGerritUiBowerServlet(
|
||||
@Named(CACHE) Cache<Path, Resource> cache) {
|
||||
return new PolyGerritUiServlet(cache,
|
||||
polyGerritBasePath().resolveSibling("bower_components"));
|
||||
}
|
||||
|
||||
private Path polyGerritBasePath() {
|
||||
return warFs != null
|
||||
? warFs.getPath("/polygerrit_ui")
|
||||
: buckOut.getParent().resolve("polygerrit-ui").resolve("app");
|
||||
}
|
||||
|
||||
private static FileSystem getDistributionArchive() {
|
||||
try {
|
||||
return GerritLauncher.getDistributionArchiveFileSystem();
|
||||
} catch (IOException e) {
|
||||
if ((e instanceof FileNotFoundException)
|
||||
&& GerritLauncher.NOT_ARCHIVED.equals(e.getMessage())) {
|
||||
return null;
|
||||
@Provides
|
||||
@Singleton
|
||||
@Named(GWT_UI_SERVLET)
|
||||
HttpServlet getGwtUiServlet(@Named(CACHE) Cache<Path, Resource> cache)
|
||||
throws IOException {
|
||||
Paths p = getPaths();
|
||||
if (p.warFs != null) {
|
||||
return new WarGwtUiServlet(cache, p.warFs);
|
||||
} else {
|
||||
return new DeveloperGwtUiServlet(cache, p.unpackedWar);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class PolyGerritUiModule extends ServletModule {
|
||||
@Override
|
||||
public void configureServlets() {
|
||||
serve("/").with(PolyGerritUiIndexServlet.class);
|
||||
serve("/c/*").with(PolyGerritUiIndexServlet.class);
|
||||
serve("/q/*").with(PolyGerritUiIndexServlet.class);
|
||||
serve("/x/*").with(PolyGerritUiIndexServlet.class);
|
||||
serve("/admin/*").with(PolyGerritUiIndexServlet.class);
|
||||
serve("/dashboard/*").with(PolyGerritUiIndexServlet.class);
|
||||
serve("/settings/*").with(PolyGerritUiIndexServlet.class);
|
||||
// TODO(dborowitz): These fragments conflict with the REST API namespace,
|
||||
// so they will need to use a different path.
|
||||
//serve("/groups/*").with(PolyGerritUiIndexServlet.class);
|
||||
//serve("/projects/*").with(PolyGerritUiIndexServlet.class);
|
||||
|
||||
if (getPaths().warFs == null) {
|
||||
serve("/bower_components/*").with(
|
||||
Key.get(PolyGerritUiServlet.class, Names.named(BOWER_SERVLET)));
|
||||
}
|
||||
serve("/*").with(PolyGerritUiServlet.class);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
PolyGerritUiIndexServlet getPolyGerritUiIndexServlet(
|
||||
@Named(CACHE) Cache<Path, Resource> cache) {
|
||||
return new PolyGerritUiIndexServlet(cache, polyGerritBasePath());
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
PolyGerritUiServlet getPolyGerritUiServlet(
|
||||
@Named(CACHE) Cache<Path, Resource> cache) {
|
||||
return new PolyGerritUiServlet(cache, polyGerritBasePath());
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@Named(BOWER_SERVLET)
|
||||
PolyGerritUiServlet getPolyGerritUiBowerServlet(
|
||||
@Named(CACHE) Cache<Path, Resource> cache) {
|
||||
return new PolyGerritUiServlet(cache,
|
||||
polyGerritBasePath().resolveSibling("bower_components"));
|
||||
}
|
||||
|
||||
private Path polyGerritBasePath() {
|
||||
Paths p = getPaths();
|
||||
return p.warFs != null
|
||||
? p.warFs.getPath("/polygerrit_ui")
|
||||
: p.buckOut.getParent().resolve("polygerrit-ui").resolve("app");
|
||||
}
|
||||
}
|
||||
|
||||
private static class Paths {
|
||||
private final FileSystem warFs;
|
||||
private final Path buckOut;
|
||||
private final Path unpackedWar;
|
||||
|
||||
private Paths() {
|
||||
try {
|
||||
warFs = getDistributionArchive();
|
||||
if (warFs == null) {
|
||||
buckOut = getDeveloperBuckOut();
|
||||
unpackedWar = makeWarTempDir();
|
||||
} else {
|
||||
buckOut = null;
|
||||
unpackedWar = null;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new ProvisionException(
|
||||
"Error initializing static content paths", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static FileSystem getDistributionArchive() throws IOException {
|
||||
File war;
|
||||
try {
|
||||
war = GerritLauncher.getDistributionArchive();
|
||||
} catch (IOException e) {
|
||||
if ((e instanceof FileNotFoundException)
|
||||
&& GerritLauncher.NOT_ARCHIVED.equals(e.getMessage())) {
|
||||
return null;
|
||||
} else {
|
||||
ProvisionException pe =
|
||||
new ProvisionException("Error reading gerrit.war");
|
||||
pe.initCause(e);
|
||||
throw pe;
|
||||
}
|
||||
}
|
||||
return GerritLauncher.getZipFileSystem(war.toPath());
|
||||
}
|
||||
|
||||
private static Path getDeveloperBuckOut() {
|
||||
try {
|
||||
return GerritLauncher.getDeveloperBuckOut();
|
||||
} catch (FileNotFoundException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static Path makeWarTempDir() {
|
||||
// Obtain our local temporary directory, but it comes back as a file
|
||||
// so we have to switch it to be a directory post creation.
|
||||
//
|
||||
try {
|
||||
File dstwar = GerritLauncher.createTempFile("gerrit_", "war");
|
||||
if (!dstwar.delete() || !dstwar.mkdir()) {
|
||||
throw new IOException("Cannot mkdir " + dstwar.getAbsolutePath());
|
||||
}
|
||||
|
||||
// Jetty normally refuses to serve out of a symlinked directory, as
|
||||
// a security feature. Try to resolve out any symlinks in the path.
|
||||
//
|
||||
try {
|
||||
return dstwar.getCanonicalFile().toPath();
|
||||
} catch (IOException e) {
|
||||
return dstwar.getAbsoluteFile().toPath();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
ProvisionException pe =
|
||||
new ProvisionException("Error reading gerrit.war");
|
||||
new ProvisionException("Cannot create war tempdir");
|
||||
pe.initCause(e);
|
||||
throw pe;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Path getDeveloperBuckOut() {
|
||||
try {
|
||||
return GerritLauncher.getDeveloperBuckOut();
|
||||
} catch (FileNotFoundException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static Path makeWarTempDir() {
|
||||
// Obtain our local temporary directory, but it comes back as a file
|
||||
// so we have to switch it to be a directory post creation.
|
||||
//
|
||||
try {
|
||||
File dstwar = GerritLauncher.createTempFile("gerrit_", "war");
|
||||
if (!dstwar.delete() || !dstwar.mkdir()) {
|
||||
throw new IOException("Cannot mkdir " + dstwar.getAbsolutePath());
|
||||
}
|
||||
|
||||
// Jetty normally refuses to serve out of a symlinked directory, as
|
||||
// a security feature. Try to resolve out any symlinks in the path.
|
||||
//
|
||||
try {
|
||||
return dstwar.getCanonicalFile().toPath();
|
||||
} catch (IOException e) {
|
||||
return dstwar.getAbsoluteFile().toPath();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
ProvisionException pe =
|
||||
new ProvisionException("Cannot create war tempdir");
|
||||
pe.initCause(e);
|
||||
throw pe;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -38,7 +38,9 @@ import java.security.CodeSource;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.SortedMap;
|
||||
import java.util.TreeMap;
|
||||
import java.util.jar.Attributes;
|
||||
@@ -300,9 +302,10 @@ public final class GerritLauncher {
|
||||
}
|
||||
|
||||
private static volatile File myArchive;
|
||||
private static volatile FileSystem myArchiveFs;
|
||||
private static volatile File myHome;
|
||||
|
||||
private static final Map<Path, FileSystem> zipFileSystems = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Locate the JAR/WAR file we were launched from.
|
||||
*
|
||||
@@ -319,19 +322,24 @@ public final class GerritLauncher {
|
||||
return result;
|
||||
}
|
||||
result = locateMyArchive();
|
||||
myArchiveFs = FileSystems.newFileSystem(
|
||||
URI.create("jar:" + result.toPath().toUri()),
|
||||
Collections.<String, String> emptyMap());
|
||||
myArchive = result;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static FileSystem getDistributionArchiveFileSystem()
|
||||
throws FileNotFoundException, IOException {
|
||||
getDistributionArchive();
|
||||
return myArchiveFs;
|
||||
public static synchronized FileSystem getZipFileSystem(Path zip)
|
||||
throws IOException {
|
||||
// FileSystems canonicalizes the path, so we should too.
|
||||
zip = zip.toRealPath();
|
||||
FileSystem zipFs = zipFileSystems.get(zip);
|
||||
if (zipFs == null) {
|
||||
zipFs = FileSystems.newFileSystem(
|
||||
URI.create("jar:" + zip.toUri()),
|
||||
Collections.<String, String> emptyMap());
|
||||
zipFileSystems.put(zip, zipFs);
|
||||
}
|
||||
return zipFs;
|
||||
}
|
||||
|
||||
private static File locateMyArchive() throws FileNotFoundException {
|
||||
|
Reference in New Issue
Block a user