Catch RPC exceptions and display them in a modal dialog
Currently we really can't recover automatically from any RPC error, so instead of failing silently in compiled mode (due the compiler stripping out GWT.log calls) we show a modal popup to the user to let them know things didn't go according to plan. In the case of a star toggle, we untoggle it, but pretty much all of the other actions don't really have a reverse, so we use the default error display logic in the dialog. Future versions of gwtjsonrpc should support automatically retrying certain types of requests, especially when we can identify the error as a network connectivity problem. Signed-off-by: Shawn O. Pearce <sop@google.com>
This commit is contained in:
81
webapp/src/com/google/gerrit/client/ErrorDialog.java
Normal file
81
webapp/src/com/google/gerrit/client/ErrorDialog.java
Normal file
@@ -0,0 +1,81 @@
|
||||
// Copyright 2008 Google Inc.
|
||||
//
|
||||
// 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.client;
|
||||
|
||||
import com.google.gwt.user.client.ui.Button;
|
||||
import com.google.gwt.user.client.ui.ClickListener;
|
||||
import com.google.gwt.user.client.ui.DialogBox;
|
||||
import com.google.gwt.user.client.ui.FlowPanel;
|
||||
import com.google.gwt.user.client.ui.Label;
|
||||
import com.google.gwt.user.client.ui.Widget;
|
||||
import com.google.gwtjsonrpc.client.RemoteJsonException;
|
||||
|
||||
/** A dialog box showing an error message, when bad things happen. */
|
||||
public class ErrorDialog extends DialogBox {
|
||||
private final FlowPanel body;
|
||||
|
||||
protected ErrorDialog() {
|
||||
super(/* auto hide */true, /* modal */true);
|
||||
setText(Gerrit.C.errorDialogTitle());
|
||||
|
||||
body = new FlowPanel();
|
||||
final FlowPanel buttons = new FlowPanel();
|
||||
buttons.setStyleName("gerrit-ErrorDialog-Buttons");
|
||||
final Button closey = new Button();
|
||||
closey.setText(Gerrit.C.errorDialogClose());
|
||||
closey.addClickListener(new ClickListener() {
|
||||
public void onClick(final Widget sender) {
|
||||
hide();
|
||||
}
|
||||
});
|
||||
buttons.add(closey);
|
||||
|
||||
final FlowPanel center = new FlowPanel();
|
||||
center.setStyleName("gerrit-ErrorDialog");
|
||||
center.add(body);
|
||||
center.add(buttons);
|
||||
add(center);
|
||||
}
|
||||
|
||||
/** Create a dialog box to show a single message string. */
|
||||
public ErrorDialog(final String message) {
|
||||
this();
|
||||
body.add(label(message, "gerrit-ErrorDialog-ErrorMessage"));
|
||||
}
|
||||
|
||||
/** Create a dialog box to nicely format an exception. */
|
||||
public ErrorDialog(final Throwable what) {
|
||||
this();
|
||||
|
||||
String cn;
|
||||
if (what instanceof RemoteJsonException) {
|
||||
cn = com.google.gerrit.client.rpc.Util.C.errorRemoteJsonException();
|
||||
} else {
|
||||
cn = what.getClass().getName();
|
||||
if (cn.startsWith("java.lang.")) {
|
||||
cn = cn.substring("java.lang.".length());
|
||||
}
|
||||
}
|
||||
|
||||
body.add(label(cn, "gerrit-ErrorDialog-ErrorType"));
|
||||
body.add(label(what.getMessage(), "gerrit-ErrorDialog-ErrorMessage"));
|
||||
}
|
||||
|
||||
private static Label label(final String what, final String style) {
|
||||
final Label r = new Label(what);
|
||||
r.setStyleName(style);
|
||||
return r;
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,9 @@ public interface GerritConstants extends Constants {
|
||||
String menuSettings();
|
||||
String signInDialogTitle();
|
||||
|
||||
String errorDialogTitle();
|
||||
String errorDialogClose();
|
||||
|
||||
String notFoundTitle();
|
||||
String notFoundBody();
|
||||
|
||||
|
||||
@@ -3,6 +3,9 @@ menuSignOut = Sign Out
|
||||
menuSettings = Settings
|
||||
signInDialogTitle = Code Review - Sign In
|
||||
|
||||
errorDialogTitle = Code Review - Unexpected Error
|
||||
errorDialogClose = Close
|
||||
|
||||
notFoundTitle = Not Found
|
||||
notFoundBody = The page you requested was not found.
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ package com.google.gerrit.client;
|
||||
|
||||
import com.google.gerrit.client.account.SignInResult;
|
||||
import com.google.gerrit.client.account.SignInResult.Status;
|
||||
import com.google.gerrit.client.rpc.GerritCallback;
|
||||
import com.google.gwt.core.client.GWT;
|
||||
import com.google.gwt.user.client.Command;
|
||||
import com.google.gwt.user.client.DeferredCommand;
|
||||
@@ -61,14 +62,10 @@ public class SignInDialog extends DialogBox {
|
||||
|
||||
signInCallback =
|
||||
com.google.gerrit.client.account.Util.LOGIN_SVC
|
||||
.signIn(new AsyncCallback<SignInResult>() {
|
||||
.signIn(new GerritCallback<SignInResult>() {
|
||||
public void onSuccess(final SignInResult result) {
|
||||
onCallback(result);
|
||||
}
|
||||
|
||||
public void onFailure(Throwable caught) {
|
||||
GWT.log("Unexpected signIn failure", caught);
|
||||
}
|
||||
});
|
||||
appCallback = callback;
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ package com.google.gerrit.client.account;
|
||||
import com.google.gerrit.client.Gerrit;
|
||||
import com.google.gerrit.client.reviewdb.Account;
|
||||
import com.google.gerrit.client.reviewdb.ReviewDb;
|
||||
import com.google.gerrit.client.rpc.NotSignedInException;
|
||||
import com.google.gwt.user.client.rpc.AsyncCallback;
|
||||
import com.google.gwtjsonrpc.client.CookieAccess;
|
||||
import com.google.gwtorm.client.OrmException;
|
||||
@@ -32,7 +33,7 @@ public class AccountServiceImpl implements AccountService {
|
||||
public void myAccount(final AsyncCallback<Account> callback) {
|
||||
final int id = idFromCookie(callback);
|
||||
if (id <= 0) {
|
||||
callback.onSuccess(null);
|
||||
callback.onFailure(new NotSignedInException());
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,9 +15,9 @@
|
||||
package com.google.gerrit.client.account;
|
||||
|
||||
import com.google.gerrit.client.reviewdb.Account;
|
||||
import com.google.gerrit.client.rpc.GerritCallback;
|
||||
import com.google.gerrit.client.ui.AccountScreen;
|
||||
import com.google.gwt.core.client.GWT;
|
||||
import com.google.gwt.user.client.rpc.AsyncCallback;
|
||||
|
||||
public class AccountSettings extends AccountScreen {
|
||||
public AccountSettings() {
|
||||
@@ -27,11 +27,7 @@ public class AccountSettings extends AccountScreen {
|
||||
@Override
|
||||
public void onLoad() {
|
||||
super.onLoad();
|
||||
Util.ACCOUNT_SVC.myAccount(new AsyncCallback<Account>() {
|
||||
public void onFailure(Throwable caught) {
|
||||
GWT.log("myAccount failed", caught);
|
||||
}
|
||||
|
||||
Util.ACCOUNT_SVC.myAccount(new GerritCallback<Account>() {
|
||||
public void onSuccess(Account result) {
|
||||
GWT.log("yay, i am " + result.getPreferredEmail(), null);
|
||||
GWT.log("created on " + result.getRegisteredOn(), null);
|
||||
|
||||
@@ -17,10 +17,9 @@ package com.google.gerrit.client.changes;
|
||||
import com.google.gerrit.client.data.AccountDashboardInfo;
|
||||
import com.google.gerrit.client.data.AccountInfo;
|
||||
import com.google.gerrit.client.reviewdb.Account;
|
||||
import com.google.gerrit.client.rpc.GerritCallback;
|
||||
import com.google.gerrit.client.ui.Screen;
|
||||
import com.google.gwt.core.client.GWT;
|
||||
import com.google.gwt.user.client.History;
|
||||
import com.google.gwt.user.client.rpc.AsyncCallback;
|
||||
|
||||
|
||||
public class AccountDashboardScreen extends Screen {
|
||||
@@ -58,14 +57,10 @@ public class AccountDashboardScreen extends Screen {
|
||||
super.onLoad();
|
||||
table.setSavePointerId(History.getToken());
|
||||
Util.LIST_SVC.forAccount(ownerId,
|
||||
new AsyncCallback<AccountDashboardInfo>() {
|
||||
new GerritCallback<AccountDashboardInfo>() {
|
||||
public void onSuccess(final AccountDashboardInfo r) {
|
||||
display(r);
|
||||
}
|
||||
|
||||
public void onFailure(final Throwable caught) {
|
||||
GWT.log("Fail", caught);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import com.google.gerrit.client.reviewdb.ChangeAccess;
|
||||
import com.google.gerrit.client.reviewdb.ReviewDb;
|
||||
import com.google.gerrit.client.reviewdb.StarredChange;
|
||||
import com.google.gerrit.client.reviewdb.Change.Id;
|
||||
import com.google.gerrit.client.rpc.NotSignedInException;
|
||||
import com.google.gwt.user.client.rpc.AsyncCallback;
|
||||
import com.google.gwtjsonrpc.client.CookieAccess;
|
||||
import com.google.gwtjsonrpc.client.VoidResult;
|
||||
@@ -77,7 +78,7 @@ public class ChangeListServiceImpl implements ChangeListService {
|
||||
public void myStarredChanges(final AsyncCallback<List<ChangeInfo>> callback) {
|
||||
final Account.Id me = idFromCookie();
|
||||
if (me == null) {
|
||||
callback.onFailure(new IllegalArgumentException("Not signed in"));
|
||||
callback.onFailure(new NotSignedInException());
|
||||
return;
|
||||
}
|
||||
try {
|
||||
@@ -101,7 +102,7 @@ public class ChangeListServiceImpl implements ChangeListService {
|
||||
final AsyncCallback<VoidResult> callback) {
|
||||
final Account.Id me = idFromCookie();
|
||||
if (me == null) {
|
||||
callback.onFailure(new IllegalArgumentException("Not signed in"));
|
||||
callback.onFailure(new NotSignedInException());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -148,7 +149,7 @@ public class ChangeListServiceImpl implements ChangeListService {
|
||||
public void myStarredChangeIds(final AsyncCallback<Set<Id>> callback) {
|
||||
final Account.Id me = idFromCookie();
|
||||
if (me == null) {
|
||||
callback.onFailure(new IllegalArgumentException("Not signed in"));
|
||||
callback.onFailure(new NotSignedInException());
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,14 +20,13 @@ import com.google.gerrit.client.SignedInListener;
|
||||
import com.google.gerrit.client.data.ChangeInfo;
|
||||
import com.google.gerrit.client.reviewdb.Change;
|
||||
import com.google.gerrit.client.reviewdb.Change.Id;
|
||||
import com.google.gwt.core.client.GWT;
|
||||
import com.google.gerrit.client.rpc.GerritCallback;
|
||||
import com.google.gwt.user.client.Command;
|
||||
import com.google.gwt.user.client.DOM;
|
||||
import com.google.gwt.user.client.DeferredCommand;
|
||||
import com.google.gwt.user.client.Element;
|
||||
import com.google.gwt.user.client.Event;
|
||||
import com.google.gwt.user.client.History;
|
||||
import com.google.gwt.user.client.rpc.AsyncCallback;
|
||||
import com.google.gwt.user.client.ui.AbstractImagePrototype;
|
||||
import com.google.gwt.user.client.ui.Composite;
|
||||
import com.google.gwt.user.client.ui.FlexTable;
|
||||
@@ -42,7 +41,7 @@ import com.google.gwt.user.client.ui.SourcesTableEvents;
|
||||
import com.google.gwt.user.client.ui.TableListener;
|
||||
import com.google.gwt.user.client.ui.Widget;
|
||||
import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
|
||||
import com.google.gwtjsonrpc.client.VoidCallback;
|
||||
import com.google.gwtjsonrpc.client.VoidResult;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
@@ -155,11 +154,7 @@ public class ChangeTable extends Composite implements HasFocus {
|
||||
|
||||
signedInListener = new SignedInListener() {
|
||||
public void onSignIn() {
|
||||
Util.LIST_SVC.myStarredChangeIds(new AsyncCallback<Set<Change.Id>>() {
|
||||
public void onFailure(final Throwable caught) {
|
||||
GWT.log("ChangeTable.onSignIn myStarredChangeIds failed", caught);
|
||||
}
|
||||
|
||||
Util.LIST_SVC.myStarredChangeIds(new GerritCallback<Set<Change.Id>>() {
|
||||
public void onSuccess(final Set<Change.Id> result) {
|
||||
if (result != null) {
|
||||
final int max = table.getRowCount();
|
||||
@@ -197,12 +192,23 @@ public class ChangeTable extends Composite implements HasFocus {
|
||||
protected void onStarClick(final int row) {
|
||||
final ChangeInfo c = getChangeInfo(row);
|
||||
if (c != null && Gerrit.isSignedIn()) {
|
||||
c.setStarred(!c.isStarred());
|
||||
final boolean prior = c.isStarred();
|
||||
c.setStarred(!prior);
|
||||
setStar(row, c);
|
||||
|
||||
final ToggleStarRequest req = new ToggleStarRequest();
|
||||
req.toggle(c.getId(), c.isStarred());
|
||||
Util.LIST_SVC.toggleStars(req, VoidCallback.INSTANCE);
|
||||
Util.LIST_SVC.toggleStars(req, new GerritCallback<VoidResult>() {
|
||||
public void onSuccess(final VoidResult result) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(final Throwable caught) {
|
||||
super.onFailure(caught);
|
||||
c.setStarred(prior);
|
||||
setStar(row, c);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,10 +15,9 @@
|
||||
package com.google.gerrit.client.changes;
|
||||
|
||||
import com.google.gerrit.client.data.ChangeInfo;
|
||||
import com.google.gerrit.client.rpc.GerritCallback;
|
||||
import com.google.gerrit.client.ui.AccountScreen;
|
||||
import com.google.gwt.core.client.GWT;
|
||||
import com.google.gwt.user.client.History;
|
||||
import com.google.gwt.user.client.rpc.AsyncCallback;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -41,15 +40,11 @@ public class MineStarredScreen extends AccountScreen {
|
||||
public void onLoad() {
|
||||
super.onLoad();
|
||||
table.setSavePointerId(History.getToken());
|
||||
Util.LIST_SVC.myStarredChanges(new AsyncCallback<List<ChangeInfo>>() {
|
||||
Util.LIST_SVC.myStarredChanges(new GerritCallback<List<ChangeInfo>>() {
|
||||
public void onSuccess(final List<ChangeInfo> result) {
|
||||
starred.display(result);
|
||||
table.finishDisplay();
|
||||
}
|
||||
|
||||
public void onFailure(final Throwable caught) {
|
||||
GWT.log("Fail", caught);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
45
webapp/src/com/google/gerrit/client/rpc/GerritCallback.java
Normal file
45
webapp/src/com/google/gerrit/client/rpc/GerritCallback.java
Normal file
@@ -0,0 +1,45 @@
|
||||
// Copyright 2008 Google Inc.
|
||||
//
|
||||
// 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.client.rpc;
|
||||
|
||||
import com.google.gerrit.client.ErrorDialog;
|
||||
import com.google.gwt.core.client.GWT;
|
||||
import com.google.gwt.user.client.rpc.AsyncCallback;
|
||||
import com.google.gwtjsonrpc.client.RemoteJsonException;
|
||||
import com.google.gwtjsonrpc.client.ServerUnavailableException;
|
||||
|
||||
/** Abstract callback handling generic error conditions automatically */
|
||||
public abstract class GerritCallback<T> implements AsyncCallback<T> {
|
||||
public void onFailure(final Throwable caught) {
|
||||
if (isNotSignedIn(caught)) {
|
||||
new ErrorDialog(Util.C.errorNotSignedIn()).center();
|
||||
|
||||
} else if (caught instanceof ServerUnavailableException) {
|
||||
new ErrorDialog(Util.C.errorServerUnavailable()).center();
|
||||
|
||||
} else {
|
||||
GWT.log(getClass().getName() + " caught " + caught, caught);
|
||||
new ErrorDialog(caught).center();
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isNotSignedIn(final Throwable caught) {
|
||||
if (caught instanceof NotSignedInException) {
|
||||
return true;
|
||||
}
|
||||
return caught instanceof RemoteJsonException
|
||||
&& caught.getMessage().equals(NotSignedInException.MESSAGE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
// Copyright 2008 Google Inc.
|
||||
//
|
||||
// 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.client.rpc;
|
||||
|
||||
/** Error stating the user must be signed-in in order to perform this action. */
|
||||
public class NotSignedInException extends Exception {
|
||||
public static final String MESSAGE = "Not Signed In";
|
||||
|
||||
public NotSignedInException() {
|
||||
super(MESSAGE);
|
||||
}
|
||||
}
|
||||
23
webapp/src/com/google/gerrit/client/rpc/RpcConstants.java
Normal file
23
webapp/src/com/google/gerrit/client/rpc/RpcConstants.java
Normal file
@@ -0,0 +1,23 @@
|
||||
// Copyright 2008 Google Inc.
|
||||
//
|
||||
// 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.client.rpc;
|
||||
|
||||
import com.google.gwt.i18n.client.Constants;
|
||||
|
||||
public interface RpcConstants extends Constants {
|
||||
String errorNotSignedIn();
|
||||
String errorServerUnavailable();
|
||||
String errorRemoteJsonException();
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
errorNotSignedIn = Not Signed In
|
||||
errorServerUnavailable = Server Unavailable
|
||||
errorRemoteJsonException = Server Error
|
||||
21
webapp/src/com/google/gerrit/client/rpc/Util.java
Normal file
21
webapp/src/com/google/gerrit/client/rpc/Util.java
Normal file
@@ -0,0 +1,21 @@
|
||||
// Copyright 2008 Google Inc.
|
||||
//
|
||||
// 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.client.rpc;
|
||||
|
||||
import com.google.gwt.core.client.GWT;
|
||||
|
||||
public class Util {
|
||||
public static final RpcConstants C = GWT.create(RpcConstants.class);
|
||||
}
|
||||
@@ -80,6 +80,28 @@
|
||||
}
|
||||
|
||||
|
||||
/** Error Dialog **/
|
||||
|
||||
.gerrit-ErrorDialog {
|
||||
margin: 10px 10px 10px 10px;
|
||||
}
|
||||
|
||||
.gerrit-ErrorDialog-ErrorType {
|
||||
font-weight: bold;
|
||||
white-space: nowrap;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.gerrit-ErrorDialog-ErrorMessage {
|
||||
}
|
||||
|
||||
.gerrit-ErrorDialog-Buttons {
|
||||
width: 100%;
|
||||
margin-top: 15px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
|
||||
/** Screen **/
|
||||
.gerrit-Screen {
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user