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();