Remove gwtjsonrpc types from new REST API

Only rely on native GWT supplied interfaces like the GWT version of
AsyncCallback and StatusCodeException. This allows us to later think
about dropping gwtjsonrpc as a project dependency once everything has
been converted and no more JSON-RPC 2.0 interfaces exist.

Given the size of this change, its better to do it before too many
more REST style interfaces are written and depend upon gwtjsonrpc.

Change-Id: I7a9f8b73c3612bf7a55a7ec5f82165f8a5cd7107
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/Dispatcher.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/Dispatcher.java
index 6989339..0229d38 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/Dispatcher.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/Dispatcher.java
@@ -17,8 +17,8 @@
 import static com.google.gerrit.common.PageLinks.ADMIN_CREATE_GROUP;
 import static com.google.gerrit.common.PageLinks.ADMIN_CREATE_PROJECT;
 import static com.google.gerrit.common.PageLinks.ADMIN_GROUPS;
-import static com.google.gerrit.common.PageLinks.ADMIN_PROJECTS;
 import static com.google.gerrit.common.PageLinks.ADMIN_PLUGINS;
+import static com.google.gerrit.common.PageLinks.ADMIN_PROJECTS;
 import static com.google.gerrit.common.PageLinks.DASHBOARDS;
 import static com.google.gerrit.common.PageLinks.MINE;
 import static com.google.gerrit.common.PageLinks.PROJECTS;
@@ -74,6 +74,7 @@
 import com.google.gerrit.client.dashboards.DashboardList;
 import com.google.gerrit.client.patches.PatchScreen;
 import com.google.gerrit.client.rpc.GerritCallback;
+import com.google.gerrit.client.rpc.RestApi;
 import com.google.gerrit.client.ui.Screen;
 import com.google.gerrit.common.PageLinks;
 import com.google.gerrit.common.auth.SignInMode;
@@ -89,7 +90,6 @@
 import com.google.gwt.core.client.RunAsyncCallback;
 import com.google.gwt.http.client.URL;
 import com.google.gwt.user.client.Window;
-import com.google.gwtjsonrpc.client.RemoteJsonException;
 import com.google.gwtorm.client.KeyUtil;
 
 public class Dispatcher {
@@ -416,9 +416,7 @@
 
           @Override
           public void onFailure(Throwable caught) {
-            if ("default".equals(dashboardId)
-                && caught instanceof RemoteJsonException
-                && ((RemoteJsonException) caught).getCode() == 404) {
+            if ("default".equals(dashboardId) && RestApi.isNotFound(caught)) {
               Gerrit.display(PageLinks.toChangeQuery(
                   PageLinks.projectQuery(new Project.NameKey(project))));
             } else {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ErrorDialog.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ErrorDialog.java
index 13bba12..3adec8f 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ErrorDialog.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ErrorDialog.java
@@ -14,11 +14,13 @@
 
 package com.google.gerrit.client;
 
+import com.google.gerrit.client.rpc.RestApi;
 import com.google.gerrit.client.rpc.RpcConstants;
 import com.google.gwt.event.dom.client.ClickEvent;
 import com.google.gwt.event.dom.client.ClickHandler;
 import com.google.gwt.event.dom.client.KeyPressEvent;
 import com.google.gwt.event.dom.client.KeyPressHandler;
+import com.google.gwt.http.client.Response;
 import com.google.gwt.user.client.DOM;
 import com.google.gwt.user.client.Window;
 import com.google.gwt.user.client.rpc.StatusCodeException;
@@ -104,34 +106,54 @@
   public ErrorDialog(final Throwable what) {
     this();
 
-    String cn;
-    if (what instanceof RemoteJsonException) {
-      cn = RpcConstants.C.errorRemoteJsonException();
+    String hdr;
+    String msg;
 
-    } else if (what instanceof StatusCodeException) {
-      cn = RpcConstants.C.errorServerUnavailable();
+    if (what instanceof StatusCodeException) {
+      StatusCodeException sc = (StatusCodeException) what;
+      if (RestApi.isExpected(sc.getStatusCode())) {
+        hdr = null;
+        msg = sc.getEncodedResponse();
+      } else if (sc.getStatusCode() == Response.SC_INTERNAL_SERVER_ERROR) {
+        hdr = null;
+        msg = what.getMessage();
+      } else {
+        hdr = RpcConstants.C.errorServerUnavailable();
+        msg = what.getMessage();
+      }
+
+    } else if (what instanceof RemoteJsonException) {
+      // TODO Remove RemoteJsonException from Gerrit sources.
+      hdr = RpcConstants.C.errorRemoteJsonException();
+      msg = what.getMessage();
 
     } else {
-      cn = what.getClass().getName();
-      if (cn.startsWith("java.lang.")) {
-        cn = cn.substring("java.lang.".length());
-      } else if (cn.startsWith("com.google.gerrit.")) {
-        cn = cn.substring(cn.lastIndexOf('.') + 1);
+      // TODO Fix callers of ErrorDialog to stop passing random types.
+      hdr = what.getClass().getName();
+      if (hdr.startsWith("java.lang.")) {
+        hdr = hdr.substring("java.lang.".length());
+      } else if (hdr.startsWith("com.google.gerrit.")) {
+        hdr = hdr.substring(hdr.lastIndexOf('.') + 1);
       }
-      if (cn.endsWith("Exception")) {
-        cn = cn.substring(0, cn.length() - "Exception".length());
-      } else if (cn.endsWith("Error")) {
-        cn = cn.substring(0, cn.length() - "Error".length());
+      if (hdr.endsWith("Exception")) {
+        hdr = hdr.substring(0, hdr.length() - "Exception".length());
+      } else if (hdr.endsWith("Error")) {
+        hdr = hdr.substring(0, hdr.length() - "Error".length());
       }
+      msg = what.getMessage();
     }
 
-    final Label r = new Label(cn);
-    r.setStyleName(Gerrit.RESOURCES.css().errorDialogErrorType());
-    body.add(r);
+    if (hdr != null) {
+      final Label r = new Label(hdr);
+      r.setStyleName(Gerrit.RESOURCES.css().errorDialogErrorType());
+      body.add(r);
+    }
 
-    final Label m = new Label(what.getMessage());
-    DOM.setStyleAttribute(m.getElement(),"whiteSpace","pre");
-    body.add(m);
+    if (msg != null) {
+      final Label m = new Label(msg);
+      DOM.setStyleAttribute(m.getElement(), "whiteSpace", "pre");
+      body.add(m);
+    }
   }
 
   public void setText(final String t) {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritConstants.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritConstants.properties
index 4176051..3eee2be 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritConstants.properties
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritConstants.properties
@@ -11,7 +11,7 @@
 registerDialogTitle = Code Review - Register New Account
 loginTypeUnsupported = Sign in is not available.
 
-errorDialogTitle = Application Error
+errorDialogTitle = Code Review - Error
 errorDialogContinue = Continue
 
 confirmationDialogOk = OK
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountCapabilities.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountCapabilities.java
index 4691e24..42399ee 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountCapabilities.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountCapabilities.java
@@ -16,7 +16,7 @@
 
 import com.google.gerrit.client.rpc.RestApi;
 import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwtjsonrpc.common.AsyncCallback;
+import com.google.gwt.user.client.rpc.AsyncCallback;
 
 /** Capabilities the caller has from {@code /accounts/self/capabilities}.  */
 public class AccountCapabilities extends JavaScriptObject {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeApi.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeApi.java
index 7db32ad..2ac2426 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeApi.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeApi.java
@@ -17,7 +17,7 @@
 import com.google.gerrit.client.rpc.NativeString;
 import com.google.gerrit.client.rpc.RestApi;
 import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwtjsonrpc.common.AsyncCallback;
+import com.google.gwt.user.client.rpc.AsyncCallback;
 
 /**
  * A collection of static methods which work on the Gerrit REST API for specific
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeInfoBlock.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeInfoBlock.java
index 5448aee..5e2eda4 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeInfoBlock.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeInfoBlock.java
@@ -16,11 +16,11 @@
 
 import static com.google.gerrit.client.FormatUtil.mediumFormat;
 
-import com.google.gerrit.client.ErrorDialog;
 import com.google.gerrit.client.Gerrit;
+import com.google.gerrit.client.rpc.GerritCallback;
 import com.google.gerrit.client.ui.AccountLink;
-import com.google.gerrit.client.ui.CommentedActionDialog;
 import com.google.gerrit.client.ui.BranchLink;
+import com.google.gerrit.client.ui.CommentedActionDialog;
 import com.google.gerrit.client.ui.ProjectLink;
 import com.google.gerrit.common.PageLinks;
 import com.google.gerrit.common.data.AccountInfoCache;
@@ -39,7 +39,6 @@
 import com.google.gwt.user.client.ui.TextBox;
 import com.google.gwt.user.client.ui.Widget;
 import com.google.gwtexpui.clippy.client.CopyableLabel;
-import com.google.gwtjsonrpc.common.AsyncCallback;
 
 public class ChangeInfoBlock extends Composite {
   private static final int R_CHANGE_ID = 0;
@@ -187,7 +186,7 @@
     public void onSend() {
       String topic = newTopic.getText();
       ChangeApi.topic(change.getId().get(), topic, getMessageText(),
-        new AsyncCallback<String>() {
+        new GerritCallback<String>() {
         @Override
         public void onSuccess(String result) {
           sent = true;
@@ -198,7 +197,7 @@
         @Override
         public void onFailure(final Throwable caught) {
           enableButtons(true);
-          new ErrorDialog(caught.getMessage()).center();
+          super.onFailure(caught);
         }});
     }
   }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeList.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeList.java
index 3cb72a1..23a3c0f 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeList.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeList.java
@@ -17,7 +17,7 @@
 import com.google.gerrit.client.rpc.NativeList;
 import com.google.gerrit.client.rpc.RestApi;
 import com.google.gerrit.common.changes.ListChangesOption;
-import com.google.gwtjsonrpc.common.AsyncCallback;
+import com.google.gwt.user.client.rpc.AsyncCallback;
 import com.google.gwtorm.client.KeyUtil;
 
 import java.util.EnumSet;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PagedSingleListScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PagedSingleListScreen.java
index 23ce178..0cbae18 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PagedSingleListScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PagedSingleListScreen.java
@@ -21,7 +21,7 @@
 import com.google.gerrit.reviewdb.client.AccountGeneralPreferences;
 import com.google.gwt.event.dom.client.KeyPressEvent;
 import com.google.gwt.user.client.History;
-import com.google.gwtjsonrpc.common.AsyncCallback;
+import com.google.gwt.user.client.rpc.AsyncCallback;
 import com.google.gwt.user.client.ui.HorizontalPanel;
 import com.google.gwtexpui.globalkey.client.KeyCommand;
 
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchSetComplexDisclosurePanel.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchSetComplexDisclosurePanel.java
index 97891bf..2872eff 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchSetComplexDisclosurePanel.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchSetComplexDisclosurePanel.java
@@ -15,7 +15,6 @@
 package com.google.gerrit.client.changes;
 
 import com.google.gerrit.client.Dispatcher;
-import com.google.gerrit.client.ErrorDialog;
 import com.google.gerrit.client.FormatUtil;
 import com.google.gerrit.client.Gerrit;
 import com.google.gerrit.client.GitwebLink;
@@ -49,7 +48,6 @@
 import com.google.gwt.user.client.ui.HTMLTable.CellFormatter;
 import com.google.gwt.user.client.ui.InlineLabel;
 import com.google.gwt.user.client.ui.Panel;
-import com.google.gwtjsonrpc.common.AsyncCallback;
 import com.google.gwtjsonrpc.common.VoidResult;
 
 import java.util.HashSet;
@@ -355,7 +353,7 @@
             @Override
             public void onSend() {
               ChangeApi.revert(changeDetail.getChange().getChangeId(),
-                  getMessageText(), new AsyncCallback<ChangeInfo>() {
+                  getMessageText(), new GerritCallback<ChangeInfo>() {
                     @Override
                     public void onSuccess(ChangeInfo result) {
                       sent = true;
@@ -367,7 +365,7 @@
                     @Override
                     public void onFailure(Throwable caught) {
                       enableButtons(true);
-                      new ErrorDialog(caught.getMessage()).center();
+                      super.onFailure(caught);
                     }
                   });
             }
@@ -395,7 +393,7 @@
               // REST APIs, we can use createCallback() rather than providing
               // them directly.
               ChangeApi.abandon(changeDetail.getChange().getChangeId(),
-                  getMessageText(), new AsyncCallback<ChangeInfo>() {
+                  getMessageText(), new GerritCallback<ChangeInfo>() {
                     @Override
                     public void onSuccess(ChangeInfo result) {
                       sent = true;
@@ -407,7 +405,7 @@
                     @Override
                     public void onFailure(Throwable caught) {
                       enableButtons(true);
-                      new ErrorDialog(caught.getMessage()).center();
+                      super.onFailure(caught);
                     }
                   });
             }
@@ -456,7 +454,7 @@
             @Override
             public void onSend() {
               ChangeApi.restore(changeDetail.getChange().getChangeId(),
-                  getMessageText(), new AsyncCallback<ChangeInfo>() {
+                  getMessageText(), new GerritCallback<ChangeInfo>() {
                     @Override
                     public void onSuccess(ChangeInfo result) {
                       sent = true;
@@ -468,7 +466,7 @@
                     @Override
                     public void onFailure(Throwable caught) {
                       enableButtons(true);
-                      new ErrorDialog(caught.getMessage()).center();
+                      super.onFailure(caught);
                     }
                   });
             }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/QueryScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/QueryScreen.java
index bc95ffa..220083e 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/QueryScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/QueryScreen.java
@@ -19,7 +19,7 @@
 import com.google.gerrit.common.PageLinks;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.RevId;
-import com.google.gwtjsonrpc.common.AsyncCallback;
+import com.google.gwt.user.client.rpc.AsyncCallback;
 import com.google.gwtorm.client.KeyUtil;
 
 public class QueryScreen extends PagedSingleListScreen implements
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/dashboards/DashboardList.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/dashboards/DashboardList.java
index 9725ef0..f4da5fa 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/dashboards/DashboardList.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/dashboards/DashboardList.java
@@ -18,7 +18,7 @@
 import com.google.gerrit.client.rpc.RestApi;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gwt.http.client.URL;
-import com.google.gwtjsonrpc.common.AsyncCallback;
+import com.google.gwt.user.client.rpc.AsyncCallback;
 
 /** Project dashboards from {@code /projects/<name>/dashboards/}. */
 public class DashboardList extends NativeList<DashboardInfo> {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/plugins/PluginMap.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/plugins/PluginMap.java
index 0251a2e..e4c5159 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/plugins/PluginMap.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/plugins/PluginMap.java
@@ -16,7 +16,7 @@
 
 import com.google.gerrit.client.rpc.NativeMap;
 import com.google.gerrit.client.rpc.RestApi;
-import com.google.gwtjsonrpc.common.AsyncCallback;
+import com.google.gwt.user.client.rpc.AsyncCallback;
 
 /** Plugins available from {@code /plugins/}. */
 public class PluginMap extends NativeMap<PluginInfo> {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ProjectMap.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ProjectMap.java
index d441f0a..7ce8912 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ProjectMap.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ProjectMap.java
@@ -16,7 +16,7 @@
 
 import com.google.gerrit.client.rpc.NativeMap;
 import com.google.gerrit.client.rpc.RestApi;
-import com.google.gwtjsonrpc.common.AsyncCallback;
+import com.google.gwt.user.client.rpc.AsyncCallback;
 
 /** Projects available from {@code /projects/}. */
 public class ProjectMap extends NativeMap<ProjectInfo> {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/GerritCallback.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/GerritCallback.java
index dce5bb6..01bdbf3 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/GerritCallback.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/GerritCallback.java
@@ -24,14 +24,17 @@
 import com.google.gerrit.common.errors.NoSuchGroupException;
 import com.google.gerrit.common.errors.NotSignedInException;
 import com.google.gwt.core.client.GWT;
-import com.google.gwtjsonrpc.common.AsyncCallback;
+import com.google.gwt.http.client.Response;
 import com.google.gwt.user.client.rpc.InvocationException;
+import com.google.gwt.user.client.rpc.StatusCodeException;
 import com.google.gwtjsonrpc.client.RemoteJsonException;
 import com.google.gwtjsonrpc.client.ServerUnavailableException;
 import com.google.gwtjsonrpc.common.JsonConstants;
 
 /** Abstract callback handling generic error conditions automatically */
-public abstract class GerritCallback<T> implements AsyncCallback<T> {
+public abstract class GerritCallback<T> implements
+    com.google.gwtjsonrpc.common.AsyncCallback<T>,
+    com.google.gwt.user.client.rpc.AsyncCallback<T> {
   public void onFailure(final Throwable caught) {
     if (isNotSignedIn(caught) || isInvalidXSRF(caught)) {
       new NotSignedInDialog().center();
@@ -76,14 +79,16 @@
         && caught.getMessage().equals(JsonConstants.ERROR_INVALID_XSRF);
   }
 
-  private static boolean isNotSignedIn(final Throwable caught) {
-    return caught instanceof RemoteJsonException
-        && caught.getMessage().equals(NotSignedInException.MESSAGE);
+  private static boolean isNotSignedIn(Throwable caught) {
+    return RestApi.isNotSignedIn(caught)
+        || (caught instanceof RemoteJsonException
+           && caught.getMessage().equals(NotSignedInException.MESSAGE));
   }
 
-  protected static boolean isNoSuchEntity(final Throwable caught) {
-    return caught instanceof RemoteJsonException
-        && caught.getMessage().equals(NoSuchEntityException.MESSAGE);
+  protected static boolean isNoSuchEntity(Throwable caught) {
+    return RestApi.isNotFound(caught)
+        || (caught instanceof RemoteJsonException
+            && caught.getMessage().equals(NoSuchEntityException.MESSAGE));
   }
 
   protected static boolean isInactiveAccount(final Throwable caught) {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/NativeMap.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/NativeMap.java
index cde9041..54866a7 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/NativeMap.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/NativeMap.java
@@ -15,7 +15,7 @@
 package com.google.gerrit.client.rpc;
 
 import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwtjsonrpc.common.AsyncCallback;
+import com.google.gwt.user.client.rpc.AsyncCallback;
 
 import java.util.Set;
 
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/NativeString.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/NativeString.java
index b31f9e7..4ea7121 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/NativeString.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/NativeString.java
@@ -15,7 +15,7 @@
 package com.google.gerrit.client.rpc;
 
 import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwtjsonrpc.common.AsyncCallback;
+import com.google.gwt.user.client.rpc.AsyncCallback;
 
 /** Wraps a String that was returned from a JSON API. */
 public final class NativeString extends JavaScriptObject {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/RestApi.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/RestApi.java
index 1764047..4cbeb9a 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/RestApi.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/RestApi.java
@@ -34,14 +34,16 @@
 import com.google.gwt.json.client.JSONObject;
 import com.google.gwt.json.client.JSONParser;
 import com.google.gwt.json.client.JSONValue;
+import com.google.gwt.user.client.rpc.AsyncCallback;
 import com.google.gwt.user.client.rpc.StatusCodeException;
-import com.google.gwtjsonrpc.client.RemoteJsonException;
-import com.google.gwtjsonrpc.client.ServerUnavailableException;
-import com.google.gwtjsonrpc.common.AsyncCallback;
-import com.google.gwtjsonrpc.common.JsonConstants;
 
 /** Makes a REST API call to the server. */
 public class RestApi {
+  private static final int SC_UNAVAILABLE = 2;
+  private static final int SC_TRANSPORT = 3;
+  private static final String JSON_TYPE = "application/json";
+  private static final String TEXT_TYPE = "text/plain";
+
   /**
    * Expected JSON content body prefix that prevents XSSI.
    * <p>
@@ -53,8 +55,53 @@
    */
   private static final String JSON_MAGIC = ")]}'\n";
 
-  private class MyRequestCallback<T extends JavaScriptObject> implements
-      RequestCallback {
+  /** True if err is a StatusCodeException reporting Not Found. */
+  public static boolean isNotFound(Throwable err) {
+    return isStatus(err, Response.SC_NOT_FOUND);
+  }
+
+  /** True if err is describing a user that is currently anonymous. */
+  public static boolean isNotSignedIn(Throwable err) {
+    if (err instanceof StatusCodeException) {
+      StatusCodeException sce = (StatusCodeException) err;
+      if (sce.getStatusCode() == Response.SC_UNAUTHORIZED) {
+        return true;
+      }
+      return sce.getStatusCode() == Response.SC_FORBIDDEN
+          && (sce.getEncodedResponse().equals("Authentication required")
+              || sce.getEncodedResponse().startsWith("Must be signed-in"));
+    }
+    return false;
+  }
+
+  /** True if err is a StatusCodeException with a specific HTTP code. */
+  public static boolean isStatus(Throwable err, int status) {
+    return err instanceof StatusCodeException
+        && ((StatusCodeException) err).getStatusCode() == status;
+  }
+
+  /** Is the Gerrit Code Review server likely to return this status? */
+  public static boolean isExpected(int statusCode) {
+    switch (statusCode) {
+      case SC_UNAVAILABLE:
+      case 400: // Bad Request
+      case 401: // Unauthorized
+      case 403: // Forbidden
+      case 404: // Not Found
+      case 405: // Method Not Allowed
+      case 409: // Conflict
+      case 429: // Too Many Requests (RFC 6585)
+        return true;
+
+      default:
+        // Assume any other code is not expected. These may be
+        // local proxy server errors outside of our control.
+        return false;
+    }
+  }
+
+  private static class MyRequestCallback<T extends JavaScriptObject>
+      implements RequestCallback {
     private final AsyncCallback<T> cb;
 
     MyRequestCallback(AsyncCallback<T> cb) {
@@ -84,21 +131,15 @@
           msg = res.getStatusText();
         }
 
-        Throwable error;
-        if (400 <= status && status < 600) {
-          error = new RemoteJsonException(msg, status, null);
-        } else {
-          error = new StatusCodeException(status, res.getStatusText());
-        }
         RpcStatus.INSTANCE.onRpcComplete();
-        cb.onFailure(error);
+        cb.onFailure(new StatusCodeException(status, msg));
         return;
       }
 
       if (!isJsonBody(res)) {
         RpcStatus.INSTANCE.onRpcComplete();
-        cb.onFailure(new RemoteJsonException("Expected "
-            + JsonConstants.JSON_TYPE + "; received Content-Type: "
+        cb.onFailure(new StatusCodeException(200, "Expected "
+            + JSON_TYPE + "; received Content-Type: "
             + res.getHeader("Content-Type")));
         return;
       }
@@ -108,7 +149,8 @@
         data = cast(parseJson(res));
       } catch (JSONException e) {
         RpcStatus.INSTANCE.onRpcComplete();
-        cb.onFailure(new RemoteJsonException("Invalid JSON: " + e.getMessage()));
+        cb.onFailure(new StatusCodeException(200,
+            "Invalid JSON: " + e.getMessage()));
         return;
       }
 
@@ -120,9 +162,11 @@
     public void onError(Request req, Throwable err) {
       RpcStatus.INSTANCE.onRpcComplete();
       if (err.getMessage().contains("XmlHttpRequest.status")) {
-        cb.onFailure(new ServerUnavailableException());
+        cb.onFailure(new StatusCodeException(
+            SC_UNAVAILABLE,
+            RpcConstants.C.errorServerUnavailable()));
       } else {
-        cb.onFailure(err);
+        cb.onFailure(new StatusCodeException(SC_TRANSPORT, err.getMessage()));
       }
     }
   }
@@ -197,13 +241,13 @@
   }
 
   public RestApi data(JSONObject obj) {
-    contentType = JsonConstants.JSON_REQ_CT;
+    contentType = JSON_TYPE + "; charset=utf-8";
     contentData = obj.toString();
     return this;
   }
 
   public RestApi data(String data) {
-    contentType = "text/plain; charset=utf-8";
+    contentType = TEXT_TYPE + "; charset=utf-8";
     contentData = data;
     return this;
   }
@@ -229,28 +273,28 @@
       Method method,
       final AsyncCallback<T> cb) {
     RequestBuilder req = new RequestBuilder(method, url.toString());
-    req.setHeader("Accept", JsonConstants.JSON_TYPE);
+    req.setHeader("Accept", JSON_TYPE);
     if (Gerrit.getAuthorization() != null) {
       req.setHeader("Authorization", Gerrit.getAuthorization());
     }
     if (contentData != null) {
       req.setHeader("Content-Type", contentType);
     }
+    MyRequestCallback<T> httpCallback = new MyRequestCallback<T>(cb);
     try {
       RpcStatus.INSTANCE.onRpcStart();
-      req.sendRequest(contentData, new MyRequestCallback<T>(cb));
+      req.sendRequest(contentData, httpCallback);
     } catch (RequestException e) {
-      RpcStatus.INSTANCE.onRpcComplete();
-      cb.onFailure(e);
+      httpCallback.onError(null, e);
     }
   }
 
   private static boolean isJsonBody(Response res) {
-    return isContentType(res, JsonConstants.JSON_TYPE);
+    return isContentType(res, JSON_TYPE);
   }
 
   private static boolean isTextBody(Response res) {
-    return isContentType(res, "text/plain");
+    return isContentType(res, TEXT_TYPE);
   }
 
   private static boolean isContentType(Response res, String want) {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/TransformCallback.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/TransformCallback.java
index 2cd22cb..93c86a2 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/TransformCallback.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/TransformCallback.java
@@ -14,7 +14,7 @@
 
 package com.google.gerrit.client.rpc;
 
-import com.google.gwtjsonrpc.common.AsyncCallback;
+import com.google.gwt.user.client.rpc.AsyncCallback;
 
 /** Transforms a value and passes it on to another callback. */
 public abstract class TransformCallback<I, O> implements AsyncCallback<I>{
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/restapi/RestApiServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/restapi/RestApiServlet.java
index 37c75dd..ba168a0 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/restapi/RestApiServlet.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/restapi/RestApiServlet.java
@@ -14,6 +14,7 @@
 
 package com.google.gerrit.httpd.restapi;
 
+import static com.google.common.base.Charsets.UTF_8;
 import static com.google.common.base.Preconditions.checkNotNull;
 import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
 import static javax.servlet.http.HttpServletResponse.SC_CONFLICT;
@@ -35,6 +36,7 @@
 import com.google.common.collect.Maps;
 import com.google.common.collect.Multimap;
 import com.google.common.collect.Sets;
+import com.google.common.net.HttpHeaders;
 import com.google.gerrit.extensions.annotations.RequiresCapability;
 import com.google.gerrit.extensions.registration.DynamicMap;
 import com.google.gerrit.extensions.restapi.AcceptsCreate;
@@ -68,8 +70,6 @@
 import com.google.gson.JsonPrimitive;
 import com.google.gson.stream.JsonReader;
 import com.google.gson.stream.JsonToken;
-import com.google.gwtjsonrpc.common.JsonConstants;
-import com.google.gwtjsonrpc.server.RPCServletUtils;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.util.Providers;
@@ -86,7 +86,6 @@
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.io.OutputStreamWriter;
-import java.io.UnsupportedEncodingException;
 import java.io.Writer;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
@@ -110,9 +109,8 @@
       .getLogger(RestApiServlet.class);
 
   /** MIME type used for a JSON response body. */
-  private static final String JSON_TYPE = JsonConstants.JSON_TYPE;
+  private static final String JSON_TYPE = "application/json";
   private static final String FORM_TYPE = "application/x-www-form-urlencoded";
-  private static final String UTF_8 = "UTF-8";
 
   /**
    * Garbage prefix inserted before JSON output to prevent XSSI.
@@ -126,11 +124,7 @@
   private static final byte[] JSON_MAGIC;
 
   static {
-    try {
-      JSON_MAGIC = ")]}'\n".getBytes(UTF_8);
-    } catch (UnsupportedEncodingException e) {
-      throw new RuntimeException("UTF-8 not supported", e);
-    }
+    JSON_MAGIC = ")]}'\n".getBytes(UTF_8);
   }
 
   public static class Globals {
@@ -419,7 +413,7 @@
       public void writeTo(OutputStream os) throws IOException {
         buf.writeTo(os, null);
       }
-    }.setContentType(JSON_TYPE).setCharacterEncoding(UTF_8));
+    }.setContentType(JSON_TYPE).setCharacterEncoding(UTF_8.name()));
   }
 
   private static final FieldNamingPolicy NAMING =
@@ -676,11 +670,15 @@
   }
 
   private static boolean acceptsJson(HttpServletRequest req) {
-    return req != null && isType(JSON_TYPE, req.getHeader("Accept"));
+    return req != null && isType(JSON_TYPE, req.getHeader(HttpHeaders.ACCEPT));
   }
 
   private static boolean acceptsGzip(HttpServletRequest req) {
-    return req != null && RPCServletUtils.acceptsGzipEncoding(req);
+    if (req != null) {
+      String accepts = req.getHeader(HttpHeaders.ACCEPT_ENCODING);
+      return accepts != null && accepts.indexOf("gzip") != -1;
+    }
+    return false;
   }
 
   private static boolean isType(String expect, String given) {