Revision: 8411
Author: [email protected]
Date: Fri Jul 23 10:49:57 2010
Log: Add support for authentication
Review at http://gwt-code-reviews.appspot.com/706802
Review by: [email protected]
http://code.google.com/p/google-web-toolkit/source/detail?r=8411
Added:
/trunk/bikeshed/src/com/google/gwt/sample/expenses/gwt/request/UserInformationRecord.java
/trunk/bikeshed/src/com/google/gwt/sample/expenses/gwt/request/UserInformationRecordChanged.java
/trunk/bikeshed/src/com/google/gwt/sample/expenses/gwt/request/UserInformationRequest.java
/trunk/bikeshed/src/com/google/gwt/sample/expenses/server/domain/GaeUserInformation.java
/trunk/user/src/com/google/gwt/requestfactory/client/AuthenticationFailureHandler.java
/trunk/user/src/com/google/gwt/requestfactory/client/LoginWidget.java
/trunk/user/src/com/google/gwt/requestfactory/client/LoginWidget.ui.xml
/trunk/user/src/com/google/gwt/requestfactory/server/UserInformationImpl.java
/trunk/user/src/com/google/gwt/requestfactory/shared/UserInformation.java
Modified:
/trunk/bikeshed/src/com/google/gwt/sample/expenses/gwt/client/Scaffold.java
/trunk/bikeshed/src/com/google/gwt/sample/expenses/gwt/client/ScaffoldShell.java
/trunk/bikeshed/src/com/google/gwt/sample/expenses/gwt/client/ScaffoldShell.ui.xml
/trunk/bikeshed/src/com/google/gwt/sample/expenses/gwt/request/ExpensesRequestFactory.java
/trunk/bikeshed/war/WEB-INF/web.xml
/trunk/user/src/com/google/gwt/requestfactory/client/impl/RequestFactoryJsonImpl.java
/trunk/user/src/com/google/gwt/requestfactory/rebind/RequestFactoryGenerator.java
/trunk/user/src/com/google/gwt/requestfactory/server/RequestFactoryServlet.java
/trunk/user/src/com/google/gwt/requestfactory/shared/RequestEvent.java
=======================================
--- /dev/null
+++
/trunk/bikeshed/src/com/google/gwt/sample/expenses/gwt/request/UserInformationRecord.java
Fri Jul 23 10:49:57 2010
@@ -0,0 +1,45 @@
+/*
+ * 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.gwt.request;
+
+import com.google.gwt.requestfactory.shared.DataTransferObject;
+import com.google.gwt.requestfactory.shared.UserInformation;
+import com.google.gwt.sample.expenses.server.domain.GaeUserInformation;
+import com.google.gwt.valuestore.shared.Property;
+import com.google.gwt.valuestore.shared.Record;
+
+/**
+ * "API Generated" DTO interface based on
+ * {...@link com.google.gwt.sample.expenses.server.domain.GaeUserInformation}.
+ * <p>
+ * IRL this class will be generated by a JPA-savvy tool run before
compilation.
+ */
+...@datatransferobject(GaeUserInformation.class)
+public interface UserInformationRecord extends Record, UserInformation {
+ Property<String> email =
+ new Property<String>("email", "Email", String.class);
+ Property<String> loginUrl =
+ new Property<String>("loginUrl", "LoginUrl", String.class);
+ Property<String> logoutUrl =
+ new Property<String>("logoutUrl", "LogoutUrl", String.class);
+ Property<String> name =
+ new Property<String>("name", "Name", String.class);
+
+ // Most of the actual getXxx calls come from the UserInformationProvider
+ // interface
+
+}
=======================================
--- /dev/null
+++
/trunk/bikeshed/src/com/google/gwt/sample/expenses/gwt/request/UserInformationRecordChanged.java
Fri Jul 23 10:49:57 2010
@@ -0,0 +1,55 @@
+/*
+ * 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.gwt.request;
+
+import com.google.gwt.event.shared.EventHandler;
+import com.google.gwt.event.shared.GwtEvent;
+import com.google.gwt.valuestore.shared.RecordChangedEvent;
+import com.google.gwt.valuestore.shared.WriteOperation;
+
+/**
+ * "API Generated" event posted when the values of a {...@link
UserInformationRecord}
+ * change.
+ * <p>
+ * IRL this class will be generated by a JPA-savvy tool run before
compilation.
+ */
+public class UserInformationRecordChanged extends
+ RecordChangedEvent<UserInformationRecord,
UserInformationRecordChanged.Handler> {
+
+ /**
+ * Implemented by handlers of this type of event.
+ */
+ public interface Handler extends EventHandler {
+ void onUserInformationChanged(UserInformationRecordChanged event);
+ }
+
+ public static final Type<Handler> TYPE = new Type<Handler>();
+
+ public UserInformationRecordChanged(UserInformationRecord record,
WriteOperation writeOperation) {
+ super(record, writeOperation);
+ }
+
+ @Override
+ public GwtEvent.Type<Handler> getAssociatedType() {
+ return TYPE;
+ }
+
+ @Override
+ protected void dispatch(Handler handler) {
+ handler.onUserInformationChanged(this);
+ }
+}
=======================================
--- /dev/null
+++
/trunk/bikeshed/src/com/google/gwt/sample/expenses/gwt/request/UserInformationRequest.java
Fri Jul 23 10:49:57 2010
@@ -0,0 +1,35 @@
+/*
+ * 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.gwt.request;
+
+import com.google.gwt.requestfactory.shared.RecordRequest;
+import com.google.gwt.requestfactory.shared.Service;
+import com.google.gwt.sample.expenses.server.domain.GaeUserInformation;
+
+/**
+ * "API Generated" request selector interface implemented by objects that
give
+ * client access to the methods of
+ * {...@link com.google.gwt.sample.expenses.server.domain.GaeUserInformation}.
+ * <p>
+ * IRL this class will be generated by a JPA-savvy tool run before
compilation.
+ */
+...@service(GaeUserInformation.class)
+public interface UserInformationRequest {
+
+ RecordRequest<UserInformationRecord> getCurrentUserInformation();
+
+}
=======================================
--- /dev/null
+++
/trunk/bikeshed/src/com/google/gwt/sample/expenses/server/domain/GaeUserInformation.java
Fri Jul 23 10:49:57 2010
@@ -0,0 +1,71 @@
+/*
+ * 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.UserInformationImpl;
+
+/**
+ * A user information class that uses the Google App Engine authentication
+ * framework
+ */
+public class GaeUserInformation extends UserInformationImpl {
+ private static UserService userService =
UserServiceFactory.getUserService();
+
+ public static GaeUserInformation getCurrentUserInformation() {
+ return new GaeUserInformation();
+ }
+
+ public String getEmail() {
+ User user = userService.getCurrentUser();
+ if (user == null) {
+ return "";
+ }
+ return user.getEmail();
+ }
+
+ public String getLoginUrl() {
+ return userService.createLoginURL("RETURN_URL");
+ }
+
+ public String getLogoutUrl() {
+ return userService.createLogoutURL("RETURN_URL");
+ }
+
+ public String getName() {
+ User user = userService.getCurrentUser();
+ if (user == null) {
+ return "";
+ }
+ return user.getNickname();
+ }
+
+ public Long getId() {
+ User user = userService.getCurrentUser();
+ if (user == null) {
+ return 0L;
+ }
+ return new Long(user.hashCode());
+ }
+
+ public boolean isUserLoggedIn() {
+ return userService.isUserLoggedIn();
+ }
+
+}
=======================================
--- /dev/null
+++
/trunk/user/src/com/google/gwt/requestfactory/client/AuthenticationFailureHandler.java
Fri Jul 23 10:49:57 2010
@@ -0,0 +1,60 @@
+/*
+ * 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.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;
+
+ @Override
+ 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");
+ loginUrl = loginUrl.replace("RETURN_URL", Location.getHref());
+ 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();
+ }
+ }
+ }
+
+}
=======================================
--- /dev/null
+++ /trunk/user/src/com/google/gwt/requestfactory/client/LoginWidget.java
Fri Jul 23 10:49:57 2010
@@ -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.requestfactory.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.UserInformation;
+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));
+ }
+
+ public void setUserInformation(UserInformation info) {
+ name.setInnerText(info.getName());
+ logoutUrl = info.getLogoutUrl();
+ logoutUrl = logoutUrl.replace("RETURN_URL", Location.getHref());
+ }
+
+ @UiHandler("logoutLink")
+ void handleClick(ClickEvent e) {
+ if (logoutUrl != "") {
+ Location.replace(logoutUrl);
+ }
+ }
+
+}
=======================================
--- /dev/null
+++ /trunk/user/src/com/google/gwt/requestfactory/client/LoginWidget.ui.xml
Fri Jul 23 10:49:57 2010
@@ -0,0 +1,10 @@
+<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
+ xmlns:g='urn:import:com.google.gwt.user.client.ui'>
+
+ <g:HTMLPanel>
+ <div>
+ <span ui:field="name">Not logged in</span> |
+ <g:InlineLabel ui:field="logoutLink">Sign out</g:InlineLabel>
+ </div>
+ </g:HTMLPanel>
+</ui:UiBinder>
=======================================
--- /dev/null
+++
/trunk/user/src/com/google/gwt/requestfactory/server/UserInformationImpl.java
Fri Jul 23 10:49:57 2010
@@ -0,0 +1,73 @@
+/*
+ * 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;
+
+import com.google.gwt.requestfactory.shared.UserInformation;
+
+/**
+ * A base class for providing authentication related information about the
+ * user. This implementation treats the user as constantly signed in, and
+ * without any information. Services that want real authentication should
+ * subclass this class.
+ */
+public class UserInformationImpl implements UserInformation {
+ public static UserInformationImpl getCurrentUserInformation() {
+ return new UserInformationImpl();
+ }
+
+ private Long id = 0L;
+ private Integer version = 0;
+
+ public UserInformationImpl() {
+ }
+
+ public String getEmail() {
+ return "";
+ }
+
+ public Long getId() {
+ return this.id;
+ }
+
+ public String getLoginUrl() {
+ return "";
+ }
+
+ public String getLogoutUrl() {
+ return "";
+ }
+
+ public String getName() {
+ return "";
+ }
+
+ public Integer getVersion() {
+ return this.version;
+ }
+
+ public boolean isUserLoggedIn() {
+ return true;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public void setVersion(Integer version) {
+ this.version = version;
+ }
+}
=======================================
--- /dev/null
+++
/trunk/user/src/com/google/gwt/requestfactory/shared/UserInformation.java
Fri Jul 23 10:49:57 2010
@@ -0,0 +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.requestfactory.shared;
+
+/**
+ * The information which is made available to the {...@link LoginWidget} and
other
+ * UI elements that are interested in user information.
+ */
+public interface UserInformation {
+ String getEmail();
+ String getLoginUrl();
+ String getLogoutUrl();
+ String getName();
+}
=======================================
---
/trunk/bikeshed/src/com/google/gwt/sample/expenses/gwt/client/Scaffold.java
Fri Jun 18 20:16:34 2010
+++
/trunk/bikeshed/src/com/google/gwt/sample/expenses/gwt/client/Scaffold.java
Fri Jul 23 10:49:57 2010
@@ -25,12 +25,16 @@
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
import com.google.gwt.event.shared.HandlerManager;
+import com.google.gwt.requestfactory.client.AuthenticationFailureHandler;
+import com.google.gwt.requestfactory.client.LoginWidget;
+import com.google.gwt.requestfactory.shared.Receiver;
import com.google.gwt.requestfactory.shared.RequestEvent;
import com.google.gwt.requestfactory.shared.RequestEvent.State;
import com.google.gwt.sample.expenses.gwt.client.place.ListScaffoldPlace;
import com.google.gwt.sample.expenses.gwt.client.place.ScaffoldPlace;
import
com.google.gwt.sample.expenses.gwt.request.ExpensesEntityTypesProcessor;
import com.google.gwt.sample.expenses.gwt.request.ExpensesRequestFactory;
+import com.google.gwt.sample.expenses.gwt.request.UserInformationRecord;
import com.google.gwt.sample.expenses.gwt.ui.ListActivitiesMapper;
import com.google.gwt.sample.expenses.gwt.ui.ScaffoldListPlaceRenderer;
import com.google.gwt.user.client.ui.RootLayoutPanel;
@@ -72,6 +76,22 @@
}
}
});
+
+ /* Check for Authentication failures or mismatches */
+
+ eventBus.addHandler(RequestEvent.TYPE, new
AuthenticationFailureHandler());
+
+ /* Add a login widget to the page */
+
+ final LoginWidget login = shell.getLoginWidget();
+ Receiver reciever = new Receiver<UserInformationRecord>() {
+ @Override
+ public void onSuccess(UserInformationRecord userInformationRecord) {
+ login.setUserInformation(userInformationRecord);
+ }
+ };
+ requestFactory.userInformationRequest()
+ .getCurrentUserInformation().to(reciever).fire();
/* Left side lets us pick from all the types of entities */
=======================================
---
/trunk/bikeshed/src/com/google/gwt/sample/expenses/gwt/client/ScaffoldShell.java
Mon Jun 7 12:20:31 2010
+++
/trunk/bikeshed/src/com/google/gwt/sample/expenses/gwt/client/ScaffoldShell.java
Fri Jul 23 10:49:57 2010
@@ -19,6 +19,7 @@
import com.google.gwt.app.place.PlacePickerView;
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.DivElement;
+import com.google.gwt.requestfactory.client.LoginWidget;
import com.google.gwt.sample.expenses.gwt.client.place.ListScaffoldPlace;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
@@ -34,11 +35,12 @@
}
private static final Binder BINDER = GWT.create(Binder.class);
- @UiField SimplePanel master;
@UiField SimplePanel details;
- @UiField PlacePickerView<ListScaffoldPlace> placesBox;
@UiField DivElement error;
+ @UiField LoginWidget loginWidget;
+ @UiField SimplePanel master;
@UiField NotificationMole mole;
+ @UiField PlacePickerView<ListScaffoldPlace> placesBox;
public ScaffoldShell() {
initWidget(BINDER.createAndBindUi(this));
@@ -51,6 +53,13 @@
return details;
}
+ /**
+ * @return the login widget
+ */
+ public LoginWidget getLoginWidget() {
+ return loginWidget;
+ }
+
/**
* @return the panel to hold the master list
*/
=======================================
---
/trunk/bikeshed/src/com/google/gwt/sample/expenses/gwt/client/ScaffoldShell.ui.xml
Mon Jun 7 12:20:31 2010
+++
/trunk/bikeshed/src/com/google/gwt/sample/expenses/gwt/client/ScaffoldShell.ui.xml
Fri Jul 23 10:49:57 2010
@@ -1,6 +1,7 @@
<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
xmlns:g='urn:import:com.google.gwt.user.client.ui'
+ xmlns:r='urn:import:com.google.gwt.requestfactory.client'
xmlns:a='urn:import:com.google.gwt.app.client'>
<ui:image field='gwtLogo'></ui:image>
@@ -44,6 +45,14 @@
text-align: center;
background-color: red;
}
+
+ .login {
+ position: absolute;
+ left: 75%;
+ right: 0%;
+ text-align: center;
+ background-color: yellow;
+ }
.users {
position: absolute;
@@ -106,12 +115,13 @@
<g:DockLayoutPanel unit='EM'>
<g:north size='6'>
- <g:HTML styleName='{style.centered}'>
+ <g:HTMLPanel styleName='{style.centered}'>
<div class='{style.banner}'>
<div class='{style.error}' ui:field='error'></div>
<span class='{style.title}'><h2>Data Browser</h2></span>
+ <r:LoginWidget styleName='{style.login}' ui:field="loginWidget"/>
</div>
- </g:HTML>
+ </g:HTMLPanel>
</g:north>
<g:south size='2'>
<g:HTML>
=======================================
---
/trunk/bikeshed/src/com/google/gwt/sample/expenses/gwt/request/ExpensesRequestFactory.java
Fri May 28 08:44:36 2010
+++
/trunk/bikeshed/src/com/google/gwt/sample/expenses/gwt/request/ExpensesRequestFactory.java
Fri Jul 23 10:49:57 2010
@@ -41,6 +41,11 @@
* @return a request selector
*/
ReportRequest reportRequest();
+
+ /**
+ * @return a request selector
+ */
+ UserInformationRequest userInformationRequest();
// todo: probably will also need something like this to support custom
service
// methods
=======================================
--- /trunk/bikeshed/war/WEB-INF/web.xml Tue Jul 20 10:54:02 2010
+++ /trunk/bikeshed/war/WEB-INF/web.xml Fri Jul 23 10:49:57 2010
@@ -9,6 +9,10 @@
<servlet>
<servlet-name>requestFactoryServlet</servlet-name>
<servlet-class>com.google.gwt.requestfactory.server.RequestFactoryServlet</servlet-class>
+ <init-param>
+ <param-name>userInfoClass</param-name>
+
<param-value>com.google.gwt.sample.expenses.server.domain.GaeUserInformation</param-value>
+ </init-param>
</servlet>
<servlet>
=======================================
---
/trunk/user/src/com/google/gwt/requestfactory/client/impl/RequestFactoryJsonImpl.java
Thu Jun 24 14:48:00 2010
+++
/trunk/user/src/com/google/gwt/requestfactory/client/impl/RequestFactoryJsonImpl.java
Fri Jul 23 10:49:57 2010
@@ -46,10 +46,10 @@
*/
public abstract class RequestFactoryJsonImpl implements RequestFactory {
+ private HandlerManager handlerManager;
+
private ValueStoreJsonImpl valueStore;
- private HandlerManager handlerManager;
-
public void fire(final RequestObject<?> requestObject) {
RequestBuilder builder = new RequestBuilder(RequestBuilder.POST,
GWT.getHostPageBaseURL() + RequestFactory.URL);
@@ -57,7 +57,7 @@
builder.setCallback(new RequestCallback() {
public void onError(Request request, Throwable exception) {
- postRequestEvent(State.RECEIVED);
+ postRequestEvent(State.RECEIVED, null);
// shell.error.setInnerText(SERVER_ERROR);
}
@@ -69,14 +69,14 @@
// shell.error.setInnerText(SERVER_ERROR + " ("
// + response.getStatusText() + ")");
}
- postRequestEvent(State.RECEIVED);
+ postRequestEvent(State.RECEIVED, response);
}
});
try {
builder.send();
- postRequestEvent(State.SENT);
+ postRequestEvent(State.SENT, null);
} catch (RequestException e) {
// shell.error.setInnerText(SERVER_ERROR + " (" + e.getMessage() +
// ")");
@@ -105,7 +105,7 @@
builder.setCallback(new RequestCallback() {
public void onError(Request request, Throwable exception) {
- postRequestEvent(State.RECEIVED);
+ postRequestEvent(State.RECEIVED, null);
// shell.error.setInnerText(SERVER_ERROR);
}
@@ -117,13 +117,13 @@
// shell.error.setInnerText(SERVER_ERROR + " ("
// + response.getStatusText() + ")");
}
- postRequestEvent(State.RECEIVED);
+ postRequestEvent(State.RECEIVED, response);
}
});
try {
builder.send();
- postRequestEvent(State.SENT);
+ postRequestEvent(State.SENT, null);
} catch (RequestException e) {
// shell.error.setInnerText(SERVER_ERROR + " (" + e.getMessage()
+
// ")");
@@ -145,7 +145,7 @@
this.handlerManager = handlerManager;
}
- private void postRequestEvent(State received) {
- handlerManager.fireEvent(new RequestEvent(received));
+ private void postRequestEvent(State received, Response response) {
+ handlerManager.fireEvent(new RequestEvent(received, response));
}
}
=======================================
---
/trunk/user/src/com/google/gwt/requestfactory/rebind/RequestFactoryGenerator.java
Thu Jun 24 14:48:00 2010
+++
/trunk/user/src/com/google/gwt/requestfactory/rebind/RequestFactoryGenerator.java
Fri Jul 23 10:49:57 2010
@@ -257,13 +257,15 @@
}
requestSelectors.add(asInterface);
}
+
// write a method for each sub-interface
for (JClassType requestSelector : requestSelectors) {
+ String qualifiedSourceName =
requestSelector.getQualifiedSourceName();
String simpleSourceName = requestSelector.getSimpleSourceName();
- sw.println("public " + simpleSourceName + " "
+ sw.println("public " + qualifiedSourceName + " "
+ getMethodName(simpleSourceName) + "() {");
sw.indent();
- sw.println("return new " + simpleSourceName + "Impl(this);");
+ sw.println("return new " + qualifiedSourceName + "Impl(this);");
sw.outdent();
sw.println("}");
sw.println();
@@ -285,11 +287,12 @@
for (JClassType nestedInterface : requestSelectors) {
String nestedImplName = nestedInterface.getName() + "Impl";
- PrintWriter pw = generatorContext.tryCreate(logger, packageName,
+ String nestedImplPackage = nestedInterface.getPackage().getName();
+ PrintWriter pw = generatorContext.tryCreate(logger,
nestedImplPackage,
nestedImplName);
if (pw != null) {
generateRequestSelectorImplementation(logger, generatorContext, pw,
- nestedInterface, interfaceType, packageName, nestedImplName);
+ nestedInterface, interfaceType, nestedImplPackage,
nestedImplName);
}
}
@@ -325,10 +328,10 @@
sw.println("public RecordSchema<? extends Record> getType(Class token)
{");
sw.indent();
for (JClassType publicRecordType : generatedRecordTypes) {
-
- sw.println("if (token == " + publicRecordType.getName() + ".class)
{");
+ String qualifiedSourceName =
publicRecordType.getQualifiedSourceName();
+ sw.println("if (token == " + qualifiedSourceName + ".class) {");
sw.indent();
- sw.println("return " + publicRecordType.getName() + "Impl.SCHEMA;");
+ sw.println("return " + qualifiedSourceName + "Impl.SCHEMA;");
sw.outdent();
sw.println("}");
}
@@ -357,7 +360,7 @@
f.addImport(ClientRequestHelper.class.getName());
f.addImport(RequestDataManager.class.getName());
- f.addImplementedInterface(interfaceType.getName());
+ f.addImplementedInterface(interfaceType.getQualifiedSourceName());
SourceWriter sw = f.createSourceWriter(generatorContext, out);
sw.println();
=======================================
---
/trunk/user/src/com/google/gwt/requestfactory/server/RequestFactoryServlet.java
Thu Jun 24 14:48:00 2010
+++
/trunk/user/src/com/google/gwt/requestfactory/server/RequestFactoryServlet.java
Fri Jul 23 10:49:57 2010
@@ -58,17 +58,21 @@
* development, and is very likely to be deleted. Use it at your own risk.
* </span>
* </p>
- * Handles GWT RequestFactory JSON requests. Configured via servlet context
- * param <code>servlet.serverOperation</code>, which must be set to the
name of
- * a default instantiable class implementing
- * com.google.gwt.requestfactory.shared.RequestFactory.Config.
+ * 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. If authentication fails, 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.
+ * e.g. - in order to use GAE authentication:
*
- * <pre> <context-param>
- <param-name>servlet.serverOperation</param-name>
-
<param-value>com.myco.myapp.MyAppServerSideOperations</param-value>
- </context-param>
+ * <pre> <init-param>
+ <param-name>userInfoClass</param-name>
+
<param-value>com.google.gwt.sample.expenses.server.domain.GaeUserInformation</param-value>
+ </init-param>
* </pre>
*/
@@ -98,7 +102,8 @@
}
return Collections.unmodifiableSet(blackList);
}
-
+
+ private UserInformationImpl userInfo;
private OperationRegistry operationRegistry;
@SuppressWarnings("unchecked")
@@ -110,48 +115,55 @@
RequestDefinition operation = null;
try {
- response.setStatus(HttpServletResponse.SC_OK);
- PrintWriter writer = response.getWriter();
- JSONObject topLevelJsonObject = new JSONObject(getContent(request));
- String operationName =
topLevelJsonObject.getString(RequestDataManager.OPERATION_TOKEN);
- if (operationName.equals(RequestFactory.SYNC)) {
-
sync(topLevelJsonObject.getString(RequestDataManager.CONTENT_TOKEN),
- writer);
+ // Check that user is logged in before proceeding
+ if (!userInfo.isUserLoggedIn()) {
+ response.setHeader("login", userInfo.getLoginUrl());
+ response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
} else {
- operation = getOperation(operationName);
- Class<?> domainClass =
Class.forName(operation.getDomainClassName());
- Method domainMethod = domainClass.getMethod(
- operation.getDomainMethodName(),
operation.getParameterTypes());
- if (!Modifier.isStatic(domainMethod.getModifiers())) {
- throw new IllegalArgumentException("the " +
domainMethod.getName()
- + " is not static");
- }
- Object args[] = RequestDataManager.getObjectsFromParameterMap(
- getParameterMap(topLevelJsonObject),
- domainMethod.getParameterTypes());
- Object result = invokeStaticDomainMethod(domainMethod, args);
-
- if ((result instanceof List<?>) != operation.isReturnTypeList()) {
- throw new IllegalArgumentException(String.format(
- "Type mismatch, expected %s%s, but %s returns %s",
- operation.isReturnTypeList() ? "list of " : "",
- operation.getReturnType(), domainMethod,
- domainMethod.getReturnType()));
- }
-
- if (result instanceof List<?>) {
- JSONArray jsonArray = getJsonArray((List<?>) result,
- (Class<? extends Record>) operation.getReturnType());
- writer.print(jsonArray.toString());
- } else if (result instanceof Number) {
- writer.print(result.toString());
- } else {
- JSONObject jsonObject = getJsonObject(result,
- (Class<? extends Record>) operation.getReturnType());
- writer.print("(" + jsonObject.toString() + ")");
- }
- }
- writer.flush();
+ response.setHeader("userId", String.format("%d",
userInfo.getId()));
+ response.setStatus(HttpServletResponse.SC_OK);
+ PrintWriter writer = response.getWriter();
+ JSONObject topLevelJsonObject = new
JSONObject(getContent(request));
+ String operationName =
topLevelJsonObject.getString(RequestDataManager.OPERATION_TOKEN);
+ if (operationName.equals(RequestFactory.SYNC)) {
+
sync(topLevelJsonObject.getString(RequestDataManager.CONTENT_TOKEN),
+ writer);
+ } else {
+ operation = getOperation(operationName);
+ Class<?> domainClass =
Class.forName(operation.getDomainClassName());
+ Method domainMethod = domainClass.getMethod(
+ operation.getDomainMethodName(),
operation.getParameterTypes());
+ if (!Modifier.isStatic(domainMethod.getModifiers())) {
+ throw new IllegalArgumentException("the " +
domainMethod.getName()
+ + " is not static");
+ }
+ Object args[] = RequestDataManager.getObjectsFromParameterMap(
+ getParameterMap(topLevelJsonObject),
+ domainMethod.getParameterTypes());
+ Object result = invokeStaticDomainMethod(domainMethod, args);
+
+ if ((result instanceof List<?>) != operation.isReturnTypeList())
{
+ throw new IllegalArgumentException(String.format(
+ "Type mismatch, expected %s%s, but %s returns %s",
+ operation.isReturnTypeList() ? "list of " : "",
+ operation.getReturnType(), domainMethod,
+ domainMethod.getReturnType()));
+ }
+
+ if (result instanceof List<?>) {
+ JSONArray jsonArray = getJsonArray((List<?>) result,
+ (Class<? extends Record>) operation.getReturnType());
+ writer.print(jsonArray.toString());
+ } else if (result instanceof Number) {
+ writer.print(result.toString());
+ } else {
+ JSONObject jsonObject = getJsonObject(result,
+ (Class<? extends Record>) operation.getReturnType());
+ writer.print("(" + jsonObject.toString() + ")");
+ }
+ }
+ writer.flush();
+ }
// TODO: clean exception handling code below.
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException(e);
@@ -275,6 +287,22 @@
private void ensureConfig() {
operationRegistry = new ReflectionBasedOperationRegistry(
new DefaultSecurityProvider());
+
+ // 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) {
+ try {
+ userInfo = (UserInformationImpl)
Class.forName(userInfoClass).newInstance();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ if (userInfo == null) {
+ userInfo = new UserInformationImpl();
+ }
}
private String getContent(HttpServletRequest request) throws IOException
{
=======================================
--- /trunk/user/src/com/google/gwt/requestfactory/shared/RequestEvent.java
Thu Jun 24 14:48:00 2010
+++ /trunk/user/src/com/google/gwt/requestfactory/shared/RequestEvent.java
Fri Jul 23 10:49:57 2010
@@ -17,6 +17,7 @@
import com.google.gwt.event.shared.EventHandler;
import com.google.gwt.event.shared.GwtEvent;
+import com.google.gwt.http.client.Response;
/**
* <p>
@@ -44,9 +45,14 @@
public static final Type<Handler> TYPE = new Type<Handler>();
private final State state;
-
- public RequestEvent(State state) {
+
+ // Will only be non-null if this is an event of type RECIEVED, and the
+ // RPC was successful
+ private final Response response;
+
+ public RequestEvent(State state, Response response) {
this.state = state;
+ this.response = response;
}
@Override
@@ -54,6 +60,10 @@
return TYPE;
}
+ public Response getResponse() {
+ return response;
+ }
+
public State getState() {
return state;
}
--
http://groups.google.com/group/Google-Web-Toolkit-Contributors