Adding 2.1.1-rc1 tag.
git-svn-id: https://google-web-toolkit.googlecode.com/svn/tags/2.1.1-rc1@9388 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/ExpensesCommon.gwt.xml b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/ExpensesCommon.gwt.xml
index e70fe2e..c4acdb2 100644
--- a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/ExpensesCommon.gwt.xml
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/ExpensesCommon.gwt.xml
@@ -2,10 +2,10 @@
<!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit 0.0.999//EN" "http://google-web-toolkit.googlecode.com/svn/tags/0.0.999/distro-source/core/src/gwt-module.dtd">
<module>
<inherits name='com.google.gwt.activity.Activity' />
+ <inherits name='com.google.gwt.mobile.Mobile'/>
<inherits name='com.google.gwt.place.Place' />
<inherits name='com.google.gwt.requestfactory.RequestFactory'/>
- <!-- <inherits name='com.google.gwt.sample.expenses.client.style.Style'/> -->
- <inherits name='com.google.gwt.mobile.Mobile'/>
+ <inherits name='com.google.gwt.sample.gaerequest.GaeRequest'/>
<inherits name='com.google.gwt.user.cellview.CellView'/>
<source path='client'/>
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpenseReportDetails.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpenseReportDetails.java
index d4c02c7..6ab26c8 100644
--- a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpenseReportDetails.java
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpenseReportDetails.java
@@ -16,6 +16,8 @@
package com.google.gwt.sample.expenses.client;
import com.google.gwt.activity.shared.Activity;
+import com.google.gwt.activity.shared.IsActivity;
+import com.google.gwt.activity.shared.SimpleActivity;
import com.google.gwt.cell.client.AbstractInputCell;
import com.google.gwt.cell.client.Cell;
import com.google.gwt.cell.client.DateCell;
@@ -23,7 +25,6 @@
import com.google.gwt.cell.client.NumberCell;
import com.google.gwt.cell.client.TextCell;
import com.google.gwt.cell.client.ValueUpdater;
-import com.google.gwt.cell.client.Cell.Context;
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
@@ -47,6 +48,7 @@
import com.google.gwt.requestfactory.ui.client.EntityProxyKeyProvider;
import com.google.gwt.resources.client.ImageResource;
import com.google.gwt.safehtml.client.SafeHtmlTemplates;
+import com.google.gwt.safehtml.client.SafeHtmlTemplates.Template;
import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.safehtml.shared.SafeHtmlUtils;
@@ -89,11 +91,11 @@
* Details about the current expense report on the right side of the app,
* including the list of expenses.
*/
-public class ExpenseReportDetails extends Composite implements Activity {
+public class ExpenseReportDetails extends Composite implements IsActivity {
interface Binder extends UiBinder<Widget, ExpenseReportDetails> {
}
-
+
/**
* Fetches an employee and a report in parallel. A fine example of the kind of
* thing that will no longer be necessary when RequestFactory provides server
@@ -127,7 +129,7 @@
});
}
}
-
+
/**
* The resources applied to the table.
*/
@@ -323,6 +325,13 @@
}
}
+ private final Activity activityAspect = new SimpleActivity() {
+ @Override
+ public void start(AcceptsOneWidget panel, EventBus eventBus) {
+ ExpenseReportDetails.this.start(panel, eventBus);
+ }
+ };
+
private static Template template;
/**
@@ -469,6 +478,10 @@
});
}
+ public Activity asActivity() {
+ return activityAspect;
+ }
+
public ReportListPlace getReportListPlace() {
ReportListPlace listPlace = place.getListPlace();
return listPlace == null ? ReportListPlace.ALL : listPlace;
@@ -478,13 +491,6 @@
return reportsLink;
}
- public String mayStop() {
- return null;
- }
-
- public void onCancel() {
- }
-
public void onExpenseRecordChanged(EntityProxyChange<ExpenseProxy> event) {
final EntityProxyId<ExpenseProxy> proxyId = event.getProxyId();
@@ -531,10 +537,17 @@
}
}
- public void onStop() {
+ /**
+ * In this application, called by {@link ExpensesActivityMapper} each time a
+ * ReportListPlace is posted. In a more typical set up, this would be a
+ * constructor argument to a one shot activity, perhaps managing a shared
+ * widget view instance.
+ */
+ public void updateForPlace(final ReportPlace place) {
+ this.place = place;
}
- public void start(AcceptsOneWidget panel, EventBus eventBus) {
+ void start(AcceptsOneWidget panel, EventBus eventBus) {
final ReportListPlace listPlace = place.getListPlace();
if (listPlace.getEmployeeId() == null) {
@@ -575,16 +588,6 @@
}
/**
- * In this application, called by {@link ExpensesActivityMapper} each time a
- * ReportListPlace is posted. In a more typical set up, this would be a
- * constructor argument to a one shot activity, perhaps managing a shared
- * widget view instance.
- */
- public void updateForPlace(final ReportPlace place) {
- this.place = place;
- }
-
- /**
* Add a column of a {@link Comparable} type using default comparators.
*
* @param <C> the column type
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpenseReportList.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpenseReportList.java
index 2df0dbf..e970c24 100644
--- a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpenseReportList.java
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpenseReportList.java
@@ -16,6 +16,8 @@
package com.google.gwt.sample.expenses.client;
import com.google.gwt.activity.shared.Activity;
+import com.google.gwt.activity.shared.IsActivity;
+import com.google.gwt.activity.shared.SimpleActivity;
import com.google.gwt.cell.client.AbstractCell;
import com.google.gwt.cell.client.Cell;
import com.google.gwt.cell.client.DateCell;
@@ -76,7 +78,7 @@
* The list of expense reports on the right side of the app.
*/
public class ExpenseReportList extends Composite implements
- EntityProxyChange.Handler<ReportProxy>, Activity {
+ EntityProxyChange.Handler<ReportProxy>, IsActivity {
interface Binder extends UiBinder<Widget, ExpenseReportList> {
}
@@ -177,6 +179,23 @@
}
}
+ private final Activity activityAspect = new SimpleActivity() {
+ @Override
+ public void onCancel() {
+ ExpenseReportList.this.onCancel();
+ }
+
+ @Override
+ public void onStop() {
+ ExpenseReportList.this.onStop();
+ }
+
+ @Override
+ public void start(AcceptsOneWidget panel, EventBus eventBus) {
+ ExpenseReportList.this.start(panel, eventBus);
+ }
+ };
+
private static final ProvidesKey<ReportProxy> keyProvider = new EntityProxyKeyProvider<ReportProxy>();
/**
@@ -337,17 +356,13 @@
});
}
- public String mayStop() {
- return null;
- }
-
- public void onCancel() {
- onStop();
+ public Activity asActivity() {
+ return activityAspect;
}
public void onProxyChange(EntityProxyChange<ReportProxy> event) {
EntityProxyId<ReportProxy> changedId = event.getProxyId();
- List<ReportProxy> records = table.getDisplayedItems();
+ List<ReportProxy> records = table.getVisibleItems();
int i = 0;
for (ReportProxy record : records) {
if (record != null && changedId.equals(record.stableId())) {
@@ -359,24 +374,10 @@
}
}
- public void onStop() {
- running = false;
- refreshTimer.cancel();
- }
-
public void setListener(Listener listener) {
this.listener = listener;
}
- public void start(AcceptsOneWidget panel, EventBus eventBus) {
- running = true;
- doUpdateForPlace();
-
- EntityProxyChange.registerForProxyType(eventBus, ReportProxy.class, this);
- requestReports(false);
- panel.setWidget(this);
- }
-
/**
* In this application, called by {@link ExpensesActivityMapper} each time a
* ReportListPlace is posted. In a more typical set up, this would be a
@@ -398,6 +399,24 @@
return p;
}
+ void onCancel() {
+ onStop();
+ }
+
+ void onStop() {
+ running = false;
+ refreshTimer.cancel();
+ }
+
+ void start(AcceptsOneWidget panel, EventBus eventBus) {
+ running = true;
+ doUpdateForPlace();
+
+ EntityProxyChange.registerForProxyType(eventBus, ReportProxy.class, this);
+ requestReports(false);
+ panel.setWidget(this);
+ }
+
/**
* Add a sortable column to the table.
*
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpensesActivityMapper.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpensesActivityMapper.java
index ed58976..c50f736 100644
--- a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpensesActivityMapper.java
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpensesActivityMapper.java
@@ -38,12 +38,12 @@
public Activity getActivity(Place place) {
if (place instanceof ReportListPlace) {
expenseList.updateForPlace((ReportListPlace) place);
- return expenseList;
+ return expenseList.asActivity();
}
if (place instanceof ReportPlace) {
expenseDetails.updateForPlace((ReportPlace) place);
- return expenseDetails;
+ return expenseDetails.asActivity();
}
return null;
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpensesApp.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpensesApp.java
index 0975f0a..fe52102 100644
--- a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpensesApp.java
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpensesApp.java
@@ -23,17 +23,11 @@
import com.google.gwt.place.shared.PlaceController;
import com.google.gwt.place.shared.PlaceHistoryHandler;
import com.google.gwt.requestfactory.shared.EntityProxyId;
-import com.google.gwt.requestfactory.shared.Receiver;
-import com.google.gwt.requestfactory.shared.RequestEvent;
-import com.google.gwt.requestfactory.shared.UserInformationProxy;
-import com.google.gwt.requestfactory.ui.client.AuthenticationFailureHandler;
-import com.google.gwt.requestfactory.ui.client.LoginWidget;
import com.google.gwt.sample.expenses.client.place.ReportListPlace;
import com.google.gwt.sample.expenses.client.place.ReportPlace;
import com.google.gwt.sample.expenses.shared.EmployeeProxy;
-import com.google.gwt.sample.expenses.shared.ExpensesRequestFactory;
import com.google.gwt.sample.expenses.shared.ReportProxy;
-import com.google.gwt.user.client.Window.Location;
+import com.google.gwt.sample.gaerequest.client.ReloadOnAuthenticationFailure;
import com.google.gwt.user.client.ui.HasWidgets;
import java.util.logging.Level;
@@ -55,7 +49,6 @@
private final EventBus eventBus;
private final PlaceController placeController;
private final PlaceHistoryHandler placeHistoryHandler;
- private final ExpensesRequestFactory requestFactory;
private final ExpensesShell shell;
private EntityProxyId<EmployeeProxy> lastEmployee;
@@ -63,12 +56,11 @@
public ExpensesApp(ActivityManager activityManager, EventBus eventBus,
PlaceController placeController, PlaceHistoryHandler placeHistoryHandler,
- ExpensesRequestFactory requestFactory, ExpensesShell shell) {
+ ExpensesShell shell) {
this.activityManager = activityManager;
this.eventBus = eventBus;
this.placeController = placeController;
this.placeHistoryHandler = placeHistoryHandler;
- this.requestFactory = requestFactory;
this.shell = shell;
}
@@ -94,18 +86,7 @@
});
// Check for Authentication failures or mismatches
- RequestEvent.register(eventBus, new AuthenticationFailureHandler());
-
- // Kick off the login widget
- final LoginWidget login = shell.getLoginWidget();
- Receiver<UserInformationProxy> receiver = new Receiver<UserInformationProxy>() {
- @Override
- public void onSuccess(UserInformationProxy userInformationRecord) {
- login.setUserInformation(userInformationRecord);
- }
- };
- requestFactory.userInformationRequest().getCurrentUserInformation(
- Location.getHref()).fire(receiver);
+ new ReloadOnAuthenticationFailure().register(eventBus);
// Listen for requests from ExpenseTree.
expenseTree.setListener(new ExpenseTree.Listener() {
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpensesMobile.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpensesMobile.java
index e7ef3f4..6f6362b 100644
--- a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpensesMobile.java
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpensesMobile.java
@@ -20,21 +20,25 @@
import com.google.gwt.event.shared.EventBus;
import com.google.gwt.event.shared.SimpleEventBus;
import com.google.gwt.requestfactory.shared.Receiver;
-import com.google.gwt.requestfactory.shared.RequestEvent;
-import com.google.gwt.requestfactory.shared.UserInformationProxy;
-import com.google.gwt.requestfactory.ui.client.AuthenticationFailureHandler;
-import com.google.gwt.requestfactory.ui.client.LoginWidget;
import com.google.gwt.sample.expenses.shared.EmployeeProxy;
import com.google.gwt.sample.expenses.shared.ExpensesRequestFactory;
+import com.google.gwt.sample.gaerequest.client.GaeAuthRequestTransport;
+import com.google.gwt.sample.gaerequest.client.LoginWidget;
+import com.google.gwt.sample.gaerequest.client.ReloadOnAuthenticationFailure;
import com.google.gwt.user.client.Window;
-import com.google.gwt.user.client.Window.Location;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RootPanel;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
/**
* Entry point for the mobile version of the Expenses app.
+ * <p>
+ * TODO Should be using ExpenseFactory
*/
public class ExpensesMobile implements EntryPoint {
+ private static final Logger log = Logger.getLogger(ExpensesMobile.class.getName());
/**
* The url parameter that specifies the employee id.
@@ -77,8 +81,7 @@
public void onModuleLoad() {
GWT.setUncaughtExceptionHandler(new GWT.UncaughtExceptionHandler() {
public void onUncaughtException(Throwable e) {
- Window.alert("Error: " + e.getMessage());
- // placeController.goTo(Place.NOWHERE);
+ log.log(Level.SEVERE, e.getMessage(), e);
}
});
@@ -96,29 +99,18 @@
final EventBus eventBus = new SimpleEventBus();
final ExpensesRequestFactory requestFactory = GWT.create(ExpensesRequestFactory.class);
- requestFactory.initialize(eventBus);
+ requestFactory.initialize(eventBus, new GaeAuthRequestTransport(eventBus));
requestFactory.employeeRequest().findEmployee(employeeId).fire(
new Receiver<EmployeeProxy>() {
@Override
public void onSuccess(EmployeeProxy employee) {
final ExpensesMobileShell shell = new ExpensesMobileShell(eventBus,
- requestFactory, employee);
+ requestFactory, employee, new LoginWidget(requestFactory));
RootPanel.get().add(shell);
// Check for Authentication failures or mismatches
- RequestEvent.register(eventBus, new AuthenticationFailureHandler());
-
- // Add a login widget to the page
- final LoginWidget login = shell.getLoginWidget();
- Receiver<UserInformationProxy> receiver = new Receiver<UserInformationProxy>() {
- @Override
- public void onSuccess(UserInformationProxy userInformationRecord) {
- login.setUserInformation(userInformationRecord);
- }
- };
- requestFactory.userInformationRequest().getCurrentUserInformation(
- Location.getHref()).fire(receiver);
+ new ReloadOnAuthenticationFailure().register(eventBus);
}
});
}
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpensesMobileShell.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpensesMobileShell.java
index f35cf1d..9d72842 100644
--- a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpensesMobileShell.java
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpensesMobileShell.java
@@ -19,11 +19,11 @@
import com.google.gwt.dom.client.Element;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.shared.EventBus;
-import com.google.gwt.requestfactory.ui.client.LoginWidget;
import com.google.gwt.sample.expenses.shared.EmployeeProxy;
import com.google.gwt.sample.expenses.shared.ExpenseProxy;
import com.google.gwt.sample.expenses.shared.ExpensesRequestFactory;
import com.google.gwt.sample.expenses.shared.ReportProxy;
+import com.google.gwt.sample.gaerequest.client.LoginWidget;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.uibinder.client.UiHandler;
@@ -39,13 +39,19 @@
*/
public class ExpensesMobileShell extends Composite {
- interface ShellUiBinder extends UiBinder<Widget, ExpensesMobileShell> { }
+ interface ShellUiBinder extends UiBinder<Widget, ExpensesMobileShell> {
+ }
+
private static ShellUiBinder BINDER = GWT.create(ShellUiBinder.class);
- @UiField SimplePanel container;
- @UiField HTML backButton, addButton, refreshButton, customButton;
- @UiField LoginWidget loginWidget;
- @UiField Element titleSpan;
+ @UiField
+ SimplePanel container;
+ @UiField
+ HTML backButton, addButton, refreshButton, customButton;
+ @UiField(provided = true)
+ final LoginWidget loginWidget;
+ @UiField
+ Element titleSpan;
private MobileReportList reportList;
private MobileExpenseList expenseList;
@@ -59,10 +65,12 @@
private ArrayList<MobilePage> pages = new ArrayList<MobilePage>();
public ExpensesMobileShell(EventBus eventBus,
- ExpensesRequestFactory requestFactory, EmployeeProxy employee) {
+ ExpensesRequestFactory requestFactory, EmployeeProxy employee,
+ LoginWidget loginWidget) {
this.eventBus = eventBus;
this.requestFactory = requestFactory;
this.employee = employee;
+ this.loginWidget = loginWidget;
initWidget(BINDER.createAndBindUi(this));
showReportList();
@@ -74,7 +82,7 @@
public LoginWidget getLoginWidget() {
return loginWidget;
}
-
+
@UiHandler("addButton")
void onAdd(@SuppressWarnings("unused") ClickEvent evt) {
topPage().onAdd();
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpensesMobileShell.ui.xml b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpensesMobileShell.ui.xml
index 6a392fd..f6727a5 100644
--- a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpensesMobileShell.ui.xml
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpensesMobileShell.ui.xml
@@ -3,17 +3,37 @@
xmlns:ui='urn:ui:com.google.gwt.uibinder'
xmlns:m='urn:import:com.google.gwt.mobile.client'
xmlns:r='urn:import:com.google.gwt.requestfactory.ui.client'
- xmlns:g='urn:import:com.google.gwt.user.client.ui'>
+ xmlns:g='urn:import:com.google.gwt.user.client.ui'
+ xmlns:a='urn:import:com.google.gwt.sample.gaerequest.client'>
- <ui:style field='mobile' src='mobile.css'/>
+ <ui:image field='add'/>
+ <ui:image field='refresh'/>
+ <ui:style field='mobile' src='mobile.css'>
+ @sprite .refresh {
+ gwt-image: "refresh";
+ }
+ @sprite .add {
+ gwt-image: "add";
+ }
+ .button {
+ cursor: pointer;
+ }
+ .backButton {
+ cursor: pointer;
+ }
+ .customButton {
+ cursor: pointer;
+ }
+ </ui:style>
+
<g:HTMLPanel>
- <r:LoginWidget styleName='{mobile.login}' ui:field="loginWidget"/>
+ <a:LoginWidget styleName='{mobile.login}' ui:field="loginWidget"/>
<div class='{mobile.bar}'>
<g:HTML ui:field='backButton' styleName='{mobile.backButton}'><div>Back</div></g:HTML>
- <g:HTML ui:field='addButton' styleName='{mobile.button}'><img src='images/add.png'/></g:HTML>
+ <g:HTML ui:field='addButton' addStyleNames='{mobile.button}'><div class='{mobile.add}'/></g:HTML>
<g:HTML ui:field='customButton' styleName='{mobile.customButton}'/>
- <g:HTML ui:field='refreshButton' styleName='{mobile.button}'><img src='images/refresh.png'/></g:HTML>
+ <g:HTML ui:field='refreshButton' styleName='{mobile.button}'><div class='{mobile.refresh}'/></g:HTML>
<div class='{mobile.title}' ui:field='titleSpan'>Expenses</div>
</div>
<g:SimplePanel ui:field='container'/>
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpensesShell.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpensesShell.java
index bee5035..0fc57c0 100644
--- a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpensesShell.java
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpensesShell.java
@@ -16,7 +16,7 @@
package com.google.gwt.sample.expenses.client;
import com.google.gwt.core.client.GWT;
-import com.google.gwt.requestfactory.ui.client.LoginWidget;
+import com.google.gwt.sample.gaerequest.client.LoginWidget;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.user.client.ui.Composite;
@@ -35,21 +35,25 @@
@UiField(provided = true)
final ExpenseReportList expenseList;
-
+
@UiField(provided = true)
final ExpenseReportDetails expenseDetails;
@UiField(provided = true)
final ExpenseTree expenseTree;
-
+
+ @UiField(provided = true)
+ final LoginWidget loginWidget;
+
@UiField SlidingPanel slidingPanel;
- @UiField LoginWidget loginWidget;
@UiField DockLayoutPanel dockLayout;
- public ExpensesShell(ExpenseTree expenseTree, ExpenseReportList expenseList, ExpenseReportDetails expenseDetails) {
+ public ExpensesShell(ExpenseTree expenseTree, ExpenseReportList expenseList,
+ ExpenseReportDetails expenseDetails, LoginWidget loginWidget) {
this.expenseTree = expenseTree;
this.expenseList = expenseList;
this.expenseDetails = expenseDetails;
+ this.loginWidget = loginWidget;
initWidget(uiBinder.createAndBindUi(this));
}
@@ -68,7 +72,7 @@
public LoginWidget getLoginWidget() {
return loginWidget;
}
-
+
public HasOneWidget getPanel() {
return slidingPanel;
}
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpensesShell.ui.xml b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpensesShell.ui.xml
index a146dbd..391f1ee 100644
--- a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpensesShell.ui.xml
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpensesShell.ui.xml
@@ -4,7 +4,9 @@
xmlns:g='urn:import:com.google.gwt.user.client.ui'
xmlns:m='urn:import:com.google.gwt.mobile.client'
xmlns:r='urn:import:com.google.gwt.requestfactory.ui.client'
- xmlns:e='urn:import:com.google.gwt.sample.expenses.client'>
+ xmlns:e='urn:import:com.google.gwt.sample.expenses.client'
+ xmlns:a='urn:import:com.google.gwt.sample.gaerequest.client'>
+
<ui:with field='styles' type='com.google.gwt.sample.expenses.client.style.Styles' />
@@ -44,7 +46,7 @@
<g:DockLayoutPanel unit='PX'>
<g:north size='96'>
<g:HTMLPanel styleName='{style.title}'>
- <r:LoginWidget styleName='{style.login}' ui:field="loginWidget"/>
+ <a:LoginWidget styleName='{style.login}' ui:field="loginWidget"/>
<table height='100%' cellpadding='8' cellspacing='0'>
<tr>
<td>
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/MobileReportEntry.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/MobileReportEntry.java
index 8d541a1..0270578 100644
--- a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/MobileReportEntry.java
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/MobileReportEntry.java
@@ -166,9 +166,15 @@
@SuppressWarnings("deprecation")
private void showCreationDate(Date d) {
- // TODO(jgw): Use non-deprecated date methods for this.
- dateYear.setSelectedIndex(d.getYear() - 100);
- dateMonth.setSelectedIndex(d.getMonth());
- dateDay.setSelectedIndex(d.getDate() - 1);
+ if (d != null) {
+ // TODO(jgw): Use non-deprecated date methods for this.
+ dateYear.setSelectedIndex(d.getYear() - 100);
+ dateMonth.setSelectedIndex(d.getMonth());
+ dateDay.setSelectedIndex(d.getDate() - 1);
+ } else {
+ dateYear.setSelectedIndex(0);
+ dateMonth.setSelectedIndex(0);
+ dateDay.setSelectedIndex(0);
+ }
}
}
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/add.png b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/add.png
new file mode 100644
index 0000000..4db1652
--- /dev/null
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/add.png
Binary files differ
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ioc/ExpensesFactory.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ioc/ExpensesFactory.java
index aa5ec92..62e7cb6 100644
--- a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ioc/ExpensesFactory.java
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ioc/ExpensesFactory.java
@@ -32,6 +32,8 @@
import com.google.gwt.sample.expenses.client.place.ReportListPlace;
import com.google.gwt.sample.expenses.client.place.ReportPlace;
import com.google.gwt.sample.expenses.shared.ExpensesRequestFactory;
+import com.google.gwt.sample.gaerequest.client.GaeAuthRequestTransport;
+import com.google.gwt.sample.gaerequest.client.LoginWidget;
/**
* In charge of instantiation.
@@ -41,6 +43,7 @@
public class ExpensesFactory {
private final EventBus eventBus = new SimpleEventBus();
+ private final GaeAuthRequestTransport requestTransport = new GaeAuthRequestTransport(eventBus);
private final ExpensesRequestFactory requestFactory = GWT.create(ExpensesRequestFactory.class);
private final ExpensesPlaceHistoryMapper historyMapper = GWT.create(ExpensesPlaceHistoryMapper.class);
private final PlaceHistoryHandler placeHistoryHandler;
@@ -49,21 +52,23 @@
private final ExpenseReportList expenseList = new ExpenseReportList(requestFactory);
private final ExpenseReportDetails expenseDetails = new ExpenseReportDetails(
requestFactory);
+ private final LoginWidget loginWidget = new LoginWidget(requestFactory);
+
private final ActivityMapper activityMapper = new ExpensesActivityMapper(
expenseDetails, expenseList);
private final ActivityManager activityManager = new ActivityManager(
activityMapper, eventBus);
public ExpensesFactory() {
- requestFactory.initialize(eventBus);
+ requestFactory.initialize(eventBus, requestTransport);
historyMapper.setFactory(this);
placeHistoryHandler = new PlaceHistoryHandler(historyMapper);
}
public ExpensesApp getExpensesApp() {
return new ExpensesApp(activityManager, eventBus, placeController,
- placeHistoryHandler, requestFactory, new ExpensesShell(expenseTree,
- expenseList, expenseDetails));
+ placeHistoryHandler, new ExpensesShell(expenseTree,
+ expenseList, expenseDetails, loginWidget));
}
/**
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/refresh.png b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/refresh.png
new file mode 100644
index 0000000..454c107
--- /dev/null
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/refresh.png
Binary files differ
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/server/domain/GaeUserInformation.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/server/domain/GaeUserInformation.java
deleted file mode 100644
index f9d78e1..0000000
--- a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/server/domain/GaeUserInformation.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright 2010 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.gwt.sample.expenses.server.domain;
-
-import com.google.appengine.api.users.User;
-import com.google.appengine.api.users.UserService;
-import com.google.appengine.api.users.UserServiceFactory;
-import com.google.gwt.requestfactory.server.UserInformation;
-
-/**
- * A user information class that uses the Google App Engine authentication
- * framework.
- */
-public class GaeUserInformation extends UserInformation {
- private static UserService userService = UserServiceFactory.getUserService();
-
- public static GaeUserInformation getCurrentUserInformation(String redirectUrl) {
- return new GaeUserInformation(redirectUrl);
- }
-
- public GaeUserInformation(String redirectUrl) {
- super(redirectUrl);
- }
-
- @Override
- public String getEmail() {
- User user = userService.getCurrentUser();
- if (user == null) {
- return "";
- }
- return user.getEmail();
- }
-
- @Override
- public Long getId() {
- User user = userService.getCurrentUser();
- if (user == null) {
- return 0L;
- }
- return new Long(user.hashCode());
- }
-
- @Override
- public String getLoginUrl() {
- return userService.createLoginURL(redirectUrl);
- }
-
- @Override
- public String getLogoutUrl() {
- return userService.createLogoutURL(redirectUrl);
- }
-
- @Override
- public String getName() {
- User user = userService.getCurrentUser();
- if (user == null) {
- return "";
- }
- return user.getNickname();
- }
-
- @Override
- public boolean isUserLoggedIn() {
- return userService.isUserLoggedIn();
- }
-
- /**
- * Does nothing since in GAE authentication, the unique ID is provided by
- * the user service and is based on a hash in the User object.
- */
- @Override
- public void setId(Long id) {
- // Do nothing
- }
-
-}
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/shared/EmployeeProxy.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/shared/EmployeeProxy.java
index fffc9f1..4a92b9c 100644
--- a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/shared/EmployeeProxy.java
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/shared/EmployeeProxy.java
@@ -20,10 +20,7 @@
import com.google.gwt.requestfactory.shared.ProxyFor;
/**
- * "API Generated" DTO interface based on
- * {@link com.google.gwt.sample.expenses.server.domain.Employee}.
- * <p>
- * IRL this class will be generated by a JPA-savvy tool run before compilation.
+ * Employee DTO.
*/
@ProxyFor(com.google.gwt.sample.expenses.server.domain.Employee.class)
public interface EmployeeProxy extends EntityProxy {
@@ -31,6 +28,10 @@
String getDisplayName();
+ /*
+ * TODO You shouldn't need to expose Ids like this.
+ * Instead use EntityProxy.stableId() and RequestFactory.find()
+ */
Long getId();
String getPassword();
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/shared/EmployeeRequest.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/shared/EmployeeRequest.java
index 0391a48..ffc7b57 100644
--- a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/shared/EmployeeRequest.java
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/shared/EmployeeRequest.java
@@ -24,11 +24,7 @@
import java.util.List;
/**
- * "API Generated" request selector interface implemented by objects that give
- * client access to the methods of
- * {@link com.google.gwt.sample.expenses.server.domain.Employee}.
- * <p>
- * IRL this class will be generated by a JPA-savvy tool run before compilation.
+ * Builds requests for the Employee service.
*/
@Service(Employee.class)
public interface EmployeeRequest extends RequestContext {
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/shared/ExpenseProxy.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/shared/ExpenseProxy.java
index eabf78e..1919f7a 100644
--- a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/shared/ExpenseProxy.java
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/shared/ExpenseProxy.java
@@ -22,10 +22,7 @@
import java.util.Date;
/**
- * "API Generated" DTO interface based on
- * {@link com.google.gwt.sample.expenses.server.domain.Expense}.
- * <p>
- * IRL this class will be generated by a JPA-savvy tool run before compilation.
+ * Expense DTO.
*/
@ProxyFor(com.google.gwt.sample.expenses.server.domain.Expense.class)
public interface ExpenseProxy extends EntityProxy {
@@ -39,6 +36,10 @@
String getDescription();
+ /*
+ * TODO You shouldn't need to expose Ids like this.
+ * Instead use EntityProxy.stableId() and RequestFactory.find()
+ */
Long getId();
String getReasonDenied();
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/shared/ExpenseRequest.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/shared/ExpenseRequest.java
index 38a279c..9f61c3a 100644
--- a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/shared/ExpenseRequest.java
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/shared/ExpenseRequest.java
@@ -24,11 +24,7 @@
import java.util.List;
/**
- * "API Generated" request selector interface implemented by objects that give
- * client access to the methods of
- * {@link com.google.gwt.sample.expenses.server.domain.Expense}.
- * <p>
- * IRL this class will be generated by a JPA-savvy tool run before compilation.
+ * Builds requests for the ExpenseRequest service.
*/
@Service(Expense.class)
public interface ExpenseRequest extends RequestContext {
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/shared/ExpensesEntityTypesProcessor.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/shared/ExpensesEntityTypesProcessor.java
index 23b7003..9ac89f4 100644
--- a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/shared/ExpensesEntityTypesProcessor.java
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/shared/ExpensesEntityTypesProcessor.java
@@ -22,11 +22,6 @@
import java.util.Set;
/**
- * "API Generated" tool for resolving an arbitray Class to a specific proxy
- * type.
- * <p>
- * IRL this class will be generated by a JPA-savvy tool run before compilation.
- * <p>
* A helper class for dealing with proxy types. Subclass it and override the
* various handle methods for type specific handling of proxy objects or
* classes, then call {@link #process(Class)} or {@link #process(Object)}.
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/shared/ExpensesRequestFactory.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/shared/ExpensesRequestFactory.java
index 201abcb..53b56a0 100644
--- a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/shared/ExpensesRequestFactory.java
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/shared/ExpensesRequestFactory.java
@@ -16,17 +16,13 @@
package com.google.gwt.sample.expenses.shared;
import com.google.gwt.requestfactory.shared.RequestFactory;
-import com.google.gwt.requestfactory.shared.UserInformationRequest;
+import com.google.gwt.sample.gaerequest.shared.MakesGaeRequests;
/**
- * "API generated" factory interface to build request objects for the methods of
- * {@link com.google.gwt.sample.expenses.server.domain}. Client code can
- * GWT.create() an instance of this interface to build and fire request objects.
- * <p>
- * IRL this interface will be generated by a JPA-savvy tool run before
- * compilation.
+ * RequestFactory interface. Instances created via {@link com.google.gwt.core.client.GWT#create}
+ * can insantiate RPC request objects.
*/
-public interface ExpensesRequestFactory extends RequestFactory {
+public interface ExpensesRequestFactory extends RequestFactory, MakesGaeRequests {
/**
* @return a request selector
@@ -42,10 +38,4 @@
* @return a request selector
*/
ReportRequest reportRequest();
-
- /**
- * @return a request selector
- */
- UserInformationRequest userInformationRequest();
-
}
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/shared/ReportProxy.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/shared/ReportProxy.java
index 0bf0c48..846a949 100644
--- a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/shared/ReportProxy.java
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/shared/ReportProxy.java
@@ -22,10 +22,7 @@
import java.util.Date;
/**
- * "API Generated" DTO interface based on
- * {@link com.google.gwt.sample.expenses.server.domain.Report}.
- * <p>
- * IRL this class will be generated by a JPA-savvy tool run before compilation.
+ * Report DTO.
*/
@ProxyFor(com.google.gwt.sample.expenses.server.domain.Report.class)
public interface ReportProxy extends EntityProxy {
@@ -35,6 +32,10 @@
String getDepartment();
+ /*
+ * TODO You shouldn't need to expose Ids like this.
+ * Instead use EntityProxy.stableId() and RequestFactory.find()
+ */
Long getId();
String getNotes();
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/shared/ReportRequest.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/shared/ReportRequest.java
index 40af51e..2cb6a73 100644
--- a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/shared/ReportRequest.java
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/shared/ReportRequest.java
@@ -24,11 +24,7 @@
import java.util.List;
/**
- * "API Generated" request selector interface implemented by objects that give
- * client access to the methods of
- * {@link com.google.gwt.sample.expenses.server.domain.Report}.
- * <p>
- * IRL this class will be generated by a JPA-savvy tool run before compilation.
+ * Builds requests for the Report service.
*/
@Service(Report.class)
public interface ReportRequest extends RequestContext {
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/gaerequest/GaeRequest.gwt.xml b/samples/expenses/src/main/java/com/google/gwt/sample/gaerequest/GaeRequest.gwt.xml
new file mode 100644
index 0000000..4b46229
--- /dev/null
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/gaerequest/GaeRequest.gwt.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit 0.0.999//EN" "http://google-web-toolkit.googlecode.com/svn/tags/0.0.999/distro-source/core/src/gwt-module.dtd">
+<module>
+ <inherits name='com.google.gwt.requestfactory.RequestFactory'/>
+
+ <source path='client'/>
+ <source path='shared'/>
+</module>
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/gaerequest/client/GaeAuthRequestTransport.java b/samples/expenses/src/main/java/com/google/gwt/sample/gaerequest/client/GaeAuthRequestTransport.java
new file mode 100644
index 0000000..74b8dba
--- /dev/null
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/gaerequest/client/GaeAuthRequestTransport.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2010 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.gwt.sample.gaerequest.client;
+
+import com.google.gwt.event.shared.EventBus;
+import com.google.gwt.http.client.Request;
+import com.google.gwt.http.client.RequestCallback;
+import com.google.gwt.http.client.Response;
+import com.google.gwt.requestfactory.client.DefaultRequestTransport;
+import com.google.gwt.requestfactory.shared.ServerFailure;
+
+/**
+ * Extends DefaultRequestTransport to handle the authentication failures
+ * reported by {@link com.google.gwt.sample.gaerequest.server.GaeAuthFilter}
+ */
+public class GaeAuthRequestTransport extends DefaultRequestTransport {
+ private final EventBus eventBus;
+
+ public GaeAuthRequestTransport(EventBus eventBus) {
+ this.eventBus = eventBus;
+ }
+
+ @Override
+ protected RequestCallback createRequestCallback(
+ final TransportReceiver receiver) {
+ final RequestCallback superCallback = super.createRequestCallback(receiver);
+
+ return new RequestCallback() {
+ public void onResponseReceived(Request request, Response response) {
+ /*
+ * The GaeAuthFailure filter responds with Response.SC_UNAUTHORIZED and
+ * adds a "login" url header if the user is not logged in. When we
+ * receive that combo, post an event so that the app can handle things
+ * as it sees fit.
+ */
+
+ if (Response.SC_UNAUTHORIZED == response.getStatusCode()) {
+ String loginUrl = response.getHeader("login");
+ if (loginUrl != null) {
+ /*
+ * Hand the receiver a non-fatal callback, so that
+ * com.google.gwt.requestfactory.shared.Receiver will not post a
+ * runtime exception.
+ */
+ receiver.onTransportFailure(new ServerFailure(
+ "Unauthenticated user", null, null, false /* not fatal */));
+ eventBus.fireEvent(new GaeAuthenticationFailureEvent(loginUrl));
+ return;
+ }
+ }
+ superCallback.onResponseReceived(request, response);
+ }
+
+ public void onError(Request request, Throwable exception) {
+ superCallback.onError(request, exception);
+ }
+ };
+ }
+}
diff --git a/user/src/com/google/gwt/requestfactory/shared/RequestEvent.java b/samples/expenses/src/main/java/com/google/gwt/sample/gaerequest/client/GaeAuthenticationFailureEvent.java
similarity index 61%
rename from user/src/com/google/gwt/requestfactory/shared/RequestEvent.java
rename to samples/expenses/src/main/java/com/google/gwt/sample/gaerequest/client/GaeAuthenticationFailureEvent.java
index c08641d..fab5e22 100644
--- a/user/src/com/google/gwt/requestfactory/shared/RequestEvent.java
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/gaerequest/client/GaeAuthenticationFailureEvent.java
@@ -13,7 +13,7 @@
* License for the specific language governing permissions and limitations under
* the License.
*/
-package com.google.gwt.requestfactory.shared;
+package com.google.gwt.sample.gaerequest.client;
import com.google.gwt.event.shared.EventBus;
import com.google.gwt.event.shared.EventHandler;
@@ -22,50 +22,41 @@
import com.google.gwt.http.client.Response;
/**
- * An event posted whenever an RPC request is sent or its response is received.
+ * An event posted when an authentication failure is detected.
*/
-public class RequestEvent extends GwtEvent<RequestEvent.Handler> {
+public class GaeAuthenticationFailureEvent extends GwtEvent<GaeAuthenticationFailureEvent.Handler> {
/**
* Implemented by handlers of this type of event.
*/
public interface Handler extends EventHandler {
/**
- * Called when a {@link RequestEvent} is fired.
+ * Called when a {@link GaeAuthenticationFailureEvent} is fired.
*
- * @param requestEvent a {@link RequestEvent} instance
+ * @param requestEvent a {@link GaeAuthenticationFailureEvent} instance
*/
- void onRequestEvent(RequestEvent requestEvent);
- }
-
- /**
- * The request state.
- */
- public enum State {
- SENT, RECEIVED
+ void onAuthFailure(GaeAuthenticationFailureEvent requestEvent);
}
private static final Type<Handler> TYPE = new Type<Handler>();
/**
- * Register a {@link RequestEvent.Handler} on an {@link EventBus}.
+ * Register a {@link GaeAuthenticationFailureEvent.Handler} on an {@link EventBus}.
*
* @param eventBus the {@link EventBus}
- * @param handler a {@link RequestEvent.Handler}
+ * @param handler a {@link GaeAuthenticationFailureEvent.Handler}
* @return a {@link HandlerRegistration} instance
*/
public static HandlerRegistration register(EventBus eventBus,
- RequestEvent.Handler handler) {
+ GaeAuthenticationFailureEvent.Handler handler) {
return eventBus.addHandler(TYPE, handler);
}
- private final State state;
-
/**
* Will only be non-null if this is an event of type {@link State#RECEIVED},
* and the RPC was successful.
*/
- private final Response response;
+ private final String loginUrl;
/**
* Constructs a new @{link RequestEvent}.
@@ -73,9 +64,8 @@
* @param state a {@link State} instance
* @param response a {@link Response} instance
*/
- public RequestEvent(State state, Response response) {
- this.state = state;
- this.response = response;
+ public GaeAuthenticationFailureEvent(String loginUrl) {
+ this.loginUrl = loginUrl;
}
@Override
@@ -84,25 +74,16 @@
}
/**
- * Returns the {@link Response} associated with this event.
- *
+ * Returns the URL the user can visit to reauthenticate.
+ *
* @return a {@link Response} instance
*/
- public Response getResponse() {
- return response;
- }
-
- /**
- * Returns the {@link State} associated with this event.
- *
- * @return a {@link State} instance
- */
- public State getState() {
- return state;
+ public String getLoginUrl() {
+ return loginUrl;
}
@Override
protected void dispatch(Handler handler) {
- handler.onRequestEvent(this);
+ handler.onAuthFailure(this);
}
}
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/gaerequest/client/LoginWidget.java b/samples/expenses/src/main/java/com/google/gwt/sample/gaerequest/client/LoginWidget.java
new file mode 100644
index 0000000..be5a9cf
--- /dev/null
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/gaerequest/client/LoginWidget.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2010 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.gwt.sample.gaerequest.client;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.dom.client.SpanElement;
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.requestfactory.shared.Receiver;
+import com.google.gwt.sample.gaerequest.shared.GaeUser;
+import com.google.gwt.sample.gaerequest.shared.GaeUserServiceRequest;
+import com.google.gwt.sample.gaerequest.shared.MakesGaeRequests;
+import com.google.gwt.uibinder.client.UiBinder;
+import com.google.gwt.uibinder.client.UiField;
+import com.google.gwt.uibinder.client.UiHandler;
+import com.google.gwt.user.client.Window.Location;
+import com.google.gwt.user.client.ui.Anchor;
+import com.google.gwt.user.client.ui.Composite;
+import com.google.gwt.user.client.ui.Widget;
+
+/**
+ * A simple widget which displays info about the user and a logout link. In real
+ * life you'd probably blow this up into MVP parts.
+ * <p>
+ * On the other hand, it's pleasant that this widget is completely self
+ * contained, taking care of its own RPC needs when awoken by being attached to
+ * the dom. Hopefully that's not too magical.
+ */
+public class LoginWidget extends Composite {
+ interface Binder extends UiBinder<Widget, LoginWidget> {
+ }
+
+ private static final Binder BINDER = GWT.create(Binder.class);
+
+ private final MakesGaeRequests requests;
+
+ @UiField
+ SpanElement name;
+ @UiField
+ Anchor logoutLink;
+
+ public LoginWidget(MakesGaeRequests requests) {
+ this.requests = requests;
+ initWidget(BINDER.createAndBindUi(this));
+ }
+
+ @Override
+ protected void onLoad() {
+ GaeUserServiceRequest request = requests.userServiceRequest();
+
+ request.createLogoutURL(Location.getHref()).to(new Receiver<String>() {
+ public void onSuccess(String response) {
+ setLogoutUrl(response);
+ }
+ });
+ request.getCurrentUser().to(new Receiver<GaeUser>() {
+ @Override
+ public void onSuccess(GaeUser response) {
+ setUserName(response.getNickname());
+ }
+ });
+ request.fire();
+ }
+
+ public void setUserName(String userName) {
+ name.setInnerText(userName);
+ }
+
+ public void setLogoutUrl(String url) {
+ logoutLink.setHref(url);
+ }
+
+ /**
+ * Squelch clicks of the logout link if no href has been set.
+ */
+ @UiHandler("logoutLink")
+ void handleClick(ClickEvent e) {
+ if ("".equals(logoutLink.getHref())) {
+ e.stopPropagation();
+ }
+ }
+}
diff --git a/user/src/com/google/gwt/requestfactory/ui/client/LoginWidget.ui.xml b/samples/expenses/src/main/java/com/google/gwt/sample/gaerequest/client/LoginWidget.ui.xml
similarity index 60%
rename from user/src/com/google/gwt/requestfactory/ui/client/LoginWidget.ui.xml
rename to samples/expenses/src/main/java/com/google/gwt/sample/gaerequest/client/LoginWidget.ui.xml
index 13259d7..3acf351 100644
--- a/user/src/com/google/gwt/requestfactory/ui/client/LoginWidget.ui.xml
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/gaerequest/client/LoginWidget.ui.xml
@@ -2,14 +2,16 @@
xmlns:g='urn:import:com.google.gwt.user.client.ui'>
<ui:style>
.link {
- cursor: pointer;
+ /* make it look like text */
+ color: inherit;
+ text-decoration: inherit;
}
</ui:style>
<g:HTMLPanel>
<div>
<span ui:field="name">Not logged in</span> |
- <g:InlineLabel ui:field="logoutLink" addStyleNames="{style.link}">Sign out</g:InlineLabel>
+ <g:Anchor ui:field="logoutLink" addStyleNames="{style.link}">Sign out</g:Anchor>
</div>
</g:HTMLPanel>
</ui:UiBinder>
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/gaerequest/client/ReloadOnAuthenticationFailure.java b/samples/expenses/src/main/java/com/google/gwt/sample/gaerequest/client/ReloadOnAuthenticationFailure.java
new file mode 100644
index 0000000..8e5f52f
--- /dev/null
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/gaerequest/client/ReloadOnAuthenticationFailure.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2010 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.gwt.sample.gaerequest.client;
+
+import com.google.gwt.event.shared.EventBus;
+import com.google.gwt.event.shared.HandlerRegistration;
+import com.google.gwt.user.client.Window.Location;
+
+/**
+ * A minimal auth failure handler which takes the user a login page.
+ */
+public class ReloadOnAuthenticationFailure implements
+ GaeAuthenticationFailureEvent.Handler {
+
+ public HandlerRegistration register(EventBus eventBus) {
+ return GaeAuthenticationFailureEvent.register(eventBus, this);
+ }
+
+ public void onAuthFailure(GaeAuthenticationFailureEvent requestEvent) {
+ Location.replace(requestEvent.getLoginUrl());
+ }
+}
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/gaerequest/server/GaeAuthFilter.java b/samples/expenses/src/main/java/com/google/gwt/sample/gaerequest/server/GaeAuthFilter.java
new file mode 100644
index 0000000..96337a5
--- /dev/null
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/gaerequest/server/GaeAuthFilter.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2010 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.gwt.sample.gaerequest.server;
+
+import com.google.appengine.api.users.UserService;
+import com.google.appengine.api.users.UserServiceFactory;
+
+import java.io.IOException;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * A servlet filter that handles basic GAE user authentication.
+ */
+public class GaeAuthFilter implements Filter {
+
+ public void destroy() {
+ }
+
+ public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
+ FilterChain filterChain) throws IOException, ServletException {
+ UserService userService = UserServiceFactory.getUserService();
+ HttpServletRequest request = (HttpServletRequest) servletRequest;
+ HttpServletResponse response = (HttpServletResponse) servletResponse;
+
+ if (!userService.isUserLoggedIn()) {
+ response.setHeader("login", userService.createLoginURL(request.getRequestURI()));
+ response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
+ return;
+ }
+
+ filterChain.doFilter(request, response);
+ }
+
+ public void init(FilterConfig config) {
+ }
+}
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/gaerequest/server/UserServiceLocator.java b/samples/expenses/src/main/java/com/google/gwt/sample/gaerequest/server/UserServiceLocator.java
new file mode 100644
index 0000000..6181b17
--- /dev/null
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/gaerequest/server/UserServiceLocator.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2010 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.gwt.sample.gaerequest.server;
+
+import com.google.appengine.api.users.User;
+import com.google.appengine.api.users.UserService;
+import com.google.appengine.api.users.UserServiceFactory;
+import com.google.gwt.requestfactory.shared.ServiceLocator;
+
+/**
+ * Gives a RequestFactory system access to the Google AppEngine UserService.
+ */
+public class UserServiceLocator implements ServiceLocator {
+ public UserServiceWrapper getInstance(Class<?> clazz) {
+ final UserService service = UserServiceFactory.getUserService();
+ return new UserServiceWrapper() {
+
+ public String createLoginURL(String destinationURL) {
+ return service.createLoginURL(destinationURL);
+ }
+
+ public String createLogoutURL(String destinationURL) {
+ return service.createLogoutURL(destinationURL);
+ }
+
+ public User getCurrentUser() {
+ return service.getCurrentUser();
+ }
+ };
+ }
+}
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/gaerequest/server/UserServiceWrapper.java b/samples/expenses/src/main/java/com/google/gwt/sample/gaerequest/server/UserServiceWrapper.java
new file mode 100644
index 0000000..2c49262
--- /dev/null
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/gaerequest/server/UserServiceWrapper.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2010 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.gwt.sample.gaerequest.server;
+
+import com.google.appengine.api.users.User;
+
+/**
+ * Service object that reduces the visible api of
+ * {@link com.google.appengine.api.users.UserService}. Needed to work around a
+ * limitation of RequestFactory, which cannot yet handle overloaded service
+ * methods.
+ */
+public interface UserServiceWrapper {
+ public String createLoginURL(String destinationURL);
+
+ public String createLogoutURL(String destinationURL);
+
+ public User getCurrentUser();
+}
diff --git a/user/src/com/google/gwt/activity/shared/AbstractActivity.java b/samples/expenses/src/main/java/com/google/gwt/sample/gaerequest/shared/GaeUser.java
similarity index 61%
copy from user/src/com/google/gwt/activity/shared/AbstractActivity.java
copy to samples/expenses/src/main/java/com/google/gwt/sample/gaerequest/shared/GaeUser.java
index 59e596c..28895c3 100644
--- a/user/src/com/google/gwt/activity/shared/AbstractActivity.java
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/gaerequest/shared/GaeUser.java
@@ -13,21 +13,16 @@
* License for the specific language governing permissions and limitations under
* the License.
*/
-package com.google.gwt.activity.shared;
+package com.google.gwt.sample.gaerequest.shared;
+
+import com.google.gwt.requestfactory.shared.ProxyForName;
+import com.google.gwt.requestfactory.shared.ValueProxy;
/**
- * Simple Activity implementation that is always willing to stop,
- * and does nothing onStop and onCancel.
+ * Client visible proxy of Google AppEngine User class.
*/
-public abstract class AbstractActivity implements Activity {
-
- public String mayStop() {
- return null;
- }
-
- public void onCancel() {
- }
-
- public void onStop() {
- }
+@ProxyForName("com.google.appengine.api.users.User")
+public interface GaeUser extends ValueProxy {
+ String getNickname();
+ String getEmail();
}
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/gaerequest/shared/GaeUserServiceRequest.java b/samples/expenses/src/main/java/com/google/gwt/sample/gaerequest/shared/GaeUserServiceRequest.java
new file mode 100644
index 0000000..f1217ab
--- /dev/null
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/gaerequest/shared/GaeUserServiceRequest.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2010 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.gwt.sample.gaerequest.shared;
+
+import com.google.gwt.requestfactory.shared.Request;
+import com.google.gwt.requestfactory.shared.RequestContext;
+import com.google.gwt.requestfactory.shared.ServiceName;
+
+/**
+ * Makes requests of the Google AppEngine UserService.
+ */
+@ServiceName(value = "com.google.gwt.sample.gaerequest.server.UserServiceWrapper",
+ locator = "com.google.gwt.sample.gaerequest.server.UserServiceLocator")
+public interface GaeUserServiceRequest extends RequestContext {
+ public Request<String> createLoginURL(String destinationURL);
+
+ public Request<String> createLogoutURL(String destinationURL);
+
+ public Request<GaeUser> getCurrentUser();
+}
diff --git a/user/src/com/google/gwt/activity/shared/AbstractActivity.java b/samples/expenses/src/main/java/com/google/gwt/sample/gaerequest/shared/MakesGaeRequests.java
similarity index 63%
copy from user/src/com/google/gwt/activity/shared/AbstractActivity.java
copy to samples/expenses/src/main/java/com/google/gwt/sample/gaerequest/shared/MakesGaeRequests.java
index 59e596c..63d0c9e 100644
--- a/user/src/com/google/gwt/activity/shared/AbstractActivity.java
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/gaerequest/shared/MakesGaeRequests.java
@@ -1,33 +1,28 @@
/*
* Copyright 2010 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.gwt.activity.shared;
+package com.google.gwt.sample.gaerequest.shared;
/**
- * Simple Activity implementation that is always willing to stop,
- * and does nothing onStop and onCancel.
+ * Implemented by {@link com.google.gwt.requestfactory.shared.RequestFactory}s
+ * that vend AppEngine requests.
*/
-public abstract class AbstractActivity implements Activity {
+public interface MakesGaeRequests {
- public String mayStop() {
- return null;
- }
-
- public void onCancel() {
- }
-
- public void onStop() {
- }
+ /**
+ * Return a request selector.
+ */
+ GaeUserServiceRequest userServiceRequest();
}
diff --git a/samples/expenses/src/main/resources/log4j.properties b/samples/expenses/src/main/resources/log4j.properties
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/samples/expenses/src/main/resources/log4j.properties
diff --git a/samples/expenses/src/main/webapp/WEB-INF/web.xml b/samples/expenses/src/main/webapp/WEB-INF/web.xml
index 44791a1..9c7305d 100644
--- a/samples/expenses/src/main/webapp/WEB-INF/web.xml
+++ b/samples/expenses/src/main/webapp/WEB-INF/web.xml
@@ -4,57 +4,153 @@
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
+ <security-constraint>
+ <display-name>
+ Redirect to the login page if needed before showing
+ any html pages
+ </display-name>
+ <web-resource-collection>
+ <url-pattern>*.html</url-pattern>
+ </web-resource-collection>
+ <auth-constraint>
+ <role-name>*</role-name>
+ </auth-constraint>
+ </security-constraint>
- <!-- Servlets -->
- <servlet>
- <servlet-name>requestFactoryServlet</servlet-name>
- <servlet-class>com.google.gwt.requestfactory.server.RequestFactoryServlet</servlet-class>
- </servlet>
+ <!--
+
+ Expenses.html and ExpensesMobile.html rely upon RequestFactoryServlet
- <servlet>
- <servlet-name>dataGeneration</servlet-name>
- <servlet-class>com.google.gwt.sample.expenses.server.DataGenerationServiceImpl</servlet-class>
- </servlet>
+ -->
- <!-- Enable remote API on Java (for datastore bulkloader). You also need
- to add appengine-tools-api.jar from the appengine plugin directory to war/WEB-INF/lib -->
- <servlet>
- <servlet-name>remoteapi</servlet-name>
- <servlet-class>com.google.apphosting.utils.remoteapi.RemoteApiServlet</servlet-class>
- </servlet>
+ <servlet>
+ <servlet-name>requestFactoryServlet</servlet-name>
+ <servlet-class>com.google.gwt.requestfactory.server.RequestFactoryServlet</servlet-class>
+ </servlet>
- <servlet-mapping>
- <servlet-name>remoteapi</servlet-name>
- <url-pattern>/remote_api</url-pattern>
- </servlet-mapping>
+ <servlet-mapping>
+ <servlet-name>requestFactoryServlet</servlet-name>
+ <url-pattern>/gwtRequest</url-pattern>
+ </servlet-mapping>
- <servlet-mapping>
- <servlet-name>requestFactoryServlet</servlet-name>
- <url-pattern>/gwtRequest</url-pattern>
- </servlet-mapping>
+ <filter>
+ <filter-name>GaeAuthFilter</filter-name>
+ <filter-class>com.google.gwt.sample.gaerequest.server.GaeAuthFilter</filter-class>
+ <description>
+ This filter demonstrates making GAE authentication
+ services visible to a RequestFactory client.
+ </description>
+ </filter>
+ <filter-mapping>
+ <filter-name>GaeAuthFilter</filter-name>
+ <url-pattern>/gwtRequest/*</url-pattern>
+ </filter-mapping>
- <servlet-mapping>
- <servlet-name>dataGeneration</servlet-name>
- <url-pattern>/loadexpensesdb/dataGeneration</url-pattern>
- </servlet-mapping>
- <!-- AppStats -->
- <!-- <servlet> <servlet-name>appstats</servlet-name> <servlet-class>com.google.appengine.tools.appstats.AppstatsServlet</servlet-class>
- <init-param> <param-name>requireAdminAuthentication</param-name> <param-value>false</param-value>
- </init-param> </servlet> <servlet-mapping> <servlet-name>appstats</servlet-name>
- <url-pattern>/appstats/*</url-pattern> </servlet-mapping> <filter> <filter-name>appstats</filter-name>
- <filter-class>com.google.appengine.tools.appstats.AppstatsFilter</filter-class>
- <init-param> <param-name>logMessage</param-name> <param-value>Appstats available:
- /appstats/details?time={ID}</param-value> </init-param> <init-param> <param-name>basePath</param-name>
- <param-value>/appstats/</param-value> </init-param> </filter> <filter-mapping>
- <filter-name>appstats</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> -->
+ <!--
+
+ LoadExpensesDB.html uses GWT RPC to implement its DataGenerationService
- <!-- <security-constraint> <web-resource-collection> <web-resource-name>remoteapi</web-resource-name>
- <url-pattern>/remote_api</url-pattern> </web-resource-collection> <auth-constraint>
- <role-name>admin</role-name> </auth-constraint> </security-constraint> -->
+ -->
- <!-- Require login. -->
- <!-- <security-constraint> <web-resource-collection> <web-resource-name>Access</web-resource-name>
- <url-pattern>/*</url-pattern> </web-resource-collection> <auth-constraint>
- <role-name>admin</role-name> </auth-constraint> </security-constraint> -->
+ <servlet>
+ <servlet-name>dataGeneration</servlet-name>
+ <servlet-class>com.google.gwt.sample.expenses.server.DataGenerationServiceImpl</servlet-class>
+ <description>
+ GWT RPC service used by LoadExpensesDB.html
+ </description>
+ </servlet>
+ <servlet-mapping>
+ <servlet-name>dataGeneration</servlet-name>
+ <url-pattern>/loadexpensesdb/dataGeneration</url-pattern>
+ </servlet-mapping>
+
+ <!--<security-constraint>
+ <display-name>
+ Require admin access for LoadExpensesDB.html and its servlet
+ </display-name>
+ <web-resource-collection>
+ <url-pattern>/loadexpensesdb/*</url-pattern>
+ <url-pattern>/LoadExpensesDB.html</url-pattern>
+ </web-resource-collection>
+ <auth-constraint>
+ <role-name>admin</role-name>
+ </auth-constraint>
+ </security-constraint>-->
+
+
+ <!--
+
+ AppStats
+
+ Uncomment to use GAE's App Stats
+ http://code.google.com/appengine/docs/java/tools/appstats.html
+
+ Visualize the stats with Speed Tracer
+ http://code.google.com/webtoolkit/speedtracer/server-side-tracing.html
+
+ -->
+
+ <!--<servlet>
+ <servlet-name>appstats</servlet-name>
+ <servlet-class>com.google.appengine.tools.appstats.AppstatsServlet</servlet-class>
+ <init-param>
+ <param-name>requireAdminAuthentication</param-name>
+ <param-value>false</param-value>
+ </init-param>
+ </servlet>
+ <servlet-mapping>
+ <servlet-name>appstats</servlet-name>
+ <url-pattern>/appstats/*</url-pattern>
+ </servlet-mapping>
+ <filter>
+ <filter-name>appstats</filter-name>
+ <filter-class>com.google.appengine.tools.appstats.AppstatsFilter</filter-class>
+ <init-param>
+ <param-name>logMessage</param-name>
+ <param-value>Appstats available:
+ /appstats/details?time={ID}</param-value>
+ </init-param>
+ <init-param>
+ <param-name>basePath</param-name>
+ <param-value>/appstats/</param-value>
+ </init-param>
+ </filter>
+ <filter-mapping>
+ <filter-name>appstats</filter-name>
+ <url-pattern>/*</url-pattern>
+ </filter-mapping>-->
+
+
+ <!--
+
+ GAE Remote API provides a bulk uploader and other goodies
+
+ -->
+
+ <!-- <servlet>
+ <servlet-name>remoteapi</servlet-name>
+ <servlet-class>com.google.apphosting.utils.remoteapi.RemoteApiServlet</servlet-class>
+ <description>
+ Provides access to the GAE datastore bulkloader, requires appengine-tools-api.jar
+ (which is commented out in pom.xml, qv)
+ </description>
+ </servlet>
+ <servlet-mapping>
+ <servlet-name>remoteapi</servlet-name>
+ <url-pattern>/remote_api</url-pattern>
+ </servlet-mapping>
+
+ <security-constraint>
+ <display-name>
+ Require admin access for remoteapi
+ </display-name>
+ <web-resource-collection>
+ <url-pattern>/remote_api</url-pattern>
+ </web-resource-collection>
+ <auth-constraint>
+ <role-name>admin</role-name>
+ </auth-constraint>
+ </security-constraint>-->
+
</web-app>
diff --git a/tools/api-checker/config/gwt21_22userApi.conf b/tools/api-checker/config/gwt21_22userApi.conf
index 3e4f820..8271107 100644
--- a/tools/api-checker/config/gwt21_22userApi.conf
+++ b/tools/api-checker/config/gwt21_22userApi.conf
@@ -41,6 +41,9 @@
:com/google/gwt/resources/rg/**\
:com/google/gwt/requestfactory/client/impl/FindRequest.java\
:com/google/gwt/requestfactory/shared/impl/MessageFactoryHolder.java\
+:com/google/gwt/requestfactory/shared/UserInformationProxy.java\
+:com/google/gwt/requestfactory/shared/UserInformationRequest.java\
+:com/google/gwt/requestfactory/ui/client/LoginWidget.java\
:com/google/gwt/rpc/client/impl/ClientWriterFactory.java\
:com/google/gwt/rpc/client/impl/EscapeUtil.java\
:com/google/gwt/soyc/**\
@@ -108,6 +111,18 @@
# when adding to the white-list, include comments as to why the addition is
# being made.
+# Changes to make Activity api evolvable in 2.1.1
+com.google.gwt.activity.shared.AbstractActivity MISSING
+com.google.gwt.activity.shared.Activity STATIC_REMOVED
+
+# RequestFactory tweaks in 2.1.1
+com.google.gwt.requestfactory.client.DefaultRequestTransport::DefaultRequestTransport(Lcom/google/gwt/event/shared/EventBus;) MISSING
+com.google.gwt.requestfactory.shared.RequestEvent MISSING
+com.google.gwt.requestfactory.shared.RequestEvent.Handler MISSING
+com.google.gwt.requestfactory.shared.RequestEvent.State MISSING
+com.google.gwt.requestfactory.shared.ServerFailure::ServerFailure(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;) MISSING
+com.google.gwt.requestfactory.ui.client.AuthenticationFailureHandler MISSING
+
# AutoBean packages have been moved since 2.1
com.google.gwt.editor.client.AutoBean MISSING
com.google.gwt.editor.client.AutoBeanFactory MISSING
diff --git a/user/src/com/google/gwt/activity/shared/Activity.java b/user/src/com/google/gwt/activity/shared/Activity.java
index f141171..209f350 100644
--- a/user/src/com/google/gwt/activity/shared/Activity.java
+++ b/user/src/com/google/gwt/activity/shared/Activity.java
@@ -1,12 +1,12 @@
/*
* Copyright 2010 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
@@ -19,45 +19,51 @@
import com.google.gwt.user.client.ui.AcceptsOneWidget;
/**
- * Implemented by objects that control a piece of user interface, with a life
- * cycle managed by an {@link ActivityManager}, in response to
- * {@link com.google.gwt.place.shared.PlaceChangeEvent} events as the user
- * navigates through the app.
+ * Object that controls a piece of user interface, with a life cycle managed by
+ * an {@link ActivityManager}.
+ * <p>
+ * Ideally this would be an interface rather than an abstract class, but we
+ * expect its api will need to evolve (slightly) in the near term. When it
+ * settles down, an interface may be introduced. To this end, future versions of
+ * this class should not introduce non-trivial behavior.
+ * <p>
+ * For composition, see {@link IsActivity}.
*/
-public interface Activity {
+public abstract class Activity {
+
/**
* Called when the user is trying to navigate away from this activity.
- *
+ *
* @return A message to display to the user, e.g. to warn of unsaved work, or
* null to say nothing
*/
- String mayStop();
+ public abstract String mayStop();
/**
* Called when {@link #start} has not yet replied to its callback, but the
* user has lost interest.
*/
- void onCancel();
+ public abstract void onCancel();
/**
* Called when the Activity's widget has been removed from view. All event
* handlers it registered will have been removed before this method is called.
*/
- void onStop();
+ public abstract void onStop();
/**
* Called when the Activity should ready its widget for the user. When the
* widget is ready (typically after an RPC response has been received),
* receiver should present it by calling
- * {@link AcceptsOneWidget#setWidget(IsWidget)} on the given panel.
+ * {@link AcceptsOneWidget#setWidget()} on the given panel.
* <p>
* Any handlers attached to the provided event bus will be de-registered when
* the activity is stopped, so activities will rarely need to hold on to the
* {@link com.google.gwt.event.shared.HandlerRegistration HandlerRegistration}
* instances returned by {@link EventBus#addHandler}.
- *
+ *
* @param panel the panel to display this activity's widget when it is ready
* @param eventBus the event bus
*/
- void start(AcceptsOneWidget panel, EventBus eventBus);
+ public abstract void start(AcceptsOneWidget panel, EventBus eventBus);
}
diff --git a/user/src/com/google/gwt/activity/shared/ActivityManager.java b/user/src/com/google/gwt/activity/shared/ActivityManager.java
index 7043665..0d0c7de 100644
--- a/user/src/com/google/gwt/activity/shared/ActivityManager.java
+++ b/user/src/com/google/gwt/activity/shared/ActivityManager.java
@@ -54,7 +54,7 @@
}
}
- private static final Activity NULL_ACTIVITY = new AbstractActivity() {
+ private static final Activity NULL_ACTIVITY = new SimpleActivity() {
public void start(AcceptsOneWidget panel, EventBus eventBus) {
}
};
diff --git a/user/src/com/google/gwt/activity/shared/AbstractActivity.java b/user/src/com/google/gwt/activity/shared/IsActivity.java
similarity index 69%
copy from user/src/com/google/gwt/activity/shared/AbstractActivity.java
copy to user/src/com/google/gwt/activity/shared/IsActivity.java
index 59e596c..bc06054 100644
--- a/user/src/com/google/gwt/activity/shared/AbstractActivity.java
+++ b/user/src/com/google/gwt/activity/shared/IsActivity.java
@@ -15,19 +15,14 @@
*/
package com.google.gwt.activity.shared;
+
/**
- * Simple Activity implementation that is always willing to stop,
- * and does nothing onStop and onCancel.
+ * Implemented by objects that can return an {@link Activity} aspect.
*/
-public abstract class AbstractActivity implements Activity {
-
- public String mayStop() {
- return null;
- }
-
- public void onCancel() {
- }
-
- public void onStop() {
- }
+public interface IsActivity {
+
+ /**
+ * Return the {@link Activity} aspect of this object.
+ */
+ Activity asActivity();
}
diff --git a/user/src/com/google/gwt/activity/shared/AbstractActivity.java b/user/src/com/google/gwt/activity/shared/SimpleActivity.java
similarity index 79%
rename from user/src/com/google/gwt/activity/shared/AbstractActivity.java
rename to user/src/com/google/gwt/activity/shared/SimpleActivity.java
index 59e596c..8ed2ed4 100644
--- a/user/src/com/google/gwt/activity/shared/AbstractActivity.java
+++ b/user/src/com/google/gwt/activity/shared/SimpleActivity.java
@@ -1,12 +1,12 @@
/*
* Copyright 2010 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
@@ -15,19 +15,28 @@
*/
package com.google.gwt.activity.shared;
-/**
- * Simple Activity implementation that is always willing to stop,
- * and does nothing onStop and onCancel.
- */
-public abstract class AbstractActivity implements Activity {
+/**
+ * Simple base implementation of {@link Activity}.
+ */
+public abstract class SimpleActivity extends Activity {
+
+ /**
+ * Return null.
+ */
public String mayStop() {
return null;
}
+ /**
+ * No-op.
+ */
public void onCancel() {
}
+ /**
+ * No-op.
+ */
public void onStop() {
}
}
diff --git a/user/src/com/google/gwt/cell/client/IconCellDecorator.java b/user/src/com/google/gwt/cell/client/IconCellDecorator.java
index 3e0c7da..8f67fb1 100644
--- a/user/src/com/google/gwt/cell/client/IconCellDecorator.java
+++ b/user/src/com/google/gwt/cell/client/IconCellDecorator.java
@@ -38,7 +38,7 @@
public class IconCellDecorator<C> implements Cell<C> {
interface Template extends SafeHtmlTemplates {
- @Template("<div style=\"position:relative;padding-{0}:{1}px;\">{2}<div>{3}</div></div>")
+ @Template("<div style=\"position:relative;padding-{0}:{1}px;zoom:1;\">{2}<div>{3}</div></div>")
SafeHtml outerDiv(String direction, int width, SafeHtml icon,
SafeHtml cellContents);
diff --git a/user/src/com/google/gwt/requestfactory/client/DefaultRequestTransport.java b/user/src/com/google/gwt/requestfactory/client/DefaultRequestTransport.java
index f8e555c..6cc5306 100644
--- a/user/src/com/google/gwt/requestfactory/client/DefaultRequestTransport.java
+++ b/user/src/com/google/gwt/requestfactory/client/DefaultRequestTransport.java
@@ -18,16 +18,14 @@
import static com.google.gwt.user.client.rpc.RpcRequestBuilder.STRONG_NAME_HEADER;
import com.google.gwt.core.client.GWT;
-import com.google.gwt.event.shared.EventBus;
import com.google.gwt.http.client.Request;
import com.google.gwt.http.client.RequestBuilder;
import com.google.gwt.http.client.RequestCallback;
import com.google.gwt.http.client.RequestException;
import com.google.gwt.http.client.Response;
-import com.google.gwt.requestfactory.shared.RequestEvent;
import com.google.gwt.requestfactory.shared.RequestFactory;
import com.google.gwt.requestfactory.shared.RequestTransport;
-import com.google.gwt.requestfactory.shared.RequestEvent.State;
+import com.google.gwt.requestfactory.shared.ServerFailure;
import com.google.gwt.user.client.Window.Location;
import java.util.logging.Level;
@@ -38,6 +36,7 @@
* {@link RequestBuilder}.
*/
public class DefaultRequestTransport implements RequestTransport {
+ private static final String SERVER_ERROR = "Server Error";
/**
* The default URL for a DefaultRequestTransport is
@@ -52,29 +51,13 @@
* happen every time a request is made from the server should be logged to
* this logger.
*/
- private static Logger wireLogger = Logger.getLogger("WireActivityLogger");
- private static final String SERVER_ERROR = "Server Error";
+ private static final Logger wireLogger = Logger.getLogger("WireActivityLogger");
- private final EventBus eventBus;
private String requestUrl = GWT.getHostPageBaseURL() + URL;
/**
- * Construct a DefaultRequestTransport.
- *
- * @param eventBus the same EventBus passed into
- * {@link RequestFactory#initialize(EventBus)
- * RequestFactory.initialize}.
- */
- public DefaultRequestTransport(EventBus eventBus) {
- if (eventBus == null) {
- throw new IllegalArgumentException("eventBus must not be null");
- }
- this.eventBus = eventBus;
- }
-
- /**
* Returns the current URL used by this transport.
- *
+ *
* @return the URL as a String
* @see #setRequestUrl(String)
*/
@@ -92,7 +75,6 @@
try {
wireLogger.finest("Sending fire request");
builder.send();
- postRequestEvent(State.SENT, null);
} catch (RequestException e) {
wireLogger.log(Level.SEVERE, SERVER_ERROR + " (" + e.getMessage() + ")",
e);
@@ -111,7 +93,7 @@
/**
* Override to change the headers sent in the HTTP request.
- *
+ *
* @param builder a {@link RequestBuilder} instance
*/
protected void configureRequestBuilder(RequestBuilder builder) {
@@ -134,7 +116,7 @@
* Creates a RequestCallback that maps the HTTP response onto the
* {@link com.google.gwt.requestfactory.shared.RequestTransport.TransportReceiver
* TransportReceiver} interface.
- *
+ *
* @param receiver a {@link TransportReceiver}
* @return a {@link RequestCallback} instance
*/
@@ -143,40 +125,22 @@
return new RequestCallback() {
public void onError(Request request, Throwable exception) {
- postRequestEvent(State.RECEIVED, null);
wireLogger.log(Level.SEVERE, SERVER_ERROR, exception);
- receiver.onTransportFailure(exception.getMessage());
+ receiver.onTransportFailure(new ServerFailure(exception.getMessage()));
}
public void onResponseReceived(Request request, Response response) {
wireLogger.finest("Response received");
- try {
- if (200 == response.getStatusCode()) {
- String text = response.getText();
- receiver.onTransportSuccess(text);
- } else if (Response.SC_UNAUTHORIZED == response.getStatusCode()) {
- String message = "Need to log in";
- wireLogger.finest(message);
- receiver.onTransportFailure(message);
- } else if (response.getStatusCode() > 0) {
- /*
- * During the redirection for logging in, we get a response with no
- * status code, but it's not an error, so we only log errors with
- * bad status codes here.
- */
- String message = SERVER_ERROR + " " + response.getStatusCode()
- + " " + response.getText();
- wireLogger.severe(message);
- receiver.onTransportFailure(message);
- }
- } finally {
- postRequestEvent(State.RECEIVED, response);
+ if (Response.SC_OK == response.getStatusCode()) {
+ String text = response.getText();
+ receiver.onTransportSuccess(text);
+ } else {
+ String message = SERVER_ERROR + " " + response.getStatusCode() + " "
+ + response.getText();
+ wireLogger.severe(message);
+ receiver.onTransportFailure(new ServerFailure(message));
}
}
};
}
-
- private void postRequestEvent(State received, Response response) {
- eventBus.fireEvent(new RequestEvent(received, response));
- }
}
diff --git a/user/src/com/google/gwt/requestfactory/client/impl/AbstractClientRequestFactory.java b/user/src/com/google/gwt/requestfactory/client/impl/AbstractClientRequestFactory.java
index 2e6a312..c38fba9 100644
--- a/user/src/com/google/gwt/requestfactory/client/impl/AbstractClientRequestFactory.java
+++ b/user/src/com/google/gwt/requestfactory/client/impl/AbstractClientRequestFactory.java
@@ -26,6 +26,6 @@
AbstractRequestFactory {
@Override
public void initialize(EventBus eventBus) {
- initialize(eventBus, new DefaultRequestTransport(eventBus));
+ initialize(eventBus, new DefaultRequestTransport());
}
}
diff --git a/user/src/com/google/gwt/requestfactory/server/DefaultExceptionHandler.java b/user/src/com/google/gwt/requestfactory/server/DefaultExceptionHandler.java
index 068fa77..77b901b 100644
--- a/user/src/com/google/gwt/requestfactory/server/DefaultExceptionHandler.java
+++ b/user/src/com/google/gwt/requestfactory/server/DefaultExceptionHandler.java
@@ -24,6 +24,6 @@
public class DefaultExceptionHandler implements ExceptionHandler {
public ServerFailure createServerFailure(Throwable throwable) {
return new ServerFailure("Server Error: "
- + (throwable == null ? null : throwable.getMessage()), null, null);
+ + (throwable == null ? null : throwable.getMessage()), null, null, true);
}
}
\ No newline at end of file
diff --git a/user/src/com/google/gwt/requestfactory/server/RequestFactoryInterfaceValidator.java b/user/src/com/google/gwt/requestfactory/server/RequestFactoryInterfaceValidator.java
index f034b1c..6b64aea 100644
--- a/user/src/com/google/gwt/requestfactory/server/RequestFactoryInterfaceValidator.java
+++ b/user/src/com/google/gwt/requestfactory/server/RequestFactoryInterfaceValidator.java
@@ -127,6 +127,102 @@
}
/**
+ * Improves error messages by providing context for the user.
+ * <p>
+ * Visible for testing.
+ */
+ static class ErrorContext {
+ private final Logger logger;
+ private final ErrorContext parent;
+ private Type currentType;
+ private Method currentMethod;
+ private RequestFactoryInterfaceValidator validator;
+
+ public ErrorContext(Logger logger) {
+ this.logger = logger;
+ this.parent = null;
+ }
+
+ protected ErrorContext(ErrorContext parent) {
+ this.logger = parent.logger;
+ this.parent = parent;
+ this.validator = parent.validator;
+ }
+
+ public void poison(String msg, Object... args) {
+ poison();
+ logger.logp(Level.SEVERE, currentType(), currentMethod(),
+ String.format(msg, args));
+ validator.poisoned = true;
+ }
+
+ public void poison(String msg, Throwable t) {
+ poison();
+ logger.logp(Level.SEVERE, currentType(), currentMethod(), msg, t);
+ validator.poisoned = true;
+ }
+
+ public ErrorContext setMethod(Method method) {
+ ErrorContext toReturn = fork();
+ toReturn.currentMethod = method;
+ return toReturn;
+ }
+
+ public ErrorContext setType(Type type) {
+ ErrorContext toReturn = fork();
+ toReturn.currentType = type;
+ return toReturn;
+ }
+
+ public void spam(String msg, Object... args) {
+ logger.logp(Level.FINEST, currentType(), currentMethod(),
+ String.format(msg, args));
+ }
+
+ protected ErrorContext fork() {
+ return new ErrorContext(this);
+ }
+
+ void setValidator(RequestFactoryInterfaceValidator validator) {
+ assert this.validator == null : "Cannot set validator twice";
+ this.validator = validator;
+ }
+
+ private String currentMethod() {
+ if (currentMethod != null) {
+ return print(currentMethod);
+ }
+ if (parent != null) {
+ return parent.currentMethod();
+ }
+ return null;
+ }
+
+ private String currentType() {
+ if (currentType != null) {
+ return print(currentType);
+ }
+ if (parent != null) {
+ return parent.currentType();
+ }
+ return null;
+ }
+
+ /**
+ * Populate {@link RequestFactoryInterfaceValidator#badTypes} with the
+ * current context.
+ */
+ private void poison() {
+ if (currentType != null) {
+ validator.badTypes.add(currentType.getClassName());
+ }
+ if (parent != null) {
+ parent.poison();
+ }
+ }
+ }
+
+ /**
* Used internally as a placeholder for types that cannot be mapped to a
* domain object.
*/
@@ -206,12 +302,14 @@
@Override
public void visit(String name, Object value) {
String sourceName;
- if ("value".equals(name) || "locator".equals(name)) {
+ boolean locatorRequired = "locator".equals(name);
+ boolean valueRequired = "value".equals(name);
+ if (valueRequired || locatorRequired) {
sourceName = (String) value;
} else {
return;
}
-
+
/*
* The input is a source name, so we need to convert it to an
* internal name. We'll do this by substituting dollar signs for the
@@ -222,17 +320,25 @@
logger.spam("Did not find " + desc.toString());
int idx = desc.lastIndexOf("/");
if (idx == -1) {
+ if (locatorRequired) {
+ logger.poison("Cannot find locator named %s", value);
+ } else if (valueRequired) {
+ logger.poison("Cannot find domain type named %s", value);
+ }
return;
}
desc.setCharAt(idx, '$');
}
- if ("locator".equals(name)) {
+ if (locatorRequired) {
locatorInternalName = desc.toString();
- } else {
+ logger.spam(locatorInternalName);
+ } else if (valueRequired) {
domainInternalName = desc.toString();
+ logger.spam(domainInternalName);
+ } else {
+ throw new RuntimeException("Should not reach here");
}
- logger.spam(domainInternalName);
}
};
}
@@ -254,89 +360,6 @@
}
/**
- * Improves error messages by providing context for the user.
- */
- private class ErrorContext {
- private final Logger logger;
- private final ErrorContext parent;
- private Type currentType;
- private Method currentMethod;
-
- public ErrorContext(Logger logger) {
- this.logger = logger;
- this.parent = null;
- }
-
- private ErrorContext(ErrorContext parent) {
- this.logger = parent.logger;
- this.parent = parent;
- }
-
- public void poison(String msg, Object... args) {
- poison();
- logger.logp(Level.SEVERE, currentType(), currentMethod(),
- String.format(msg, args));
- poisoned = true;
- }
-
- public void poison(String msg, Throwable t) {
- poison();
- logger.logp(Level.SEVERE, currentType(), currentMethod(), msg, t);
- poisoned = true;
- }
-
- public ErrorContext setMethod(Method method) {
- ErrorContext toReturn = new ErrorContext(this);
- toReturn.currentMethod = method;
- return toReturn;
- }
-
- public ErrorContext setType(Type type) {
- ErrorContext toReturn = new ErrorContext(this);
- toReturn.currentType = type;
- return toReturn;
- }
-
- public void spam(String msg, Object... args) {
- logger.logp(Level.FINEST, currentType(), currentMethod(),
- String.format(msg, args));
- }
-
- private String currentMethod() {
- if (currentMethod != null) {
- return print(currentMethod);
- }
- if (parent != null) {
- return parent.currentMethod();
- }
- return null;
- }
-
- private String currentType() {
- if (currentType != null) {
- return print(currentType);
- }
- if (parent != null) {
- return parent.currentType();
- }
- return null;
- }
-
- /**
- * Populate {@link RequestFactoryInterfaceValidator#badTypes} with the
- * current context.
- */
- private void poison() {
- if (currentType != null) {
- badTypes.add(currentType.getClassName());
- }
- if (parent != null) {
- parent.poison();
- }
- }
- }
-
- /**
* Collects information about domain objects. This visitor is intended to be
* iteratively applied to collect all methods in a type hierarchy.
*/
@@ -623,19 +646,32 @@
* Contains vaue types (e.g. Integer).
*/
private final Set<Type> valueTypes = new HashSet<Type>();
-
+
/**
* Maps a domain object to the type returned from its getId method.
*/
private final Map<Type, Type> unresolvedKeyTypes = new HashMap<Type, Type>();
- public RequestFactoryInterfaceValidator(Logger logger, Loader loader) {
- this.parentLogger = new ErrorContext(logger);
- this.loader = loader;
+ {
for (Class<?> clazz : VALUE_TYPES) {
valueTypes.add(Type.getType(clazz));
}
}
+
+ public RequestFactoryInterfaceValidator(Logger logger, Loader loader) {
+ this.parentLogger = new ErrorContext(logger);
+ parentLogger.setValidator(this);
+ this.loader = loader;
+ }
+
+ /**
+ * Visible for testing.
+ */
+ RequestFactoryInterfaceValidator(ErrorContext errorContext, Loader loader) {
+ this.parentLogger = errorContext;
+ this.loader = loader;
+ errorContext.setValidator(this);
+ }
/**
* Reset the poisoned status of the validator so that it may be reused without
@@ -1332,7 +1368,8 @@
return true;
}
- private boolean isCollectionType(ErrorContext logger, Type type) {
+ private boolean isCollectionType(@SuppressWarnings("unused") ErrorContext logger, Type type) {
+ // keeping the logger arg just for internal consistency for our small minds
return "java/util/List".equals(type.getInternalName())
|| "java/util/Set".equals(type.getInternalName());
}
diff --git a/user/src/com/google/gwt/requestfactory/server/RequestFactoryServlet.java b/user/src/com/google/gwt/requestfactory/server/RequestFactoryServlet.java
index 4ad67c1..cfac4b4 100644
--- a/user/src/com/google/gwt/requestfactory/server/RequestFactoryServlet.java
+++ b/user/src/com/google/gwt/requestfactory/server/RequestFactoryServlet.java
@@ -29,24 +29,7 @@
import javax.servlet.http.HttpServletResponse;
/**
- * Handles GWT RequestFactory JSON requests. Does user authentication on every
- * request, returning SC_UNAUTHORIZED if authentication fails, as well as a
- * header named "login" which contains the URL the user should be sent in to
- * login. Note that the servlet expects a "pageurl" header in the request,
- * indicating the page to redirect to after authentication. If authentication
- * succeeds, a header named "userId" is returned, which will be unique to the
- * user (so the app can react if the signed in user has changed).
- *
- * Configured via servlet init params.
- * <p>
- * e.g. - in order to use GAE authentication:
- *
- * <pre> <init-param>
- <param-name>userInfoClass</param-name>
- <param-value>com.google.gwt.sample.expenses.server.domain.GaeUserInformation</param-value>
- </init-param>
-
- * </pre>
+ * Handles GWT RequestFactory JSON requests.
*/
@SuppressWarnings("serial")
public class RequestFactoryServlet extends HttpServlet {
@@ -132,24 +115,16 @@
}
try {
- // Check that user is logged in before proceeding
- UserInformation userInfo = UserInformation.getCurrentUserInformation(request.getHeader("pageurl"));
- if (!userInfo.isUserLoggedIn()) {
- response.setHeader("login", userInfo.getLoginUrl());
- response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
- } else {
- String payload = processor.process(jsonRequestString);
- if (DUMP_PAYLOAD) {
- System.out.println("<<< " + payload);
- }
- response.setHeader("userId", String.format("%s", userInfo.getId()));
- response.setStatus(HttpServletResponse.SC_OK);
- response.setContentType(RequestFactory.JSON_CONTENT_TYPE_UTF8);
- // The Writer must be obtained after setting the content type
- PrintWriter writer = response.getWriter();
- writer.print(payload);
- writer.flush();
+ String payload = processor.process(jsonRequestString);
+ if (DUMP_PAYLOAD) {
+ System.out.println("<<< " + payload);
}
+ response.setStatus(HttpServletResponse.SC_OK);
+ response.setContentType(RequestFactory.JSON_CONTENT_TYPE_UTF8);
+ // The Writer must be obtained after setting the content type
+ PrintWriter writer = response.getWriter();
+ writer.print(payload);
+ writer.flush();
} catch (RuntimeException e) {
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
log.log(Level.SEVERE, "Unexpected error", e);
@@ -161,15 +136,6 @@
}
private void ensureConfig() {
- // Instantiate a class for authentication, using either the default
- // UserInfo class, or a subclass if the web.xml specifies one. This allows
- // clients to use a Google App Engine based authentication class without
- // adding GAE dependencies to GWT.
- String userInfoClass = getServletConfig().getInitParameter("userInfoClass");
- if (userInfoClass != null) {
- UserInformation.setUserInformationImplClass(userInfoClass);
- }
-
String symbolMapsDirectory = getServletConfig().getInitParameter(
"symbolMapsDirectory");
if (symbolMapsDirectory != null) {
diff --git a/user/src/com/google/gwt/requestfactory/server/SimpleRequestProcessor.java b/user/src/com/google/gwt/requestfactory/server/SimpleRequestProcessor.java
index 07fe1f5..2835056 100644
--- a/user/src/com/google/gwt/requestfactory/server/SimpleRequestProcessor.java
+++ b/user/src/com/google/gwt/requestfactory/server/SimpleRequestProcessor.java
@@ -186,7 +186,7 @@
processOperationMessages(state, message);
List<Object> decoded = decodeInvocationArguments(state,
message.getInvocations().get(0).getParameters(),
- new Class<?>[] {proxyType}, new Type[] {domainClass});
+ new Class<?>[]{proxyType}, new Type[]{domainClass});
@SuppressWarnings("unchecked")
List<T> toReturn = (List<T>) decoded;
@@ -240,6 +240,7 @@
msg.setExceptionType(failure.getExceptionType());
msg.setMessage(failure.getMessage());
msg.setStackTrace(failure.getStackTraceString());
+ msg.setFatal(failure.isFatal());
return bean;
}
@@ -272,10 +273,16 @@
Splittable version = null;
if (writeOperation == WriteOperation.PERSIST
|| writeOperation == WriteOperation.UPDATE) {
+ /*
+ * If we're sending an operation, the domain object must be persistent.
+ * This means that it must also have a non-null version.
+ */
Object domainVersion = service.getVersion(domainObject);
- if (domainVersion != null) {
- version = returnState.flatten(domainVersion);
+ if (domainVersion == null) {
+ throw new UnexpectedException("The persisted entity with id "
+ + service.getId(domainObject) + " has a null version", null);
}
+ version = returnState.flatten(domainVersion);
}
boolean inResponse = bean.getTag(Constants.IN_RESPONSE) != null;
@@ -345,7 +352,7 @@
* is not static, the instance object will be in the 0th position.
*/
private List<Object> decodeInvocationArguments(RequestState source,
- InvocationMessage invocation, Method contextMethod, Method domainMethod) {
+ InvocationMessage invocation, Method contextMethod) {
boolean isStatic = Request.class.isAssignableFrom(contextMethod.getReturnType());
int baseLength = contextMethod.getParameterTypes().length;
int length = baseLength + (isStatic ? 0 : 1);
@@ -374,7 +381,8 @@
private List<Object> decodeInvocationArguments(RequestState source,
List<Splittable> parameters, Class<?>[] contextArgs, Type[] genericArgs) {
if (parameters == null) {
- return Collections.emptyList();
+ // Can't return Collections.emptyList() because this must be mutable
+ return new ArrayList<Object>();
}
assert parameters.size() == contextArgs.length;
@@ -420,7 +428,7 @@
// Compute the arguments
List<Object> args = decodeInvocationArguments(state, invocation,
- contextMethod, domainMethod);
+ contextMethod);
// Possibly use a ServiceLocator
if (service.requiresServiceLocator(contextMethod, domainMethod)) {
Object serviceInstance = service.createServiceInstance(contextMethod,
diff --git a/user/src/com/google/gwt/requestfactory/server/UserInformation.java b/user/src/com/google/gwt/requestfactory/server/UserInformation.java
deleted file mode 100644
index 4d3ac97..0000000
--- a/user/src/com/google/gwt/requestfactory/server/UserInformation.java
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
- * Copyright 2010 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.gwt.requestfactory.server;
-
-/**
- * A base class for providing authentication related information about the user.
- * Services that want real authentication should subclass this class with a
- * matching constructor, and set their class name via
- * {@link #setUserInformationImplClass(String)}.
- */
-public abstract class UserInformation {
-
- /**
- * Reset by {@link #getCurrentUserInformation}, which is called by
- * {@link RequestFactoryServlet#doPost} at the start of each request, so
- * shouldn't leak between re-used threads.
- */
- private static final ThreadLocal<UserInformation> currentUser = new ThreadLocal<UserInformation>();
-
- private static String userInformationImplClass = "";
-
- /**
- * Instance finder method required by RequestFactory. Returns the last
- * UserInformation established for this thread by a call to
- * getCurrentUserInformation, or null if non has been set.
- *
- * @param id ignored, required by RequestFactoryServlet
- */
- public static UserInformation findUserInformation(Long id) {
- return currentUser.get();
- }
-
- /**
- * Called by {@link RequestFactoryServlet#doPost} at the start of each request
- * received. Establishes the current user information for this request, and
- * notes a redirect url to be provided back to the client if the user's bona
- * fides cannot be established. All succeeding calls to
- * {@link #findUserInformation(Long)} made from the same thread will return
- * the same UserInfo instance.
- * <p>
- * If {@link #setUserInformationImplClass(String)} has been called with a
- * class name, that class is used to gather the information by calling a
- * (String) constructor. If the impl class name is "", or if the class cannont
- * be instantiated, dummy user info is returned.
- *
- * @param redirectUrl the redirect URL as a String
- * @return a {@link UserInformation} instance
- */
- public static UserInformation getCurrentUserInformation(String redirectUrl) {
- currentUser.remove();
- if (!"".equals(userInformationImplClass)) {
- try {
- currentUser.set((UserInformation) Class.forName(
- userInformationImplClass).getConstructor(String.class).newInstance(
- redirectUrl));
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- if (currentUser.get() == null) {
- currentUser.set(new UserInformationSimpleImpl(redirectUrl));
- }
- return currentUser.get();
- }
-
- /**
- * Sets the implementation class to be used to gather user information in
- * {@link #getCurrentUserInformation(String)}.
- *
- * @param clazz a class name
- */
- public static void setUserInformationImplClass(String clazz) {
- userInformationImplClass = clazz;
- }
-
- /**
- * The redirect URL as a String.
- */
- protected String redirectUrl = "";
- private Integer version = 0;
-
- /**
- * Constructs a new {@link UserInformation} instance.
- *
- * @param redirectUrl the redirect URL as a String
- */
- public UserInformation(String redirectUrl) {
- if (redirectUrl != null) {
- this.redirectUrl = redirectUrl;
- }
- }
-
- /**
- * Returns the user's email address.
- *
- * @return the user's email address as a String
- */
- public abstract String getEmail();
-
- /**
- * Returns the user's id.
- *
- * @return the user's id as a Long
- * @see #setId(Long)
- */
- public abstract Long getId();
-
- /**
- * Returns the user's login URL.
- *
- * @return the user's login URL as a String
- */
- public abstract String getLoginUrl();
-
- /**
- * Returns the user's logout URL.
- *
- * @return the user's logout URL as a String
- */
- public abstract String getLogoutUrl();
-
- /**
- * Returns the user's name.
- *
- * @return the user's name as a String
- */
- public abstract String getName();
-
- /**
- * Returns the version of this instance.
- *
- * @return an Integer version number
- * @see #setVersion(Integer)
- */
- public Integer getVersion() {
- return this.version;
- }
-
- /**
- * Returns whether the user is logged in.
- *
- * @return {@code true} if the user is logged in
- */
- public abstract boolean isUserLoggedIn();
-
- /**
- * Sets the id for this user.
- *
- * @param id a String id
- * @see #getId()
- */
- public abstract void setId(Long id);
-
- /**
- * Sets the version of this instance.
- *
- * @param version an Integer version number
- * @see #getVersion()
- */
- public void setVersion(Integer version) {
- this.version = version;
- }
-}
diff --git a/user/src/com/google/gwt/requestfactory/server/UserInformationSimpleImpl.java b/user/src/com/google/gwt/requestfactory/server/UserInformationSimpleImpl.java
deleted file mode 100644
index c9a66eb..0000000
--- a/user/src/com/google/gwt/requestfactory/server/UserInformationSimpleImpl.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright 2010 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.gwt.requestfactory.server;
-
-/**
- * This implementation treats the user as constantly signed in, and without any
- * information.
- */
-public class UserInformationSimpleImpl extends UserInformation {
-
- private Long id = 0L;
-
- /**
- * Constructs an UserInformationSimpleImpl object.
- *
- * @param redirectUrl the redirect URL as a String
- */
- public UserInformationSimpleImpl(String redirectUrl) {
- super(redirectUrl);
- }
-
- @Override
- public String getEmail() {
- return "Dummy Email";
- }
-
- @Override
- public Long getId() {
- return this.id;
- }
-
- @Override
- public String getLoginUrl() {
- return "";
- }
-
- @Override
- public String getLogoutUrl() {
- return "";
- }
-
- @Override
- public String getName() {
- return "Dummy User";
- }
-
- @Override
- public boolean isUserLoggedIn() {
- return true;
- }
-
- @Override
- public void setId(Long id) {
- this.id = id;
- }
-}
diff --git a/user/src/com/google/gwt/requestfactory/server/testing/InProcessRequestTransport.java b/user/src/com/google/gwt/requestfactory/server/testing/InProcessRequestTransport.java
index a10adb2..cf713be 100644
--- a/user/src/com/google/gwt/requestfactory/server/testing/InProcessRequestTransport.java
+++ b/user/src/com/google/gwt/requestfactory/server/testing/InProcessRequestTransport.java
@@ -17,6 +17,7 @@
import com.google.gwt.requestfactory.server.SimpleRequestProcessor;
import com.google.gwt.requestfactory.shared.RequestTransport;
+import com.google.gwt.requestfactory.shared.ServerFailure;
/**
* A RequesTransports that calls a {@link SimpleRequestProcessor}. This test can
@@ -57,7 +58,7 @@
receiver.onTransportSuccess(result);
} catch (RuntimeException e) {
e.printStackTrace();
- receiver.onTransportFailure(e.getMessage());
+ receiver.onTransportFailure(new ServerFailure(e.getMessage()));
}
}
}
diff --git a/user/src/com/google/gwt/requestfactory/shared/Receiver.java b/user/src/com/google/gwt/requestfactory/shared/Receiver.java
index 33fabe2..a1a4fc6 100644
--- a/user/src/com/google/gwt/requestfactory/shared/Receiver.java
+++ b/user/src/com/google/gwt/requestfactory/shared/Receiver.java
@@ -18,24 +18,28 @@
import java.util.Set;
/**
- * Implemented by objects that display values.
+ * Callback object for {@link Request#fire(Receiver)} and
+ * {@link RequestContext#fire(Receiver)}.
*
* @param <V> value type
*/
public abstract class Receiver<V> {
/**
- * Receives general failure notifications. The default implementation throws a
- * RuntimeException with the provided error message.
+ * Receives general failure notifications. The default implementation looks at
+ * {@link ServerFailure#isFatal()}, and throws a runtime exception with the
+ * failure object's error message if it is true.
*
* @param error a {@link ServerFailure} instance
*/
public void onFailure(ServerFailure error) {
- throw new RuntimeException(error.getMessage());
+ if (error.isFatal()) {
+ throw new RuntimeException(error.getMessage());
+ }
}
/**
* Called when a Request has been successfully executed on the server.
- *
+ *
* @param response a response of type V
*/
public abstract void onSuccess(V response);
@@ -44,14 +48,13 @@
* Called if an object sent to the server could not be validated. The default
* implementation calls {@link #onFailure(ServerFailure)} if <code>errors
* </code> is not empty.
- *
+ *
* @param errors a Set of {@link Violation} instances
*/
public void onViolation(Set<Violation> errors) {
if (!errors.isEmpty()) {
onFailure(new ServerFailure(
- "The call failed on the server due to a ConstraintViolation",
- "", ""));
+ "The call failed on the server due to a ConstraintViolation"));
}
}
}
diff --git a/user/src/com/google/gwt/requestfactory/shared/RequestTransport.java b/user/src/com/google/gwt/requestfactory/shared/RequestTransport.java
index 3e30f4f..b3947cb 100644
--- a/user/src/com/google/gwt/requestfactory/shared/RequestTransport.java
+++ b/user/src/com/google/gwt/requestfactory/shared/RequestTransport.java
@@ -28,22 +28,22 @@
public interface TransportReceiver {
/**
* Called when the transmission succeeds.
- *
+ *
* @param payload the String payload
*/
void onTransportSuccess(String payload);
/**
- * Called when the transmission fails.
- *
+ * Called to report a transmission failure as a ServerFailure.
+ *
* @param message the String error message
*/
- void onTransportFailure(String message);
+ void onTransportFailure(ServerFailure failure);
}
/**
* Called by the RequestFactory implementation.
- *
+ *
* @param payload the String payload
* @param receiver a {@link TransportReceiver} instance
*/
diff --git a/user/src/com/google/gwt/requestfactory/shared/ServerFailure.java b/user/src/com/google/gwt/requestfactory/shared/ServerFailure.java
index d57bf53..91f2fa8 100644
--- a/user/src/com/google/gwt/requestfactory/shared/ServerFailure.java
+++ b/user/src/com/google/gwt/requestfactory/shared/ServerFailure.java
@@ -17,36 +17,49 @@
/**
* Describes a request failure on the server.
+ * <p>
+ * This error reporting mechanism is adequate at best. When RequestFactory is
+ * extended to handle polymorphic types, this class will likely be replaced with
+ * something more expressive.
*/
public class ServerFailure {
private final String message;
private final String stackTraceString;
private final String exceptionType;
+ private final boolean fatal;
/**
- * Constructs a ServerFailure with a null message.
+ * Constructs a ServerFailure with null properties.
*/
public ServerFailure() {
- this(null, null, null);
+ this(null);
+ }
+
+ /**
+ * Constructs a fatal ServerFailure with null type and null stack trace.
+ */
+ public ServerFailure(String message) {
+ this(message, null, null, true);
}
/**
* Constructs a ServerFailure object.
- *
+ *
* @param message a String containing the failure message
* @param exceptionType a String containing the exception type
* @param stackTraceString a String containing the stack trace
*/
public ServerFailure(String message, String exceptionType,
- String stackTraceString) {
+ String stackTraceString, boolean fatal) {
this.message = message;
this.exceptionType = exceptionType;
this.stackTraceString = stackTraceString;
+ this.fatal = fatal;
}
/**
- * Returns the exception type.
- *
+ * Return the exception type.
+ *
* @return the exception type as a String
*/
public String getExceptionType() {
@@ -54,8 +67,8 @@
}
/**
- * Returns the failure message.
- *
+ * Return the failure message.
+ *
* @return the message as a String
*/
public String getMessage() {
@@ -63,11 +76,21 @@
}
/**
- * Returns the failure stack trace.
- *
+ * Return the failure stack trace.
+ *
* @return the stack trace as a String
*/
public String getStackTraceString() {
return stackTraceString;
}
+
+ /**
+ * Return true if this is a fatal error. The default implementation of
+ * {@link Receiver#onFailure} throws a runtime exception for fatal failures.
+ *
+ * @return whether this is a fatal failure
+ */
+ public boolean isFatal() {
+ return fatal;
+ }
}
diff --git a/user/src/com/google/gwt/requestfactory/shared/UserInformationProxy.java b/user/src/com/google/gwt/requestfactory/shared/UserInformationProxy.java
deleted file mode 100644
index 0dd84a0..0000000
--- a/user/src/com/google/gwt/requestfactory/shared/UserInformationProxy.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright 2010 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.gwt.requestfactory.shared;
-
-import com.google.gwt.requestfactory.server.UserInformation;
-
-/**
- * "API Generated" DTO interface based on {@link UserInformation}.
- */
-@ProxyFor(UserInformation.class)
-public interface UserInformationProxy extends EntityProxy {
- /**
- * Returns the user's email address.
- *
- * @return the user's email address as a String
- */
- String getEmail();
-
- /**
- * Returns the user's login url.
- *
- * @return the user's login url as a String
- */
- String getLoginUrl();
-
- /**
- * Returns the user's logout url.
- *
- * @return the user's logout url as a String
- */
- String getLogoutUrl();
-
- /**
- * Returns the user's name.
- *
- * @return the user's name as a String
- */
- String getName();
-}
diff --git a/user/src/com/google/gwt/requestfactory/shared/UserInformationRequest.java b/user/src/com/google/gwt/requestfactory/shared/UserInformationRequest.java
deleted file mode 100644
index 463cb36..0000000
--- a/user/src/com/google/gwt/requestfactory/shared/UserInformationRequest.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright 2010 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.gwt.requestfactory.shared;
-
-import com.google.gwt.requestfactory.server.UserInformation;
-
-/**
- * "API Generated" request selector interface implemented by objects that give
- * client access to the methods of {@link UserInformation}.
- */
-@Service(UserInformation.class)
-public interface UserInformationRequest extends RequestContext {
-
- /**
- * Returns the current user information given a redirect URL.
- *
- * @param redirectUrl the redirect UR as a String
- * @return an instance of {@link Request}<{@link UserInformationProxy}>
- */
- Request<UserInformationProxy> getCurrentUserInformation(String redirectUrl);
-}
diff --git a/user/src/com/google/gwt/requestfactory/shared/impl/AbstractRequestContext.java b/user/src/com/google/gwt/requestfactory/shared/impl/AbstractRequestContext.java
index 839f4ee..3027693 100644
--- a/user/src/com/google/gwt/requestfactory/shared/impl/AbstractRequestContext.java
+++ b/user/src/com/google/gwt/requestfactory/shared/impl/AbstractRequestContext.java
@@ -114,7 +114,10 @@
}
private static final String PARENT_OBJECT = "parentObject";
-
+ private static final WriteOperation[] PERSIST_AND_UPDATE = {
+ WriteOperation.PERSIST, WriteOperation.UPDATE};
+ private static final WriteOperation[] DELETE_ONLY = {WriteOperation.DELETE};
+ private static final WriteOperation[] UPDATE_ONLY = {WriteOperation.UPDATE};
private final List<AbstractRequest<?>> invocations = new ArrayList<AbstractRequest<?>>();
private boolean locked;
private final AbstractRequestFactory requestFactory;
@@ -148,8 +151,8 @@
public <T extends BaseProxy> T create(Class<T> clazz) {
checkLocked();
- AutoBean<T> created = requestFactory.createProxy(clazz,
- requestFactory.allocateId(clazz));
+ SimpleProxyId<T> id = requestFactory.allocateId(clazz);
+ AutoBean<T> created = requestFactory.createProxy(clazz, id);
return takeOwnership(created);
}
@@ -601,16 +604,8 @@
String payload = makePayload();
requestFactory.getRequestTransport().send(payload, new TransportReceiver() {
- public void onTransportFailure(String message) {
- ServerFailure failure = new ServerFailure(message, null, null);
- reuse();
- for (AbstractRequest<?> request : new ArrayList<AbstractRequest<?>>(
- invocations)) {
- request.onFail(failure);
- }
- if (receiver != null) {
- receiver.onFailure(failure);
- }
+ public void onTransportFailure(ServerFailure failure) {
+ fail(receiver, failure);
}
public void onTransportSuccess(String payload) {
@@ -619,16 +614,10 @@
if (response.getGeneralFailure() != null) {
ServerFailureMessage failure = response.getGeneralFailure();
ServerFailure fail = new ServerFailure(failure.getMessage(),
- failure.getExceptionType(), failure.getStackTrace());
+ failure.getExceptionType(), failure.getStackTrace(),
+ failure.isFatal());
- reuse();
- for (AbstractRequest<?> invocation : new ArrayList<AbstractRequest<?>>(
- invocations)) {
- invocation.onFail(fail);
- }
- if (receiver != null) {
- receiver.onFailure(fail);
- }
+ fail(receiver, fail);
return;
}
@@ -663,7 +652,8 @@
response.getInvocationResults().get(i)).as();
invocations.get(i).onFail(
new ServerFailure(failure.getMessage(),
- failure.getExceptionType(), failure.getStackTrace()));
+ failure.getExceptionType(), failure.getStackTrace(),
+ failure.isFatal()));
}
}
@@ -675,6 +665,17 @@
invocations.clear();
returnedProxies.clear();
}
+
+ private void fail(Receiver<Void> receiver, ServerFailure failure) {
+ reuse();
+ for (AbstractRequest<?> request : new ArrayList<AbstractRequest<?>>(
+ invocations)) {
+ request.onFail(failure);
+ }
+ if (receiver != null) {
+ receiver.onFailure(failure);
+ }
+ }
});
}
@@ -780,23 +781,36 @@
* Process an array of OperationMessages.
*/
private void processReturnOperations(ResponseMessage response) {
- List<OperationMessage> records = response.getOperations();
+ List<OperationMessage> ops = response.getOperations();
// If there are no observable effects, this will be null
- if (records == null) {
+ if (ops == null) {
return;
}
- for (OperationMessage op : records) {
+ for (OperationMessage op : ops) {
SimpleProxyId<?> id = getId(op);
- if (id.isEphemeral()) {
- processReturnOperation(id, op);
- } else if (id.wasEphemeral()) {
- processReturnOperation(id, op, WriteOperation.PERSIST,
- WriteOperation.UPDATE);
- } else {
- processReturnOperation(id, op, WriteOperation.UPDATE);
+ WriteOperation[] toPropagate = null;
+
+ // May be null if the server is returning an unpersisted object
+ WriteOperation effect = op.getOperation();
+ if (effect != null) {
+ switch (effect) {
+ case DELETE:
+ toPropagate = DELETE_ONLY;
+ break;
+ case PERSIST:
+ toPropagate = PERSIST_AND_UPDATE;
+ break;
+ case UPDATE:
+ toPropagate = UPDATE_ONLY;
+ break;
+ default:
+ // Should never reach here
+ throw new RuntimeException(effect.toString());
+ }
}
+ processReturnOperation(id, op, toPropagate);
}
}
diff --git a/user/src/com/google/gwt/requestfactory/shared/impl/AbstractRequestFactory.java b/user/src/com/google/gwt/requestfactory/shared/impl/AbstractRequestFactory.java
index b5ffeda..9616776 100644
--- a/user/src/com/google/gwt/requestfactory/shared/impl/AbstractRequestFactory.java
+++ b/user/src/com/google/gwt/requestfactory/shared/impl/AbstractRequestFactory.java
@@ -82,7 +82,7 @@
protected RequestData makeRequestData() {
return new RequestData(
"com.google.gwt.requestfactory.shared.impl.FindRequest::find",
- new Object[] {proxyId}, propertyRefs, proxyId.getProxyClass(), null);
+ new Object[]{proxyId}, propertyRefs, proxyId.getProxyClass(), null);
}
};
}
@@ -142,6 +142,8 @@
*/
protected boolean hasVersionChanged(SimpleProxyId<?> id,
String observedVersion) {
+ assert id != null : "id";
+ assert observedVersion != null : "observedVersion";
String key = getHistoryToken(id);
String existingVersion = version.get(key);
// Return true if we haven't seen this before or the versions differ
diff --git a/user/src/com/google/gwt/requestfactory/shared/messages/ServerFailureMessage.java b/user/src/com/google/gwt/requestfactory/shared/messages/ServerFailureMessage.java
index f196adf..27a5007 100644
--- a/user/src/com/google/gwt/requestfactory/shared/messages/ServerFailureMessage.java
+++ b/user/src/com/google/gwt/requestfactory/shared/messages/ServerFailureMessage.java
@@ -24,6 +24,7 @@
String EXCEPTION_TYPE = "X";
String MESSAGE = "M";
String STACK_TRACE = "S";
+ String FATAL = "F";
@PropertyName(EXCEPTION_TYPE)
String getExceptionType();
@@ -33,13 +34,19 @@
@PropertyName(STACK_TRACE)
String getStackTrace();
+
+ @PropertyName(FATAL)
+ boolean isFatal();
@PropertyName(EXCEPTION_TYPE)
void setExceptionType(String exceptionType);
+ @PropertyName(FATAL)
+ void setFatal(boolean significant);
+
@PropertyName(MESSAGE)
void setMessage(String message);
-
+
@PropertyName(STACK_TRACE)
void setStackTrace(String stackTrace);
}
diff --git a/user/src/com/google/gwt/requestfactory/ui/client/AuthenticationFailureHandler.java b/user/src/com/google/gwt/requestfactory/ui/client/AuthenticationFailureHandler.java
deleted file mode 100644
index beb0f41..0000000
--- a/user/src/com/google/gwt/requestfactory/ui/client/AuthenticationFailureHandler.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright 2010 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.gwt.requestfactory.ui.client;
-
-import com.google.gwt.http.client.Response;
-import com.google.gwt.requestfactory.shared.RequestEvent;
-import com.google.gwt.requestfactory.shared.RequestEvent.State;
-import com.google.gwt.user.client.Window.Location;
-
-/**
- * A request event handler which listens to every request and reacts if there
- * is an authentication problem. Note that the server side code is responsible
- * for making sure that no sensitive information is returned in case of
- * authentication issues. This handler is just responsible for making such
- * failures user friendly.
- */
-public class AuthenticationFailureHandler implements RequestEvent.Handler {
- private String lastSeenUser = null;
-
- public void onRequestEvent(RequestEvent requestEvent) {
- if (requestEvent.getState() == State.RECEIVED) {
- Response response = requestEvent.getResponse();
- if (response == null) {
- // We should only get to this state if the RPC failed, in which
- // case we went through the RequestCallback.onError() code path
- // already and we don't need to do any additional error handling
- // here, but we don't want to throw further exceptions.
- return;
- }
- if (Response.SC_UNAUTHORIZED == response.getStatusCode()) {
- String loginUrl = response.getHeader("login");
- Location.replace(loginUrl);
- }
- String newUser = response.getHeader("userId");
- if (lastSeenUser == null) {
- lastSeenUser = newUser;
- } else if (!lastSeenUser.equals(newUser)) {
- // A new user has logged in, just reload the app and start over
- Location.reload();
- }
- }
- }
-
-}
diff --git a/user/src/com/google/gwt/requestfactory/ui/client/LoginWidget.java b/user/src/com/google/gwt/requestfactory/ui/client/LoginWidget.java
deleted file mode 100644
index 0362693..0000000
--- a/user/src/com/google/gwt/requestfactory/ui/client/LoginWidget.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright 2010 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.gwt.requestfactory.ui.client;
-
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.dom.client.SpanElement;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.requestfactory.shared.UserInformationProxy;
-import com.google.gwt.uibinder.client.UiBinder;
-import com.google.gwt.uibinder.client.UiField;
-import com.google.gwt.uibinder.client.UiHandler;
-import com.google.gwt.user.client.Window.Location;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.Widget;
-
-/**
- * A simple widget which displays info about the user and a logout link.
- */
-public class LoginWidget extends Composite {
- interface Binder extends UiBinder<Widget, LoginWidget> { }
- private static final Binder BINDER = GWT.create(Binder.class);
-
- @UiField SpanElement name;
- String logoutUrl = "";
-
- public LoginWidget() {
- initWidget(BINDER.createAndBindUi(this));
- }
-
- /**
- * Sets the user information using a {@link UserInformationProxy}.
- *
- * @param info a {@link UserInformationProxy} instance
- */
- public void setUserInformation(UserInformationProxy info) {
- name.setInnerText(info.getName());
- logoutUrl = info.getLogoutUrl();
- }
-
- @UiHandler("logoutLink")
- void handleClick(@SuppressWarnings("unused") ClickEvent e) {
- if (logoutUrl != "") {
- Location.replace(logoutUrl);
- }
- }
-}
diff --git a/user/src/com/google/gwt/user/cellview/CellView.gwt.xml b/user/src/com/google/gwt/user/cellview/CellView.gwt.xml
index d848672..00dacb0 100644
--- a/user/src/com/google/gwt/user/cellview/CellView.gwt.xml
+++ b/user/src/com/google/gwt/user/cellview/CellView.gwt.xml
@@ -54,12 +54,4 @@
<when-property-is name="user.agent" value="ie8"/>
</any>
</replace-with>
-
- <!-- IE6-specific CellTree implementation. -->
- <replace-with class="com.google.gwt.user.cellview.client.CellTree.ImplIE6">
- <when-type-is class="com.google.gwt.user.cellview.client.CellTree.Impl"/>
- <any>
- <when-property-is name="user.agent" value="ie6"/>
- </any>
- </replace-with>
</module>
diff --git a/user/src/com/google/gwt/user/cellview/client/CellBrowser.css b/user/src/com/google/gwt/user/cellview/client/CellBrowser.css
index 8739e95..727930b 100644
--- a/user/src/com/google/gwt/user/cellview/client/CellBrowser.css
+++ b/user/src/com/google/gwt/user/cellview/client/CellBrowser.css
@@ -27,10 +27,12 @@
.cellBrowserEvenItem {
padding: 8px;
+ zoom: 1;
}
.cellBrowserOddItem {
padding: 8px;
+ zoom: 1;
}
.cellBrowserKeyboardSelectedItem {
diff --git a/user/src/com/google/gwt/user/cellview/client/CellList.css b/user/src/com/google/gwt/user/cellview/client/CellList.css
index cd7546e..48364d5 100644
--- a/user/src/com/google/gwt/user/cellview/client/CellList.css
+++ b/user/src/com/google/gwt/user/cellview/client/CellList.css
@@ -19,10 +19,12 @@
.cellListEvenItem {
cursor: pointer;
+ zoom: 1;
}
.cellListOddItem {
cursor: pointer;
+ zoom: 1;
}
.cellListKeyboardSelectedItem {
diff --git a/user/src/com/google/gwt/user/cellview/client/CellTree.css b/user/src/com/google/gwt/user/cellview/client/CellTree.css
index 23c83d1..8d521c3 100644
--- a/user/src/com/google/gwt/user/cellview/client/CellTree.css
+++ b/user/src/com/google/gwt/user/cellview/client/CellTree.css
@@ -27,6 +27,7 @@
padding-bottom: 4px;
cursor: hand;
cursor: pointer;
+ zoom: 1;
}
.cellTreeItemImage {
@@ -34,7 +35,7 @@
}
.cellTreeItemImageValue {
-
+ zoom: 1;
}
.cellTreeItemValue {
diff --git a/user/src/com/google/gwt/user/cellview/client/CellTree.java b/user/src/com/google/gwt/user/cellview/client/CellTree.java
index 9460785..a3a7de6 100644
--- a/user/src/com/google/gwt/user/cellview/client/CellTree.java
+++ b/user/src/com/google/gwt/user/cellview/client/CellTree.java
@@ -443,11 +443,6 @@
+ "width:{2}px;height:{3}px;\">{4}</div>")
SafeHtml imageWrapper(String classes, String direction, int width,
int height, SafeHtml image);
-
- @Template("<div class=\"{0}\" style=\"position:absolute;{1}:-{2}px;"
- + "width:{2}px;height:{3}px;\">{4}</div>")
- SafeHtml imageWrapperIE6(String classes, String direction, int width,
- int height, SafeHtml image);
}
/**
@@ -464,30 +459,6 @@
}
/**
- * Implementation of {@link CellTable} used by IE6.
- */
- @SuppressWarnings("unused")
- private static class ImplIE6 extends Impl {
- @Override
- public SafeHtml imageWrapper(String classes, String direction, int width,
- int height, SafeHtml image) {
- /*
- * In IE6, left/right positions are relative to the inside of the padding
- * instead of the outside of the padding. The bug does not happen on IE7,
- * which maps to the IE6 user agent, so we need a runtime check for IE6.
- */
- if (isIe6()) {
- return template.imageWrapperIE6(classes, direction, width, height, image);
- }
- return super.imageWrapper(classes, direction, width, height, image);
- }
-
- private native boolean isIe6() /*-{
- return @com.google.gwt.dom.client.DOMImplIE6::isIE6()();
- }-*/;
- }
-
- /**
* The default number of children to show under a tree node.
*/
private static final int DEFAULT_LIST_SIZE = 25;
diff --git a/user/src/com/google/gwt/user/cellview/client/CellTreeBasic.css b/user/src/com/google/gwt/user/cellview/client/CellTreeBasic.css
index f008a26..6ff00d7 100644
--- a/user/src/com/google/gwt/user/cellview/client/CellTreeBasic.css
+++ b/user/src/com/google/gwt/user/cellview/client/CellTreeBasic.css
@@ -27,6 +27,7 @@
padding-bottom: 4px;
cursor: hand;
cursor: pointer;
+ zoom: 1;
}
.cellTreeItemImage {
@@ -34,7 +35,7 @@
}
.cellTreeItemImageValue {
-
+ zoom: 1;
}
.cellTreeItemValue {
diff --git a/user/test/com/google/gwt/activity/shared/ActivityManagerTest.java b/user/test/com/google/gwt/activity/shared/ActivityManagerTest.java
index 0f25f8b..16de717 100644
--- a/user/test/com/google/gwt/activity/shared/ActivityManagerTest.java
+++ b/user/test/com/google/gwt/activity/shared/ActivityManagerTest.java
@@ -81,7 +81,7 @@
return null;
}
}
- private static class SyncActivity implements Activity {
+ private static class SyncActivity extends Activity {
boolean canceled = false;
boolean stopped = false;
AcceptsOneWidget display;
diff --git a/user/test/com/google/gwt/cell/client/IconCellDecoratorTest.java b/user/test/com/google/gwt/cell/client/IconCellDecoratorTest.java
index 054b751..438bdf0 100644
--- a/user/test/com/google/gwt/cell/client/IconCellDecoratorTest.java
+++ b/user/test/com/google/gwt/cell/client/IconCellDecoratorTest.java
@@ -67,7 +67,7 @@
cell.render(context, "helloworld", sb);
// Compare the expected render string.
- String expected = "<div style=\"position:relative;padding-left:64px;\">";
+ String expected = "<div style=\"position:relative;padding-left:64px;zoom:1;\">";
expected += cell.getImageHtml(images.prettyPiccy(),
HasVerticalAlignment.ALIGN_MIDDLE, true).asString();
expected += "<div>helloworld</div>";
@@ -123,7 +123,7 @@
@Override
protected String getExpectedInnerHtml() {
IconCellDecorator<String> cell = createCell();
- String html = "<div style=\"position:relative;padding-left:64px;\">";
+ String html = "<div style=\"position:relative;padding-left:64px;zoom:1;\">";
html += cell.getIconHtml("helloworld").asString();
html += "<div>helloworld</div>";
html += "</div>";
@@ -133,7 +133,7 @@
@Override
protected String getExpectedInnerHtmlNull() {
IconCellDecorator<String> cell = createCell();
- String html = "<div style=\"position:relative;padding-left:64px;\">";
+ String html = "<div style=\"position:relative;padding-left:64px;zoom:1;\">";
html += cell.getIconHtml("helloworld").asString();
html += "<div></div>";
html += "</div>";
diff --git a/user/test/com/google/gwt/requestfactory/client/FindServiceTest.java b/user/test/com/google/gwt/requestfactory/client/FindServiceTest.java
index 6285434..1967839 100644
--- a/user/test/com/google/gwt/requestfactory/client/FindServiceTest.java
+++ b/user/test/com/google/gwt/requestfactory/client/FindServiceTest.java
@@ -23,6 +23,7 @@
import com.google.gwt.requestfactory.shared.SimpleBarProxy;
import com.google.gwt.requestfactory.shared.SimpleBarRequest;
import com.google.gwt.requestfactory.shared.SimpleFooProxy;
+import com.google.gwt.requestfactory.shared.SimpleFooRequest;
import com.google.gwt.requestfactory.shared.SimpleRequestFactory;
/**
@@ -138,6 +139,37 @@
});
}
+ public void testFetchsAfterCreateDontUpdate() {
+ final int[] count = {0};
+ final HandlerRegistration registration = EntityProxyChange.registerForProxyType(
+ req.getEventBus(), SimpleFooProxy.class,
+ new EntityProxyChange.Handler<SimpleFooProxy>() {
+ public void onProxyChange(EntityProxyChange<SimpleFooProxy> event) {
+ count[0]++;
+ }
+ });
+ delayTestFinish(TEST_DELAY);
+ SimpleFooRequest context = req.simpleFooRequest();
+ SimpleFooProxy proxy = context.create(SimpleFooProxy.class);
+ context.persistAndReturnSelf().using(proxy).fire(
+ new Receiver<SimpleFooProxy>() {
+ @Override
+ public void onSuccess(SimpleFooProxy response) {
+ // Persist and Update events
+ assertEquals(2, count[0]);
+ req.find(response.stableId()).fire(new Receiver<SimpleFooProxy>() {
+ @Override
+ public void onSuccess(SimpleFooProxy response) {
+ // No new events
+ assertEquals(2, count[0]);
+ registration.removeHandler();
+ finishTestAndReset();
+ }
+ });
+ }
+ });
+ }
+
/**
* Demonstrates behavior when fetching an unpersisted id. The setup is
* analagous to saving a future id into a cookie and then trying to fetch it
diff --git a/user/test/com/google/gwt/requestfactory/client/RequestFactoryTest.java b/user/test/com/google/gwt/requestfactory/client/RequestFactoryTest.java
index 5d0cb95..1763de6 100644
--- a/user/test/com/google/gwt/requestfactory/client/RequestFactoryTest.java
+++ b/user/test/com/google/gwt/requestfactory/client/RequestFactoryTest.java
@@ -28,7 +28,6 @@
import com.google.gwt.requestfactory.shared.SimpleFooProxy;
import com.google.gwt.requestfactory.shared.SimpleFooRequest;
import com.google.gwt.requestfactory.shared.SimpleValueProxy;
-import com.google.gwt.requestfactory.shared.UserInformationProxy;
import com.google.gwt.requestfactory.shared.Violation;
import com.google.gwt.requestfactory.shared.impl.SimpleEntityProxyId;
@@ -198,69 +197,6 @@
}
}
- /**
- * Test that removing a parent entity and implicitly removing the child sends
- * an event to the client that the child was removed.
- *
- * TODO(rjrjr): Should cascading deletes be detected?
- */
- public void disableTestMethodWithSideEffectDeleteChild() {
- delayTestFinish(DELAY_TEST_FINISH);
-
- // Persist bar.
- SimpleBarRequest context = req.simpleBarRequest();
- final SimpleBarProxy bar = context.create(SimpleBarProxy.class);
- context.persistAndReturnSelf().using(bar).fire(
- new Receiver<SimpleBarProxy>() {
- @Override
- public void onSuccess(SimpleBarProxy persistentBar) {
- persistentBar = checkSerialization(persistentBar);
- // Persist foo with bar as a child.
- SimpleFooRequest context = req.simpleFooRequest();
- SimpleFooProxy foo = context.create(SimpleFooProxy.class);
- final Request<SimpleFooProxy> persistRequest = context.persistAndReturnSelf().using(
- foo);
- foo = context.edit(foo);
- foo.setUserName("John");
- foo.setBarField(bar);
- persistRequest.fire(new Receiver<SimpleFooProxy>() {
- @Override
- public void onSuccess(SimpleFooProxy persistentFoo) {
- persistentFoo = checkSerialization(persistentFoo);
- // Handle changes to SimpleFooProxy.
- final SimpleFooEventHandler<SimpleFooProxy> fooHandler = new SimpleFooEventHandler<SimpleFooProxy>();
- EntityProxyChange.registerForProxyType(req.getEventBus(),
- SimpleFooProxy.class, fooHandler);
-
- // Handle changes to SimpleBarProxy.
- final SimpleFooEventHandler<SimpleBarProxy> barHandler = new SimpleFooEventHandler<SimpleBarProxy>();
- EntityProxyChange.registerForProxyType(req.getEventBus(),
- SimpleBarProxy.class, barHandler);
-
- // Delete bar.
- SimpleFooRequest context = req.simpleFooRequest();
- final Request<Void> deleteRequest = context.deleteBar().using(
- persistentFoo);
- SimpleFooProxy editable = context.edit(persistentFoo);
- editable.setBarField(bar);
- deleteRequest.fire(new Receiver<Void>() {
- @Override
- public void onSuccess(Void response) {
- assertEquals(1, fooHandler.updateEventCount); // set bar to
- // null
- assertEquals(1, fooHandler.totalEventCount);
-
- assertEquals(1, barHandler.deleteEventCount); // deleted bar
- assertEquals(1, barHandler.totalEventCount);
- finishTestAndReset();
- }
- });
- }
- });
- }
- });
- }
-
@Override
public String getModuleName() {
return "com.google.gwt.requestfactory.RequestFactorySuite";
@@ -778,6 +714,17 @@
});
}
+ public void testInstanceServiceRequestByName() {
+ delayTestFinish(DELAY_TEST_FINISH);
+ req.instanceServiceRequestByName().add(5).fire(new Receiver<Integer>() {
+ @Override
+ public void onSuccess(Integer response) {
+ assertEquals(10, (int) response);
+ finishTestAndReset();
+ }
+ });
+ }
+
/**
* Make sure our stock RF logging service keeps receiving.
*/
@@ -800,6 +747,68 @@
});
}
+ /**
+ * Test that removing a parent entity and implicitly removing the child sends
+ * an event to the client that the child was removed.
+ */
+ public void testMethodWithSideEffectDeleteChild() {
+ delayTestFinish(DELAY_TEST_FINISH);
+
+ // Handle changes to SimpleFooProxy.
+ final SimpleFooEventHandler<SimpleFooProxy> fooHandler = new SimpleFooEventHandler<SimpleFooProxy>();
+ EntityProxyChange.registerForProxyType(req.getEventBus(),
+ SimpleFooProxy.class, fooHandler);
+
+ // Handle changes to SimpleBarProxy.
+ final SimpleFooEventHandler<SimpleBarProxy> barHandler = new SimpleFooEventHandler<SimpleBarProxy>();
+ EntityProxyChange.registerForProxyType(req.getEventBus(),
+ SimpleBarProxy.class, barHandler);
+
+ // Persist bar.
+ SimpleBarRequest context = req.simpleBarRequest();
+ final SimpleBarProxy bar = context.create(SimpleBarProxy.class);
+ context.persistAndReturnSelf().using(bar).fire(
+ new Receiver<SimpleBarProxy>() {
+ @Override
+ public void onSuccess(SimpleBarProxy persistentBar) {
+ persistentBar = checkSerialization(persistentBar);
+ // Persist foo with bar as a child.
+ SimpleFooRequest context = req.simpleFooRequest();
+ SimpleFooProxy foo = context.create(SimpleFooProxy.class);
+ final Request<SimpleFooProxy> persistRequest = context.persistAndReturnSelf().using(
+ foo).with("barField");
+ foo = context.edit(foo);
+ foo.setUserName("John");
+ foo.setBarField(bar);
+ persistRequest.fire(new Receiver<SimpleFooProxy>() {
+ @Override
+ public void onSuccess(SimpleFooProxy persistentFoo) {
+ persistentFoo = checkSerialization(persistentFoo);
+
+ // Delete bar.
+ SimpleFooRequest context = req.simpleFooRequest();
+ final Request<Void> deleteRequest = context.deleteBar().using(
+ persistentFoo);
+ deleteRequest.fire(new Receiver<Void>() {
+ @Override
+ public void onSuccess(Void response) {
+ assertEquals(1, fooHandler.persistEventCount);
+ assertEquals(2, fooHandler.updateEventCount);
+ assertEquals(3, fooHandler.totalEventCount);
+
+ assertEquals(1, barHandler.persistEventCount);
+ assertEquals(1, barHandler.updateEventCount);
+ assertEquals(1, barHandler.deleteEventCount);
+ assertEquals(3, barHandler.totalEventCount);
+ finishTestAndReset();
+ }
+ });
+ }
+ });
+ }
+ });
+ }
+
/*
* tests that (a) any method can have a side effect that is handled correctly.
* (b) instance methods are handled correctly and (c) a request cannot be
@@ -998,7 +1007,7 @@
*/
public void testNullValueInIntegerListRequest() {
delayTestFinish(DELAY_TEST_FINISH);
- List<Integer> list = Arrays.asList(new Integer[] {1, 2, null});
+ List<Integer> list = Arrays.asList(new Integer[]{1, 2, null});
final Request<Void> fooReq = req.simpleFooRequest().receiveNullValueInIntegerList(
list);
fooReq.fire(new Receiver<Void>() {
@@ -1014,7 +1023,7 @@
*/
public void testNullValueInStringListRequest() {
delayTestFinish(DELAY_TEST_FINISH);
- List<String> list = Arrays.asList(new String[] {"nonnull", "null", null});
+ List<String> list = Arrays.asList(new String[]{"nonnull", "null", null});
final Request<Void> fooReq = req.simpleFooRequest().receiveNullValueInStringList(
list);
fooReq.fire(new Receiver<Void>() {
@@ -1060,6 +1069,27 @@
});
}
+ /**
+ * Test that the server code will not allow a persisted entity to be returned
+ * if it has a null version property.
+ */
+ public void testPersistedEntityWithNullVersion() {
+ delayTestFinish(DELAY_TEST_FINISH);
+ simpleFooRequest().getSimpleFooWithNullVersion().fire(
+ new Receiver<SimpleFooProxy>() {
+
+ @Override
+ public void onFailure(ServerFailure error) {
+ finishTestAndReset();
+ }
+
+ @Override
+ public void onSuccess(SimpleFooProxy response) {
+ fail();
+ }
+ });
+ }
+
public void testPersistExistingEntityExistingRelation() {
delayTestFinish(DELAY_TEST_FINISH);
@@ -1791,21 +1821,6 @@
});
}
- public void testPrimitiveListBooleanAsParameter() {
- delayTestFinish(DELAY_TEST_FINISH);
-
- Request<Boolean> procReq = simpleFooRequest().processBooleanList(
- Arrays.asList(true, false));
-
- procReq.fire(new Receiver<Boolean>() {
- @Override
- public void onSuccess(Boolean response) {
- assertEquals(true, (boolean) response);
- finishTestAndReset();
- }
- });
- }
-
public void testPrimitiveListBigDecimalAsParameter() {
delayTestFinish(DELAY_TEST_FINISH);
@@ -1846,6 +1861,21 @@
});
}
+ public void testPrimitiveListBooleanAsParameter() {
+ delayTestFinish(DELAY_TEST_FINISH);
+
+ Request<Boolean> procReq = simpleFooRequest().processBooleanList(
+ Arrays.asList(true, false));
+
+ procReq.fire(new Receiver<Boolean>() {
+ @Override
+ public void onSuccess(Boolean response) {
+ assertEquals(true, (boolean) response);
+ finishTestAndReset();
+ }
+ });
+ }
+
@SuppressWarnings("deprecation")
public void testPrimitiveListDateAsParameter() {
delayTestFinish(DELAY_TEST_FINISH);
@@ -2280,28 +2310,6 @@
});
}
- /**
- * We provide a simple UserInformation class to give GAE developers a hand,
- * and other developers a hint. Make sure RF doesn't break it (it relies on
- * server side upcasting, and a somewhat sleazey reflective lookup mechanism
- * in a static method on UserInformation).
- */
- public void testUserInfo() {
- delayTestFinish(DELAY_TEST_FINISH);
- req.userInformationRequest().getCurrentUserInformation("").fire(
- new Receiver<UserInformationProxy>() {
- @Override
- public void onSuccess(UserInformationProxy response) {
- response = checkSerialization(response);
- assertEquals("Dummy Email", response.getEmail());
- assertEquals("Dummy User", response.getName());
- assertEquals("", response.getLoginUrl());
- assertEquals("", response.getLogoutUrl());
- finishTestAndReset();
- }
- });
- }
-
public void testValueObjectCreateSetRetrieveUpdate() {
delayTestFinish(DELAY_TEST_FINISH);
SimpleFooRequest req = simpleFooRequest();
diff --git a/user/test/com/google/gwt/requestfactory/client/RequestFactoryUnicodeEscapingTest.java b/user/test/com/google/gwt/requestfactory/client/RequestFactoryUnicodeEscapingTest.java
index 44de4ed..afdc2a3 100644
--- a/user/test/com/google/gwt/requestfactory/client/RequestFactoryUnicodeEscapingTest.java
+++ b/user/test/com/google/gwt/requestfactory/client/RequestFactoryUnicodeEscapingTest.java
@@ -89,7 +89,7 @@
verifyStringContainingCharacterRange(current,
Math.min(end, current + size), response);
} catch (InvalidCharacterException e) {
- fails.add(new ServerFailure(e.getMessage(), null, null));
+ fails.add(new ServerFailure(e.getMessage()));
}
nextBatch();
}
diff --git a/user/test/com/google/gwt/requestfactory/server/RequestFactoryExceptionHandlerServlet.java b/user/test/com/google/gwt/requestfactory/server/RequestFactoryExceptionHandlerServlet.java
index a5d3c1a..1bdcc6d 100644
--- a/user/test/com/google/gwt/requestfactory/server/RequestFactoryExceptionHandlerServlet.java
+++ b/user/test/com/google/gwt/requestfactory/server/RequestFactoryExceptionHandlerServlet.java
@@ -26,7 +26,7 @@
super(new ExceptionHandler() {
public ServerFailure createServerFailure(Throwable throwable) {
return new ServerFailure(throwable.getMessage(),
- throwable.getClass().getName(), "my stack trace");
+ throwable.getClass().getName(), "my stack trace", true);
}
});
}
diff --git a/user/test/com/google/gwt/requestfactory/server/RequestFactoryInterfaceValidatorTest.java b/user/test/com/google/gwt/requestfactory/server/RequestFactoryInterfaceValidatorTest.java
index 1c6c003..e6a4e10 100644
--- a/user/test/com/google/gwt/requestfactory/server/RequestFactoryInterfaceValidatorTest.java
+++ b/user/test/com/google/gwt/requestfactory/server/RequestFactoryInterfaceValidatorTest.java
@@ -32,6 +32,7 @@
import junit.framework.TestCase;
+import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.logging.Level;
@@ -195,6 +196,14 @@
interface LocatorEntityProxy extends EntityProxy {
}
+ @ProxyForName(value = "com.google.gwt.requestfactory.server.RequestFactoryInterfaceValidatorTest.LocatorEntity", locator = "badLocator")
+ interface LocatorEntityProxyWithBadLocator extends EntityProxy {
+ }
+
+ @ProxyForName(value = "badDomainType", locator = "com.google.gwt.requestfactory.server.RequestFactoryInterfaceValidatorTest.LocatorEntityProxyWithBadServiceName")
+ interface LocatorEntityProxyWithBadServiceName extends EntityProxy {
+ }
+
@ProxyFor(value = Value.class)
interface MyValueProxy extends ValueProxy {
}
@@ -262,10 +271,44 @@
static class Value {
}
- RequestFactoryInterfaceValidator v;
+ static class VisibleErrorContext extends
+ RequestFactoryInterfaceValidator.ErrorContext {
+ final List<String> logs;
+
+ public VisibleErrorContext(Logger logger) {
+ super(logger);
+ logs = new ArrayList<String>();
+ }
+
+ public VisibleErrorContext(VisibleErrorContext that) {
+ super(that);
+ this.logs = that.logs;
+ }
+
+ @Override
+ public void poison(String msg, Object... args) {
+ logs.add(String.format(msg, args));
+ super.poison(msg, args);
+ }
+
+ @Override
+ public void poison(String msg, Throwable t) {
+ logs.add(msg);
+ super.poison(msg, t);
+ }
+
+ @Override
+ protected VisibleErrorContext fork() {
+ return new VisibleErrorContext(this);
+ }
+ }
+
+ RequestFactoryInterfaceValidator v;;
private static final boolean DUMP_PAYLOAD = Boolean.getBoolean("gwt.rpc.dumpPayload");;
+ private VisibleErrorContext errors;
+
/**
* Ensure that calling {@link RequestFactoryInterfaceValidator#antidote()}
* doesn't cause information to be lost.
@@ -277,7 +320,19 @@
assertFalse(v.isPoisoned());
v.validateRequestContext(RequestContextMissingAnnotation.class.getName());
assertTrue(v.isPoisoned());
- };
+ }
+
+ public void testBadLocatorName() {
+ v.validateEntityProxy(LocatorEntityProxyWithBadLocator.class.getName());
+ assertTrue(v.isPoisoned());
+ assertTrue(errors.logs.contains("Cannot find locator named badLocator"));
+ }
+
+ public void testBadServiceName() {
+ v.validateEntityProxy(LocatorEntityProxyWithBadServiceName.class.getName());
+ assertTrue(v.isPoisoned());
+ assertTrue(errors.logs.contains("Cannot find domain type named badDomainType"));
+ }
/**
* Test that subclasses of {@code java.util.Date} are not transportable.
@@ -393,7 +448,8 @@
protected void setUp() throws Exception {
Logger logger = Logger.getLogger("");
logger.setLevel(DUMP_PAYLOAD ? Level.ALL : Level.OFF);
- v = new RequestFactoryInterfaceValidator(logger, new ClassLoaderLoader(
+ errors = new VisibleErrorContext(logger);
+ v = new RequestFactoryInterfaceValidator(errors, new ClassLoaderLoader(
Thread.currentThread().getContextClassLoader()));
}
}
diff --git a/user/test/com/google/gwt/requestfactory/server/SimpleFoo.java b/user/test/com/google/gwt/requestfactory/server/SimpleFoo.java
index 1ac5987..afcd18e 100644
--- a/user/test/com/google/gwt/requestfactory/server/SimpleFoo.java
+++ b/user/test/com/google/gwt/requestfactory/server/SimpleFoo.java
@@ -124,6 +124,16 @@
return list;
}
+ /**
+ * This tests that the server detects and disallows the use of persisted
+ * objects with a null version property.
+ */
+ public static SimpleFoo getSimpleFooWithNullVersion() {
+ SimpleFoo foo = new SimpleFoo();
+ foo.setVersion(null);
+ return foo;
+ }
+
public static SimpleFoo getSimpleFooWithSubPropertyCollection() {
SimpleFoo foo = new SimpleFoo();
SimpleFoo subFoo = new SimpleFoo();
@@ -447,9 +457,11 @@
public void deleteBar() {
if (barField != null) {
+ isChanged = true;
barField.delete();
}
barField = null;
+ persist();
}
public SimpleBar getBarField() {
diff --git a/user/test/com/google/gwt/requestfactory/shared/BasicRequestFactory.java b/user/test/com/google/gwt/requestfactory/shared/BasicRequestFactory.java
index 8b02b3f..8b50964 100644
--- a/user/test/com/google/gwt/requestfactory/shared/BasicRequestFactory.java
+++ b/user/test/com/google/gwt/requestfactory/shared/BasicRequestFactory.java
@@ -20,8 +20,5 @@
* RequestFactory interfaces works correctly.
*/
public interface BasicRequestFactory extends RequestFactory {
-
LoggingRequest loggingRequest();
-
- UserInformationRequest userInformationRequest();
}
diff --git a/user/test/com/google/gwt/requestfactory/shared/ComplexKeysTest.java b/user/test/com/google/gwt/requestfactory/shared/ComplexKeysTest.java
index 60af61d..701a276 100644
--- a/user/test/com/google/gwt/requestfactory/shared/ComplexKeysTest.java
+++ b/user/test/com/google/gwt/requestfactory/shared/ComplexKeysTest.java
@@ -67,8 +67,8 @@
return key;
}
- public Void getVersion() {
- return null;
+ public Integer getVersion() {
+ return 0;
}
}
@@ -101,8 +101,8 @@
return key;
}
- public Void getVersion() {
- return null;
+ public Integer getVersion() {
+ return 0;
}
}
@@ -129,8 +129,8 @@
return key;
}
- public Void getVersion() {
- return null;
+ public Integer getVersion() {
+ return 0;
}
}
diff --git a/user/src/com/google/gwt/activity/shared/AbstractActivity.java b/user/test/com/google/gwt/requestfactory/shared/InstanceServiceRequestByName.java
similarity index 61%
copy from user/src/com/google/gwt/activity/shared/AbstractActivity.java
copy to user/test/com/google/gwt/requestfactory/shared/InstanceServiceRequestByName.java
index 59e596c..0934a97 100644
--- a/user/src/com/google/gwt/activity/shared/AbstractActivity.java
+++ b/user/test/com/google/gwt/requestfactory/shared/InstanceServiceRequestByName.java
@@ -1,33 +1,25 @@
/*
* Copyright 2010 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.gwt.activity.shared;
+package com.google.gwt.requestfactory.shared;
/**
- * Simple Activity implementation that is always willing to stop,
- * and does nothing onStop and onCancel.
+ * Used to test the ServiceLocator extension hook.
*/
-public abstract class AbstractActivity implements Activity {
-
- public String mayStop() {
- return null;
- }
-
- public void onCancel() {
- }
-
- public void onStop() {
- }
+@ServiceName(value = "com.google.gwt.requestfactory.server.InstanceService",
+ locator = "com.google.gwt.requestfactory.server.InstanceServiceLocator")
+public interface InstanceServiceRequestByName extends RequestContext {
+ Request<Integer> add(int value);
}
diff --git a/user/test/com/google/gwt/requestfactory/shared/SimpleFooRequest.java b/user/test/com/google/gwt/requestfactory/shared/SimpleFooRequest.java
index bbb4c6b..a27ad31 100644
--- a/user/test/com/google/gwt/requestfactory/shared/SimpleFooRequest.java
+++ b/user/test/com/google/gwt/requestfactory/shared/SimpleFooRequest.java
@@ -49,6 +49,8 @@
Request<Set<Integer>> getNumberSet();
+ Request<SimpleFooProxy> getSimpleFooWithNullVersion();
+
Request<SimpleFooProxy> getSimpleFooWithSubPropertyCollection();
Request<SimpleFooProxy> getTripletReference();
diff --git a/user/test/com/google/gwt/requestfactory/shared/SimpleRequestFactory.java b/user/test/com/google/gwt/requestfactory/shared/SimpleRequestFactory.java
index 7def869..acd0863 100644
--- a/user/test/com/google/gwt/requestfactory/shared/SimpleRequestFactory.java
+++ b/user/test/com/google/gwt/requestfactory/shared/SimpleRequestFactory.java
@@ -23,6 +23,8 @@
InstanceServiceRequest instanceServiceRequest();
+ InstanceServiceRequestByName instanceServiceRequestByName();
+
SimpleBarRequest simpleBarRequest();
SimpleFooRequest simpleFooRequest();