Merge changes from topic 'static-module'

* changes:
  Hoist StaticModule out of WebModule
  StaticModule: Lazily initialize zip FileSystem
This commit is contained in:
Dave Borowitz
2015-11-19 17:38:44 +00:00
committed by Gerrit Code Review
6 changed files with 175 additions and 135 deletions

View File

@@ -23,7 +23,6 @@ import com.google.gerrit.httpd.raw.HostPageServlet;
import com.google.gerrit.httpd.raw.LegacyGerritServlet;
import com.google.gerrit.httpd.raw.RobotsServlet;
import com.google.gerrit.httpd.raw.SshInfoServlet;
import com.google.gerrit.httpd.raw.StaticModule;
import com.google.gerrit.httpd.raw.ToolServlet;
import com.google.gerrit.httpd.rpc.access.AccessRestApiServlet;
import com.google.gerrit.httpd.rpc.account.AccountsRestApiServlet;
@@ -106,9 +105,6 @@ class UrlModule extends ServletModule {
filter("/Documentation/").through(QueryDocumentationFilter.class);
serve("/robots.txt").with(RobotsServlet.class);
// Static paths are bound last, since they may include a wildcard for /*.
install(new StaticModule(options));
}
private Key<HttpServlet> notFound() {

View File

@@ -285,7 +285,7 @@ public abstract class ResourceServlet extends HttpServlet {
};
}
static class Resource {
public static class Resource {
static final Resource NOT_FOUND =
new Resource(FileTime.fromMillis(0), "", new byte[] {});

View File

@@ -19,6 +19,7 @@ import com.google.gerrit.httpd.GerritOptions;
import com.google.gerrit.httpd.raw.ResourceServlet.Resource;
import com.google.gerrit.launcher.GerritLauncher;
import com.google.gerrit.server.cache.CacheModule;
import com.google.inject.Inject;
import com.google.inject.Key;
import com.google.inject.Provides;
import com.google.inject.ProvisionException;
@@ -38,23 +39,21 @@ import javax.servlet.http.HttpServlet;
public class StaticModule extends ServletModule {
private static final String GWT_UI_SERVLET = "GwtUiServlet";
private static final String BOWER_SERVLET = "BowerServlet";
static final String CACHE = "static_content";
public 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;
@Inject
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 +68,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;
}
}
}

View File

@@ -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 {

View File

@@ -35,6 +35,7 @@ import com.google.gerrit.httpd.WebSshGlueModule;
import com.google.gerrit.httpd.auth.oauth.OAuthModule;
import com.google.gerrit.httpd.auth.openid.OpenIdModule;
import com.google.gerrit.httpd.plugins.HttpPluginModule;
import com.google.gerrit.httpd.raw.StaticModule;
import com.google.gerrit.lifecycle.LifecycleManager;
import com.google.gerrit.lucene.LuceneIndexModule;
import com.google.gerrit.metrics.dropwizard.DropWizardMetricMaker;
@@ -451,6 +452,7 @@ public class Daemon extends SiteProgram {
modules.add(H2CacheBasedWebSession.module());
modules.add(sysInjector.getInstance(GitOverHttpModule.class));
modules.add(sysInjector.getInstance(WebModule.class));
modules.add(sysInjector.getInstance(StaticModule.class));
modules.add(sysInjector.getInstance(RequireSslFilter.Module.class));
modules.add(new HttpPluginModule());
if (sshd) {

View File

@@ -23,6 +23,7 @@ import com.google.gerrit.gpg.GpgModule;
import com.google.gerrit.httpd.auth.oauth.OAuthModule;
import com.google.gerrit.httpd.auth.openid.OpenIdModule;
import com.google.gerrit.httpd.plugins.HttpPluginModule;
import com.google.gerrit.httpd.raw.StaticModule;
import com.google.gerrit.lifecycle.LifecycleManager;
import com.google.gerrit.lifecycle.LifecycleModule;
import com.google.gerrit.lucene.LuceneIndexModule;
@@ -354,6 +355,7 @@ public class WebAppInitializer extends GuiceServletContextListener
modules.add(RequestMetricsFilter.module());
modules.add(sysInjector.getInstance(GitOverHttpModule.class));
modules.add(sysInjector.getInstance(WebModule.class));
modules.add(sysInjector.getInstance(StaticModule.class));
modules.add(sysInjector.getInstance(RequireSslFilter.Module.class));
if (sshInjector != null) {
modules.add(sshInjector.getInstance(WebSshGlueModule.class));