Merge "Assign selected revision correctly if current patch is edit"
diff --git a/java/com/google/gerrit/httpd/restapi/RestApiServlet.java b/java/com/google/gerrit/httpd/restapi/RestApiServlet.java
index b8ecb23..fad3f91 100644
--- a/java/com/google/gerrit/httpd/restapi/RestApiServlet.java
+++ b/java/com/google/gerrit/httpd/restapi/RestApiServlet.java
@@ -52,6 +52,7 @@
 import com.google.common.base.Joiner;
 import com.google.common.base.Splitter;
 import com.google.common.base.Strings;
+import com.google.common.base.Throwables;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableListMultimap;
 import com.google.common.collect.ImmutableSet;
@@ -492,7 +493,7 @@
             checkRequiresCapability(viewData);
           }
 
-          if (notModified(req, rsrc, viewData.view)) {
+          if (notModified(req, traceContext, viewData, rsrc)) {
             logger.atFinest().log("REST call succeeded: %d", SC_NOT_MODIFIED);
             res.sendError(SC_NOT_MODIFIED);
             return;
@@ -586,7 +587,7 @@
           }
 
           status = response.statusCode();
-          configureCaching(req, res, rsrc, viewData.view, response.caching());
+          configureCaching(req, res, traceContext, rsrc, viewData, response.caching());
           res.setStatus(status);
           logger.atFinest().log("REST call succeeded: %d", status);
         }
@@ -711,6 +712,40 @@
     }
   }
 
+  private String getEtagWithRetry(
+      HttpServletRequest req,
+      TraceContext traceContext,
+      ViewData viewData,
+      ETagView<RestResource> view,
+      RestResource rsrc) {
+    try {
+      return invokeRestEndpointWithRetry(
+          req,
+          traceContext,
+          getViewName(viewData) + "#etag",
+          ActionType.REST_READ_REQUEST,
+          () -> view.getETag(rsrc));
+    } catch (Exception e) {
+      Throwables.throwIfUnchecked(e);
+      throw new IllegalStateException("Failed to get ETag for view", e);
+    }
+  }
+
+  private String getEtagWithRetry(
+      HttpServletRequest req, TraceContext traceContext, RestResource.HasETag rsrc) {
+    try {
+      return invokeRestEndpointWithRetry(
+          req,
+          traceContext,
+          rsrc.getClass().getSimpleName() + "#etag",
+          ActionType.REST_READ_REQUEST,
+          () -> rsrc.getETag());
+    } catch (Exception e) {
+      Throwables.throwIfUnchecked(e);
+      throw new IllegalStateException("Failed to get ETag for resource", e);
+    }
+  }
+
   private RestResource parseResourceWithRetry(
       HttpServletRequest req,
       TraceContext traceContext,
@@ -962,24 +997,27 @@
     return defaultMessage;
   }
 
-  @SuppressWarnings({"unchecked", "rawtypes"})
-  private static boolean notModified(
-      HttpServletRequest req, RestResource rsrc, RestView<RestResource> view) {
+  private boolean notModified(
+      HttpServletRequest req, TraceContext traceContext, ViewData viewData, RestResource rsrc) {
     if (!isRead(req)) {
       return false;
     }
 
+    RestView<RestResource> view = viewData.view;
     if (view instanceof ETagView) {
       String have = req.getHeader(HttpHeaders.IF_NONE_MATCH);
       if (have != null) {
-        return have.equals(((ETagView) view).getETag(rsrc));
+        String eTag =
+            getEtagWithRetry(req, traceContext, viewData, (ETagView<RestResource>) view, rsrc);
+        return have.equals(eTag);
       }
     }
 
     if (rsrc instanceof RestResource.HasETag) {
       String have = req.getHeader(HttpHeaders.IF_NONE_MATCH);
       if (have != null) {
-        return have.equals(((RestResource.HasETag) rsrc).getETag());
+        String eTag = getEtagWithRetry(req, traceContext, (RestResource.HasETag) rsrc);
+        return have.equals(eTag);
       }
     }
 
@@ -993,21 +1031,48 @@
     return false;
   }
 
-  private static <R extends RestResource> void configureCaching(
-      HttpServletRequest req, HttpServletResponse res, R rsrc, RestView<R> view, CacheControl c) {
+  private <R extends RestResource> void configureCaching(
+      HttpServletRequest req,
+      HttpServletResponse res,
+      TraceContext traceContext,
+      R rsrc,
+      ViewData viewData,
+      CacheControl cacheControl) {
+    setCacheHeaders(req, res, cacheControl);
     if (isRead(req)) {
-      switch (c.getType()) {
+      switch (cacheControl.getType()) {
+        case NONE:
+        default:
+          break;
+        case PRIVATE:
+          addResourceStateHeaders(req, res, traceContext, viewData, rsrc);
+          break;
+        case PUBLIC:
+          addResourceStateHeaders(req, res, traceContext, viewData, rsrc);
+          break;
+      }
+    }
+  }
+
+  private static <R extends RestResource> void setCacheHeaders(
+      HttpServletRequest req, HttpServletResponse res, CacheControl cacheControl) {
+    if (isRead(req)) {
+      switch (cacheControl.getType()) {
         case NONE:
         default:
           CacheHeaders.setNotCacheable(res);
           break;
         case PRIVATE:
-          addResourceStateHeaders(res, rsrc, view);
-          CacheHeaders.setCacheablePrivate(res, c.getAge(), c.getUnit(), c.isMustRevalidate());
+          CacheHeaders.setCacheablePrivate(
+              res, cacheControl.getAge(), cacheControl.getUnit(), cacheControl.isMustRevalidate());
           break;
         case PUBLIC:
-          addResourceStateHeaders(res, rsrc, view);
-          CacheHeaders.setCacheable(req, res, c.getAge(), c.getUnit(), c.isMustRevalidate());
+          CacheHeaders.setCacheable(
+              req,
+              res,
+              cacheControl.getAge(),
+              cacheControl.getUnit(),
+              cacheControl.isMustRevalidate());
           break;
       }
     } else {
@@ -1015,12 +1080,20 @@
     }
   }
 
-  private static <R extends RestResource> void addResourceStateHeaders(
-      HttpServletResponse res, R rsrc, RestView<R> view) {
+  private void addResourceStateHeaders(
+      HttpServletRequest req,
+      HttpServletResponse res,
+      TraceContext traceContext,
+      ViewData viewData,
+      RestResource rsrc) {
+    RestView<RestResource> view = viewData.view;
     if (view instanceof ETagView) {
-      res.setHeader(HttpHeaders.ETAG, ((ETagView<R>) view).getETag(rsrc));
+      String eTag =
+          getEtagWithRetry(req, traceContext, viewData, (ETagView<RestResource>) view, rsrc);
+      res.setHeader(HttpHeaders.ETAG, eTag);
     } else if (rsrc instanceof RestResource.HasETag) {
-      res.setHeader(HttpHeaders.ETAG, ((RestResource.HasETag) rsrc).getETag());
+      String eTag = getEtagWithRetry(req, traceContext, (RestResource.HasETag) rsrc);
+      res.setHeader(HttpHeaders.ETAG, eTag);
     }
     if (rsrc instanceof RestResource.HasLastModified) {
       res.setDateHeader(
@@ -1724,13 +1797,13 @@
       HttpServletResponse res,
       int statusCode,
       String msg,
-      CacheControl c,
+      CacheControl cacheControl,
       @Nullable Throwable err)
       throws IOException {
     if (err != null) {
       RequestUtil.setErrorTraceAttribute(req, err);
     }
-    configureCaching(req, res, null, null, c);
+    setCacheHeaders(req, res, cacheControl);
     checkArgument(statusCode >= 400, "non-error status: %s", statusCode);
     res.setStatus(statusCode);
     logger.atFinest().withCause(err).log("REST call failed: %d", statusCode);