diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/StaticModule.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/StaticModule.java index 8859f84c79..d8df39c9bf 100644 --- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/StaticModule.java +++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/StaticModule.java @@ -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 cache) - throws IOException { - if (warFs != null) { - return new WarGwtUiServlet(cache, warFs); - } else { - return new DeveloperGwtUiServlet(cache, unpackedWar); - } - } - - @Provides - @Singleton - PolyGerritUiIndexServlet getPolyGerritUiIndexServlet( - @Named(CACHE) Cache cache) { - return new PolyGerritUiIndexServlet(cache, polyGerritBasePath()); - } - - @Provides - @Singleton - PolyGerritUiServlet getPolyGerritUiServlet( - @Named(CACHE) Cache cache) { - return new PolyGerritUiServlet(cache, polyGerritBasePath()); - } - - @Provides - @Singleton - @Named(BOWER_SERVLET) - PolyGerritUiServlet getPolyGerritUiBowerServlet( - @Named(CACHE) Cache 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 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 cache) { + return new PolyGerritUiIndexServlet(cache, polyGerritBasePath()); + } + + @Provides + @Singleton + PolyGerritUiServlet getPolyGerritUiServlet( + @Named(CACHE) Cache cache) { + return new PolyGerritUiServlet(cache, polyGerritBasePath()); + } + + @Provides + @Singleton + @Named(BOWER_SERVLET) + PolyGerritUiServlet getPolyGerritUiBowerServlet( + @Named(CACHE) Cache 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; - } - } } diff --git a/gerrit-launcher/src/main/java/com/google/gerrit/launcher/GerritLauncher.java b/gerrit-launcher/src/main/java/com/google/gerrit/launcher/GerritLauncher.java index fb54bcffd2..adc3c79447 100644 --- a/gerrit-launcher/src/main/java/com/google/gerrit/launcher/GerritLauncher.java +++ b/gerrit-launcher/src/main/java/com/google/gerrit/launcher/GerritLauncher.java @@ -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 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. 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. emptyMap()); + zipFileSystems.put(zip, zipFs); + } + return zipFs; } private static File locateMyArchive() throws FileNotFoundException {