Merge "Refactor XDocLoader to make the code better readable and extensible"
diff --git a/BUCK b/BUCK
index 99a8eca..cccde03 100644
--- a/BUCK
+++ b/BUCK
@@ -2,7 +2,7 @@
MODULE = 'com.googlesource.gerrit.plugins.xdocs.XDocs'
-ASCIIDOCTOR = '//lib/asciidoctor:asciidoc_lib' if __standalone_mode__ \
+ASCIIDOCTOR = '//lib/asciidoctor:asciidoc_lib' if STANDALONE_MODE \
else '//plugins/x-docs/lib/asciidoctor:asciidoc_lib'
gerrit_plugin(
diff --git a/src/main/java/com/googlesource/gerrit/plugins/xdocs/XDocServlet.java b/src/main/java/com/googlesource/gerrit/plugins/xdocs/XDocServlet.java
index f557bee..78520f7 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/xdocs/XDocServlet.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/xdocs/XDocServlet.java
@@ -23,6 +23,7 @@
import com.google.common.net.HttpHeaders;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.IdString;
+import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.httpd.resources.Resource;
import com.google.gerrit.httpd.resources.SmallResource;
@@ -106,104 +107,52 @@
@Override
public void service(HttpServletRequest req, HttpServletResponse res)
throws IOException {
- if (!"GET".equals(req.getMethod()) && !"HEAD".equals(req.getMethod())) {
- CacheHeaders.setNotCacheable(res);
- res.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
- return;
- }
+ try {
+ validateRequestMethod(req);
- ResourceKey key = ResourceKey.fromPath(req.getPathInfo());
- ProjectState state = projectCache.get(key.project);
- if (state == null) {
- Resource.NOT_FOUND.send(req, res);
- return;
- }
- XDocProjectConfig cfg = cfgFactory.create(state);
- if (key.file == null) {
- res.sendRedirect(getRedirectUrl(req, key, cfg));
- return;
- }
+ ResourceKey key = ResourceKey.fromPath(req.getPathInfo());
+ ProjectState state = getProject(key);
+ XDocProjectConfig cfg = cfgFactory.create(state);
- MimeType mimeType = fileTypeRegistry.getMimeType(key.file, null);
- FormatterProvider formatter;
- if (req.getParameter("raw") != null) {
- formatter = formatters.getRawFormatter();
- } else {
- formatter = formatters.get(state, key.file);
- if (formatter == null
- && !("image".equals(mimeType.getMediaType())
- && fileTypeRegistry.isSafeInline(mimeType))) {
- Resource.NOT_FOUND.send(req, res);
+ if (key.file == null) {
+ res.sendRedirect(getRedirectUrl(req, key, cfg));
return;
}
- }
- try {
+ MimeType mimeType = fileTypeRegistry.getMimeType(key.file, null);
+ FormatterProvider formatter = getFormatter(req, key);
+ if (formatter == null && !isSafeImage(mimeType)) {
+ throw new ResourceNotFoundException();
+ }
+
ProjectControl projectControl = projectControlFactory.validateFor(key.project);
- String rev = key.revision;
- if (rev == null) {
- rev = cfg.getIndexRef();
- }
- if (Constants.HEAD.equals(rev)) {
- rev = getHead.get().apply(new ProjectResource(projectControl));
- } else {
- if (!ObjectId.isId(rev)) {
- if (!rev.startsWith(Constants.R_REFS)) {
- rev = Constants.R_HEADS + rev;
- }
- if (!projectControl.controlForRef(rev).isVisible()) {
- Resource.NOT_FOUND.send(req, res);
- return;
- }
- }
- }
+ String rev = getRevision(cfg, key.revision, projectControl);
Repository repo = repoManager.openRepository(key.project);
try {
- ObjectId revId =
- repo.resolve(MoreObjects.firstNonNull(rev, Constants.HEAD));
- if (revId == null) {
- Resource.NOT_FOUND.send(req, res);
- return;
- }
+ ObjectId revId = resolveRevision(repo, rev);
if (ObjectId.isId(rev)) {
- RevWalk rw = new RevWalk(repo);
- try {
- RevCommit commit = rw.parseCommit(repo.resolve(rev));
- if (!projectControl.canReadCommit(db.get(), rw, commit)) {
- Resource.NOT_FOUND.send(req, res);
- return;
- }
- } finally {
- rw.release();
- }
+ validateCanReadCommit(repo, projectControl, revId);
}
- String eTag = null;
- String receivedETag = req.getHeader(HttpHeaders.IF_NONE_MATCH);
- if (receivedETag != null) {
- eTag = computeETag(key.project, revId, key.file);
- if (eTag.equals(receivedETag)) {
- res.sendError(SC_NOT_MODIFIED);
- return;
- }
+ if (isResourceNotModified(req, key, revId)) {
+ res.sendError(SC_NOT_MODIFIED);
+ return;
}
Resource rsc;
if (formatter != null) {
rsc = docCache.get(formatter, key.project, key.file, revId);
- } else if ("image".equals(mimeType.getMediaType())) {
+ } else if (isImage(mimeType)) {
rsc = getImageResource(repo, revId, key.file);
} else {
rsc = Resource.NOT_FOUND;
}
if (rsc != Resource.NOT_FOUND) {
- res.setHeader(
- HttpHeaders.ETAG,
- MoreObjects.firstNonNull(eTag,
- computeETag(key.project, revId, key.file)));
+ res.setHeader(HttpHeaders.ETAG,
+ computeETag(key.project, revId, key.file));
}
CacheHeaders.setCacheablePrivate(res, 7, TimeUnit.DAYS, false);
rsc.send(req, res);
@@ -214,7 +163,9 @@
} catch (RepositoryNotFoundException | NoSuchProjectException
| ResourceNotFoundException | AuthException | RevisionSyntaxException e) {
Resource.NOT_FOUND.send(req, res);
- return;
+ } catch (MethodNotAllowedException e) {
+ CacheHeaders.setNotCacheable(res);
+ res.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
}
}
@@ -236,8 +187,7 @@
byte[] content = loader.getBytes(Integer.MAX_VALUE);
MimeType mimeType = fileTypeRegistry.getMimeType(file, content);
- if (!"image".equals(mimeType.getMediaType())
- || !fileTypeRegistry.isSafeInline(mimeType)) {
+ if (!isSafeImage(mimeType)) {
return Resource.NOT_FOUND;
}
return new SmallResource(content)
@@ -254,6 +204,94 @@
}
}
+ private static void validateRequestMethod(HttpServletRequest req)
+ throws MethodNotAllowedException {
+ if (!("GET".equals(req.getMethod()) || "HEAD".equals(req.getMethod()))) {
+ throw new MethodNotAllowedException();
+ }
+ }
+
+ private ProjectState getProject(ResourceKey key)
+ throws ResourceNotFoundException {
+ ProjectState state = projectCache.get(key.project);
+ if (state == null) {
+ throw new ResourceNotFoundException();
+ }
+ return state;
+ }
+
+ private FormatterProvider getFormatter(HttpServletRequest req, ResourceKey key)
+ throws ResourceNotFoundException {
+ if (req.getParameter("raw") != null) {
+ return formatters.getRawFormatter();
+ } else {
+ return formatters.get(getProject(key), key.file);
+ }
+ }
+
+ private boolean isSafeImage(MimeType mimeType) {
+ return isImage(mimeType) && fileTypeRegistry.isSafeInline(mimeType);
+ }
+
+ private static boolean isImage(MimeType mimeType) {
+ return "image".equals(mimeType.getMediaType());
+ }
+
+ private String getRevision(XDocProjectConfig cfg, String revision,
+ ProjectControl projectControl) throws ResourceNotFoundException,
+ AuthException, IOException {
+ String rev = revision;
+ if (rev == null) {
+ rev = cfg.getIndexRef();
+ }
+ if (Constants.HEAD.equals(rev)) {
+ rev = getHead.get().apply(new ProjectResource(projectControl));
+ } else {
+ if (!ObjectId.isId(rev)) {
+ if (!rev.startsWith(Constants.R_REFS)) {
+ rev = Constants.R_HEADS + rev;
+ }
+ if (!projectControl.controlForRef(rev).isVisible()) {
+ throw new ResourceNotFoundException();
+ }
+ }
+ }
+ return rev;
+ }
+
+ private static ObjectId resolveRevision(Repository repo, String revision)
+ throws ResourceNotFoundException, IOException {
+ ObjectId revId =
+ repo.resolve(MoreObjects.firstNonNull(revision, Constants.HEAD));
+ if (revId == null) {
+ throw new ResourceNotFoundException();
+ }
+ return revId;
+ }
+
+ private void validateCanReadCommit(Repository repo,
+ ProjectControl projectControl, ObjectId revId)
+ throws ResourceNotFoundException, IOException {
+ RevWalk rw = new RevWalk(repo);
+ try {
+ RevCommit commit = rw.parseCommit(revId);
+ if (!projectControl.canReadCommit(db.get(), rw, commit)) {
+ throw new ResourceNotFoundException();
+ }
+ } finally {
+ rw.release();
+ }
+ }
+
+ private static boolean isResourceNotModified(HttpServletRequest req,
+ ResourceKey key, ObjectId revId) {
+ String receivedETag = req.getHeader(HttpHeaders.IF_NONE_MATCH);
+ if (receivedETag != null) {
+ return receivedETag.equals(computeETag(key.project, revId, key.file));
+ }
+ return false;
+ }
+
private static String computeETag(Project.NameKey project, ObjectId revId,
String file) {
return Hashing.md5().newHasher()
diff --git a/src/main/java/com/googlesource/gerrit/plugins/xdocs/client/XDocScreen.java b/src/main/java/com/googlesource/gerrit/plugins/xdocs/client/XDocScreen.java
index 92fa3ee..0bfa9c0 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/xdocs/client/XDocScreen.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/xdocs/client/XDocScreen.java
@@ -53,8 +53,11 @@
setStyleName("xdocs-panel");
HorizontalPanel p = new HorizontalPanel();
+ p.setStyleName("xdocs-header");
p.add(new InlineHyperlink(projectName, "/admin/projects/" + projectName));
- p.add(new Label(" / " + fileName + " (" + revision + ")"));
+ p.add(new Label("/"));
+ p.add(new Label(fileName));
+ p.add(new Label("(" + revision + ")"));
add(p);
final String url = getUrl(projectName, revision, fileName);
diff --git a/src/main/java/com/googlesource/gerrit/plugins/xdocs/public/xdocs.css b/src/main/java/com/googlesource/gerrit/plugins/xdocs/public/xdocs.css
index cf984d4..42471c7 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/xdocs/public/xdocs.css
+++ b/src/main/java/com/googlesource/gerrit/plugins/xdocs/public/xdocs.css
@@ -3,8 +3,14 @@
width: 100%;
}
+.xdocs-header td {
+ padding-right: 5px;
+}
+
.xdocs-panel iframe {
width: 100%;
+ border-width: 1px;
+ border-style: solid;
}
.xdocs-error {