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
This commit is contained in:
Shawn O. Pearce
2012-11-26 22:11:48 -08:00
parent db9d019f30
commit 06bfb9030a
19 changed files with 161 additions and 97 deletions

View File

@@ -17,8 +17,8 @@ package com.google.gerrit.client;
import static com.google.gerrit.common.PageLinks.ADMIN_CREATE_GROUP; 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_CREATE_PROJECT;
import static com.google.gerrit.common.PageLinks.ADMIN_GROUPS; 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_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.DASHBOARDS;
import static com.google.gerrit.common.PageLinks.MINE; import static com.google.gerrit.common.PageLinks.MINE;
import static com.google.gerrit.common.PageLinks.PROJECTS; import static com.google.gerrit.common.PageLinks.PROJECTS;
@@ -74,6 +74,7 @@ import com.google.gerrit.client.dashboards.DashboardInfo;
import com.google.gerrit.client.dashboards.DashboardList; import com.google.gerrit.client.dashboards.DashboardList;
import com.google.gerrit.client.patches.PatchScreen; import com.google.gerrit.client.patches.PatchScreen;
import com.google.gerrit.client.rpc.GerritCallback; 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.client.ui.Screen;
import com.google.gerrit.common.PageLinks; import com.google.gerrit.common.PageLinks;
import com.google.gerrit.common.auth.SignInMode; import com.google.gerrit.common.auth.SignInMode;
@@ -89,7 +90,6 @@ import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.RunAsyncCallback; import com.google.gwt.core.client.RunAsyncCallback;
import com.google.gwt.http.client.URL; import com.google.gwt.http.client.URL;
import com.google.gwt.user.client.Window; import com.google.gwt.user.client.Window;
import com.google.gwtjsonrpc.client.RemoteJsonException;
import com.google.gwtorm.client.KeyUtil; import com.google.gwtorm.client.KeyUtil;
public class Dispatcher { public class Dispatcher {
@@ -416,9 +416,7 @@ public class Dispatcher {
@Override @Override
public void onFailure(Throwable caught) { public void onFailure(Throwable caught) {
if ("default".equals(dashboardId) if ("default".equals(dashboardId) && RestApi.isNotFound(caught)) {
&& caught instanceof RemoteJsonException
&& ((RemoteJsonException) caught).getCode() == 404) {
Gerrit.display(PageLinks.toChangeQuery( Gerrit.display(PageLinks.toChangeQuery(
PageLinks.projectQuery(new Project.NameKey(project)))); PageLinks.projectQuery(new Project.NameKey(project))));
} else { } else {

View File

@@ -14,11 +14,13 @@
package com.google.gerrit.client; package com.google.gerrit.client;
import com.google.gerrit.client.rpc.RestApi;
import com.google.gerrit.client.rpc.RpcConstants; import com.google.gerrit.client.rpc.RpcConstants;
import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.KeyPressEvent; import com.google.gwt.event.dom.client.KeyPressEvent;
import com.google.gwt.event.dom.client.KeyPressHandler; 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.DOM;
import com.google.gwt.user.client.Window; import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.StatusCodeException; import com.google.gwt.user.client.rpc.StatusCodeException;
@@ -104,34 +106,54 @@ public class ErrorDialog extends PluginSafePopupPanel {
public ErrorDialog(final Throwable what) { public ErrorDialog(final Throwable what) {
this(); this();
String cn; String hdr;
if (what instanceof RemoteJsonException) { String msg;
cn = RpcConstants.C.errorRemoteJsonException();
} else if (what instanceof StatusCodeException) { if (what instanceof StatusCodeException) {
cn = RpcConstants.C.errorServerUnavailable(); 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 { } else {
cn = what.getClass().getName(); // TODO Fix callers of ErrorDialog to stop passing random types.
if (cn.startsWith("java.lang.")) { hdr = what.getClass().getName();
cn = cn.substring("java.lang.".length()); if (hdr.startsWith("java.lang.")) {
} else if (cn.startsWith("com.google.gerrit.")) { hdr = hdr.substring("java.lang.".length());
cn = cn.substring(cn.lastIndexOf('.') + 1); } else if (hdr.startsWith("com.google.gerrit.")) {
hdr = hdr.substring(hdr.lastIndexOf('.') + 1);
} }
if (cn.endsWith("Exception")) { if (hdr.endsWith("Exception")) {
cn = cn.substring(0, cn.length() - "Exception".length()); hdr = hdr.substring(0, hdr.length() - "Exception".length());
} else if (cn.endsWith("Error")) { } else if (hdr.endsWith("Error")) {
cn = cn.substring(0, cn.length() - "Error".length()); hdr = hdr.substring(0, hdr.length() - "Error".length());
} }
msg = what.getMessage();
} }
final Label r = new Label(cn); if (hdr != null) {
r.setStyleName(Gerrit.RESOURCES.css().errorDialogErrorType()); final Label r = new Label(hdr);
body.add(r); r.setStyleName(Gerrit.RESOURCES.css().errorDialogErrorType());
body.add(r);
}
final Label m = new Label(what.getMessage()); if (msg != null) {
DOM.setStyleAttribute(m.getElement(),"whiteSpace","pre"); final Label m = new Label(msg);
body.add(m); DOM.setStyleAttribute(m.getElement(), "whiteSpace", "pre");
body.add(m);
}
} }
public void setText(final String t) { public void setText(final String t) {

View File

@@ -11,7 +11,7 @@ linkIdentityDialogTitle = Code Review - Link Identity
registerDialogTitle = Code Review - Register New Account registerDialogTitle = Code Review - Register New Account
loginTypeUnsupported = Sign in is not available. loginTypeUnsupported = Sign in is not available.
errorDialogTitle = Application Error errorDialogTitle = Code Review - Error
errorDialogContinue = Continue errorDialogContinue = Continue
confirmationDialogOk = OK confirmationDialogOk = OK

View File

@@ -16,7 +16,7 @@ package com.google.gerrit.client.account;
import com.google.gerrit.client.rpc.RestApi; import com.google.gerrit.client.rpc.RestApi;
import com.google.gwt.core.client.JavaScriptObject; 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}. */ /** Capabilities the caller has from {@code /accounts/self/capabilities}. */
public class AccountCapabilities extends JavaScriptObject { public class AccountCapabilities extends JavaScriptObject {

View File

@@ -17,7 +17,7 @@ package com.google.gerrit.client.changes;
import com.google.gerrit.client.rpc.NativeString; import com.google.gerrit.client.rpc.NativeString;
import com.google.gerrit.client.rpc.RestApi; import com.google.gerrit.client.rpc.RestApi;
import com.google.gwt.core.client.JavaScriptObject; 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 * A collection of static methods which work on the Gerrit REST API for specific

View File

@@ -16,11 +16,11 @@ package com.google.gerrit.client.changes;
import static com.google.gerrit.client.FormatUtil.mediumFormat; 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.Gerrit;
import com.google.gerrit.client.rpc.GerritCallback;
import com.google.gerrit.client.ui.AccountLink; 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.BranchLink;
import com.google.gerrit.client.ui.CommentedActionDialog;
import com.google.gerrit.client.ui.ProjectLink; import com.google.gerrit.client.ui.ProjectLink;
import com.google.gerrit.common.PageLinks; import com.google.gerrit.common.PageLinks;
import com.google.gerrit.common.data.AccountInfoCache; import com.google.gerrit.common.data.AccountInfoCache;
@@ -39,7 +39,6 @@ import com.google.gwt.user.client.ui.InlineLabel;
import com.google.gwt.user.client.ui.TextBox; import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.Widget; import com.google.gwt.user.client.ui.Widget;
import com.google.gwtexpui.clippy.client.CopyableLabel; import com.google.gwtexpui.clippy.client.CopyableLabel;
import com.google.gwtjsonrpc.common.AsyncCallback;
public class ChangeInfoBlock extends Composite { public class ChangeInfoBlock extends Composite {
private static final int R_CHANGE_ID = 0; private static final int R_CHANGE_ID = 0;
@@ -187,7 +186,7 @@ public class ChangeInfoBlock extends Composite {
public void onSend() { public void onSend() {
String topic = newTopic.getText(); String topic = newTopic.getText();
ChangeApi.topic(change.getId().get(), topic, getMessageText(), ChangeApi.topic(change.getId().get(), topic, getMessageText(),
new AsyncCallback<String>() { new GerritCallback<String>() {
@Override @Override
public void onSuccess(String result) { public void onSuccess(String result) {
sent = true; sent = true;
@@ -198,7 +197,7 @@ public class ChangeInfoBlock extends Composite {
@Override @Override
public void onFailure(final Throwable caught) { public void onFailure(final Throwable caught) {
enableButtons(true); enableButtons(true);
new ErrorDialog(caught.getMessage()).center(); super.onFailure(caught);
}}); }});
} }
} }

View File

@@ -17,7 +17,7 @@ package com.google.gerrit.client.changes;
import com.google.gerrit.client.rpc.NativeList; import com.google.gerrit.client.rpc.NativeList;
import com.google.gerrit.client.rpc.RestApi; import com.google.gerrit.client.rpc.RestApi;
import com.google.gerrit.common.changes.ListChangesOption; 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 com.google.gwtorm.client.KeyUtil;
import java.util.EnumSet; import java.util.EnumSet;

View File

@@ -21,7 +21,7 @@ import com.google.gerrit.client.ui.Screen;
import com.google.gerrit.reviewdb.client.AccountGeneralPreferences; import com.google.gerrit.reviewdb.client.AccountGeneralPreferences;
import com.google.gwt.event.dom.client.KeyPressEvent; import com.google.gwt.event.dom.client.KeyPressEvent;
import com.google.gwt.user.client.History; 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.gwt.user.client.ui.HorizontalPanel;
import com.google.gwtexpui.globalkey.client.KeyCommand; import com.google.gwtexpui.globalkey.client.KeyCommand;

View File

@@ -15,7 +15,6 @@
package com.google.gerrit.client.changes; package com.google.gerrit.client.changes;
import com.google.gerrit.client.Dispatcher; import com.google.gerrit.client.Dispatcher;
import com.google.gerrit.client.ErrorDialog;
import com.google.gerrit.client.FormatUtil; import com.google.gerrit.client.FormatUtil;
import com.google.gerrit.client.Gerrit; import com.google.gerrit.client.Gerrit;
import com.google.gerrit.client.GitwebLink; import com.google.gerrit.client.GitwebLink;
@@ -49,7 +48,6 @@ import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.HTMLTable.CellFormatter; import com.google.gwt.user.client.ui.HTMLTable.CellFormatter;
import com.google.gwt.user.client.ui.InlineLabel; import com.google.gwt.user.client.ui.InlineLabel;
import com.google.gwt.user.client.ui.Panel; import com.google.gwt.user.client.ui.Panel;
import com.google.gwtjsonrpc.common.AsyncCallback;
import com.google.gwtjsonrpc.common.VoidResult; import com.google.gwtjsonrpc.common.VoidResult;
import java.util.HashSet; import java.util.HashSet;
@@ -355,7 +353,7 @@ class PatchSetComplexDisclosurePanel extends ComplexDisclosurePanel
@Override @Override
public void onSend() { public void onSend() {
ChangeApi.revert(changeDetail.getChange().getChangeId(), ChangeApi.revert(changeDetail.getChange().getChangeId(),
getMessageText(), new AsyncCallback<ChangeInfo>() { getMessageText(), new GerritCallback<ChangeInfo>() {
@Override @Override
public void onSuccess(ChangeInfo result) { public void onSuccess(ChangeInfo result) {
sent = true; sent = true;
@@ -367,7 +365,7 @@ class PatchSetComplexDisclosurePanel extends ComplexDisclosurePanel
@Override @Override
public void onFailure(Throwable caught) { public void onFailure(Throwable caught) {
enableButtons(true); enableButtons(true);
new ErrorDialog(caught.getMessage()).center(); super.onFailure(caught);
} }
}); });
} }
@@ -395,7 +393,7 @@ class PatchSetComplexDisclosurePanel extends ComplexDisclosurePanel
// REST APIs, we can use createCallback() rather than providing // REST APIs, we can use createCallback() rather than providing
// them directly. // them directly.
ChangeApi.abandon(changeDetail.getChange().getChangeId(), ChangeApi.abandon(changeDetail.getChange().getChangeId(),
getMessageText(), new AsyncCallback<ChangeInfo>() { getMessageText(), new GerritCallback<ChangeInfo>() {
@Override @Override
public void onSuccess(ChangeInfo result) { public void onSuccess(ChangeInfo result) {
sent = true; sent = true;
@@ -407,7 +405,7 @@ class PatchSetComplexDisclosurePanel extends ComplexDisclosurePanel
@Override @Override
public void onFailure(Throwable caught) { public void onFailure(Throwable caught) {
enableButtons(true); enableButtons(true);
new ErrorDialog(caught.getMessage()).center(); super.onFailure(caught);
} }
}); });
} }
@@ -456,7 +454,7 @@ class PatchSetComplexDisclosurePanel extends ComplexDisclosurePanel
@Override @Override
public void onSend() { public void onSend() {
ChangeApi.restore(changeDetail.getChange().getChangeId(), ChangeApi.restore(changeDetail.getChange().getChangeId(),
getMessageText(), new AsyncCallback<ChangeInfo>() { getMessageText(), new GerritCallback<ChangeInfo>() {
@Override @Override
public void onSuccess(ChangeInfo result) { public void onSuccess(ChangeInfo result) {
sent = true; sent = true;
@@ -468,7 +466,7 @@ class PatchSetComplexDisclosurePanel extends ComplexDisclosurePanel
@Override @Override
public void onFailure(Throwable caught) { public void onFailure(Throwable caught) {
enableButtons(true); enableButtons(true);
new ErrorDialog(caught.getMessage()).center(); super.onFailure(caught);
} }
}); });
} }

View File

@@ -19,7 +19,7 @@ import com.google.gerrit.client.rpc.GerritCallback;
import com.google.gerrit.common.PageLinks; import com.google.gerrit.common.PageLinks;
import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.RevId; 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; import com.google.gwtorm.client.KeyUtil;
public class QueryScreen extends PagedSingleListScreen implements public class QueryScreen extends PagedSingleListScreen implements

View File

@@ -18,7 +18,7 @@ import com.google.gerrit.client.rpc.NativeList;
import com.google.gerrit.client.rpc.RestApi; import com.google.gerrit.client.rpc.RestApi;
import com.google.gerrit.reviewdb.client.Project; import com.google.gerrit.reviewdb.client.Project;
import com.google.gwt.http.client.URL; 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/}. */ /** Project dashboards from {@code /projects/<name>/dashboards/}. */
public class DashboardList extends NativeList<DashboardInfo> { public class DashboardList extends NativeList<DashboardInfo> {

View File

@@ -16,7 +16,7 @@ package com.google.gerrit.client.plugins;
import com.google.gerrit.client.rpc.NativeMap; import com.google.gerrit.client.rpc.NativeMap;
import com.google.gerrit.client.rpc.RestApi; 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/}. */ /** Plugins available from {@code /plugins/}. */
public class PluginMap extends NativeMap<PluginInfo> { public class PluginMap extends NativeMap<PluginInfo> {

View File

@@ -16,7 +16,7 @@ package com.google.gerrit.client.projects;
import com.google.gerrit.client.rpc.NativeMap; import com.google.gerrit.client.rpc.NativeMap;
import com.google.gerrit.client.rpc.RestApi; 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/}. */ /** Projects available from {@code /projects/}. */
public class ProjectMap extends NativeMap<ProjectInfo> { public class ProjectMap extends NativeMap<ProjectInfo> {

View File

@@ -24,14 +24,17 @@ import com.google.gerrit.common.errors.NoSuchEntityException;
import com.google.gerrit.common.errors.NoSuchGroupException; import com.google.gerrit.common.errors.NoSuchGroupException;
import com.google.gerrit.common.errors.NotSignedInException; import com.google.gerrit.common.errors.NotSignedInException;
import com.google.gwt.core.client.GWT; 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.InvocationException;
import com.google.gwt.user.client.rpc.StatusCodeException;
import com.google.gwtjsonrpc.client.RemoteJsonException; import com.google.gwtjsonrpc.client.RemoteJsonException;
import com.google.gwtjsonrpc.client.ServerUnavailableException; import com.google.gwtjsonrpc.client.ServerUnavailableException;
import com.google.gwtjsonrpc.common.JsonConstants; import com.google.gwtjsonrpc.common.JsonConstants;
/** Abstract callback handling generic error conditions automatically */ /** 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) { public void onFailure(final Throwable caught) {
if (isNotSignedIn(caught) || isInvalidXSRF(caught)) { if (isNotSignedIn(caught) || isInvalidXSRF(caught)) {
new NotSignedInDialog().center(); new NotSignedInDialog().center();
@@ -76,14 +79,16 @@ public abstract class GerritCallback<T> implements AsyncCallback<T> {
&& caught.getMessage().equals(JsonConstants.ERROR_INVALID_XSRF); && caught.getMessage().equals(JsonConstants.ERROR_INVALID_XSRF);
} }
private static boolean isNotSignedIn(final Throwable caught) { private static boolean isNotSignedIn(Throwable caught) {
return caught instanceof RemoteJsonException return RestApi.isNotSignedIn(caught)
&& caught.getMessage().equals(NotSignedInException.MESSAGE); || (caught instanceof RemoteJsonException
&& caught.getMessage().equals(NotSignedInException.MESSAGE));
} }
protected static boolean isNoSuchEntity(final Throwable caught) { protected static boolean isNoSuchEntity(Throwable caught) {
return caught instanceof RemoteJsonException return RestApi.isNotFound(caught)
&& caught.getMessage().equals(NoSuchEntityException.MESSAGE); || (caught instanceof RemoteJsonException
&& caught.getMessage().equals(NoSuchEntityException.MESSAGE));
} }
protected static boolean isInactiveAccount(final Throwable caught) { protected static boolean isInactiveAccount(final Throwable caught) {

View File

@@ -15,7 +15,7 @@
package com.google.gerrit.client.rpc; package com.google.gerrit.client.rpc;
import com.google.gwt.core.client.JavaScriptObject; 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; import java.util.Set;

View File

@@ -15,7 +15,7 @@
package com.google.gerrit.client.rpc; package com.google.gerrit.client.rpc;
import com.google.gwt.core.client.JavaScriptObject; 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. */ /** Wraps a String that was returned from a JSON API. */
public final class NativeString extends JavaScriptObject { public final class NativeString extends JavaScriptObject {

View File

@@ -34,14 +34,16 @@ import com.google.gwt.json.client.JSONException;
import com.google.gwt.json.client.JSONObject; import com.google.gwt.json.client.JSONObject;
import com.google.gwt.json.client.JSONParser; import com.google.gwt.json.client.JSONParser;
import com.google.gwt.json.client.JSONValue; 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.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. */ /** Makes a REST API call to the server. */
public class RestApi { 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. * Expected JSON content body prefix that prevents XSSI.
* <p> * <p>
@@ -53,8 +55,53 @@ public class RestApi {
*/ */
private static final String JSON_MAGIC = ")]}'\n"; private static final String JSON_MAGIC = ")]}'\n";
private class MyRequestCallback<T extends JavaScriptObject> implements /** True if err is a StatusCodeException reporting Not Found. */
RequestCallback { 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; private final AsyncCallback<T> cb;
MyRequestCallback(AsyncCallback<T> cb) { MyRequestCallback(AsyncCallback<T> cb) {
@@ -84,21 +131,15 @@ public class RestApi {
msg = res.getStatusText(); 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(); RpcStatus.INSTANCE.onRpcComplete();
cb.onFailure(error); cb.onFailure(new StatusCodeException(status, msg));
return; return;
} }
if (!isJsonBody(res)) { if (!isJsonBody(res)) {
RpcStatus.INSTANCE.onRpcComplete(); RpcStatus.INSTANCE.onRpcComplete();
cb.onFailure(new RemoteJsonException("Expected " cb.onFailure(new StatusCodeException(200, "Expected "
+ JsonConstants.JSON_TYPE + "; received Content-Type: " + JSON_TYPE + "; received Content-Type: "
+ res.getHeader("Content-Type"))); + res.getHeader("Content-Type")));
return; return;
} }
@@ -108,7 +149,8 @@ public class RestApi {
data = cast(parseJson(res)); data = cast(parseJson(res));
} catch (JSONException e) { } catch (JSONException e) {
RpcStatus.INSTANCE.onRpcComplete(); RpcStatus.INSTANCE.onRpcComplete();
cb.onFailure(new RemoteJsonException("Invalid JSON: " + e.getMessage())); cb.onFailure(new StatusCodeException(200,
"Invalid JSON: " + e.getMessage()));
return; return;
} }
@@ -120,9 +162,11 @@ public class RestApi {
public void onError(Request req, Throwable err) { public void onError(Request req, Throwable err) {
RpcStatus.INSTANCE.onRpcComplete(); RpcStatus.INSTANCE.onRpcComplete();
if (err.getMessage().contains("XmlHttpRequest.status")) { if (err.getMessage().contains("XmlHttpRequest.status")) {
cb.onFailure(new ServerUnavailableException()); cb.onFailure(new StatusCodeException(
SC_UNAVAILABLE,
RpcConstants.C.errorServerUnavailable()));
} else { } else {
cb.onFailure(err); cb.onFailure(new StatusCodeException(SC_TRANSPORT, err.getMessage()));
} }
} }
} }
@@ -197,13 +241,13 @@ public class RestApi {
} }
public RestApi data(JSONObject obj) { public RestApi data(JSONObject obj) {
contentType = JsonConstants.JSON_REQ_CT; contentType = JSON_TYPE + "; charset=utf-8";
contentData = obj.toString(); contentData = obj.toString();
return this; return this;
} }
public RestApi data(String data) { public RestApi data(String data) {
contentType = "text/plain; charset=utf-8"; contentType = TEXT_TYPE + "; charset=utf-8";
contentData = data; contentData = data;
return this; return this;
} }
@@ -229,28 +273,28 @@ public class RestApi {
Method method, Method method,
final AsyncCallback<T> cb) { final AsyncCallback<T> cb) {
RequestBuilder req = new RequestBuilder(method, url.toString()); RequestBuilder req = new RequestBuilder(method, url.toString());
req.setHeader("Accept", JsonConstants.JSON_TYPE); req.setHeader("Accept", JSON_TYPE);
if (Gerrit.getAuthorization() != null) { if (Gerrit.getAuthorization() != null) {
req.setHeader("Authorization", Gerrit.getAuthorization()); req.setHeader("Authorization", Gerrit.getAuthorization());
} }
if (contentData != null) { if (contentData != null) {
req.setHeader("Content-Type", contentType); req.setHeader("Content-Type", contentType);
} }
MyRequestCallback<T> httpCallback = new MyRequestCallback<T>(cb);
try { try {
RpcStatus.INSTANCE.onRpcStart(); RpcStatus.INSTANCE.onRpcStart();
req.sendRequest(contentData, new MyRequestCallback<T>(cb)); req.sendRequest(contentData, httpCallback);
} catch (RequestException e) { } catch (RequestException e) {
RpcStatus.INSTANCE.onRpcComplete(); httpCallback.onError(null, e);
cb.onFailure(e);
} }
} }
private static boolean isJsonBody(Response res) { private static boolean isJsonBody(Response res) {
return isContentType(res, JsonConstants.JSON_TYPE); return isContentType(res, JSON_TYPE);
} }
private static boolean isTextBody(Response res) { private static boolean isTextBody(Response res) {
return isContentType(res, "text/plain"); return isContentType(res, TEXT_TYPE);
} }
private static boolean isContentType(Response res, String want) { private static boolean isContentType(Response res, String want) {

View File

@@ -14,7 +14,7 @@
package com.google.gerrit.client.rpc; 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. */ /** Transforms a value and passes it on to another callback. */
public abstract class TransformCallback<I, O> implements AsyncCallback<I>{ public abstract class TransformCallback<I, O> implements AsyncCallback<I>{

View File

@@ -14,6 +14,7 @@
package com.google.gerrit.httpd.restapi; 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 com.google.common.base.Preconditions.checkNotNull;
import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST; import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
import static javax.servlet.http.HttpServletResponse.SC_CONFLICT; import static javax.servlet.http.HttpServletResponse.SC_CONFLICT;
@@ -35,6 +36,7 @@ import com.google.common.collect.Lists;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.google.common.collect.Multimap; import com.google.common.collect.Multimap;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import com.google.common.net.HttpHeaders;
import com.google.gerrit.extensions.annotations.RequiresCapability; import com.google.gerrit.extensions.annotations.RequiresCapability;
import com.google.gerrit.extensions.registration.DynamicMap; import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.restapi.AcceptsCreate; import com.google.gerrit.extensions.restapi.AcceptsCreate;
@@ -68,8 +70,6 @@ import com.google.gson.JsonParseException;
import com.google.gson.JsonPrimitive; import com.google.gson.JsonPrimitive;
import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken; 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.Inject;
import com.google.inject.Provider; import com.google.inject.Provider;
import com.google.inject.util.Providers; import com.google.inject.util.Providers;
@@ -86,7 +86,6 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.OutputStreamWriter; import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer; import java.io.Writer;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.Field; import java.lang.reflect.Field;
@@ -110,9 +109,8 @@ public class RestApiServlet extends HttpServlet {
.getLogger(RestApiServlet.class); .getLogger(RestApiServlet.class);
/** MIME type used for a JSON response body. */ /** 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 FORM_TYPE = "application/x-www-form-urlencoded";
private static final String UTF_8 = "UTF-8";
/** /**
* Garbage prefix inserted before JSON output to prevent XSSI. * Garbage prefix inserted before JSON output to prevent XSSI.
@@ -126,11 +124,7 @@ public class RestApiServlet extends HttpServlet {
private static final byte[] JSON_MAGIC; private static final byte[] JSON_MAGIC;
static { static {
try { JSON_MAGIC = ")]}'\n".getBytes(UTF_8);
JSON_MAGIC = ")]}'\n".getBytes(UTF_8);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("UTF-8 not supported", e);
}
} }
public static class Globals { public static class Globals {
@@ -419,7 +413,7 @@ public class RestApiServlet extends HttpServlet {
public void writeTo(OutputStream os) throws IOException { public void writeTo(OutputStream os) throws IOException {
buf.writeTo(os, null); buf.writeTo(os, null);
} }
}.setContentType(JSON_TYPE).setCharacterEncoding(UTF_8)); }.setContentType(JSON_TYPE).setCharacterEncoding(UTF_8.name()));
} }
private static final FieldNamingPolicy NAMING = private static final FieldNamingPolicy NAMING =
@@ -676,11 +670,15 @@ public class RestApiServlet extends HttpServlet {
} }
private static boolean acceptsJson(HttpServletRequest req) { 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) { 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) { private static boolean isType(String expect, String given) {