blob: 42062f76ca218ba0d3a9b837ee0d426a58f374ff [file] [log] [blame]
// Copyright (C) 2015 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.httpd.raw;
import static com.google.common.base.Preconditions.checkArgument;
import com.google.common.cache.Cache;
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;
import com.google.inject.Singleton;
import com.google.inject.name.Named;
import com.google.inject.name.Names;
import com.google.inject.servlet.ServletModule;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.file.FileSystem;
import java.nio.file.Path;
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";
public static final String CACHE = "static_content";
private final GerritOptions options;
private Paths paths;
@Inject
public StaticModule(GerritOptions options) {
this.options = options;
}
private Paths getPaths() {
if (paths == null) {
paths = new Paths();
}
return paths;
}
@Override
protected void configureServlets() {
serve("/static/*").with(SiteStaticDirectoryServlet.class);
install(new CacheModule() {
@Override
protected void configure() {
cache(CACHE, Path.class, Resource.class)
.maximumWeight(1 << 20)
.weigher(ResourceServlet.Weigher.class);
}
});
if (options.enablePolyGerrit()) {
install(new PolyGerritUiModule());
} else if (options.enableDefaultUi()) {
install(new GwtUiModule());
}
}
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));
}
}
@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();
boolean forceDev = options.forcePolyGerritDev();
if (forceDev) {
checkArgument(p.buckOut != null,
"no buck-out directory found for PolyGerrit developer mode");
}
return forceDev || p.warFs == null
? p.buckOut.getParent().resolve("polygerrit-ui").resolve("app")
: p.warFs.getPath("/polygerrit_ui");
}
}
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("Cannot create war tempdir");
pe.initCause(e);
throw pe;
}
}
}
}