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>  &lt;context-param>
-    &lt;param-name>servlet.serverOperation&lt;/param-name>
- &lt;param-value>com.myco.myapp.MyAppServerSideOperations&lt;/param-value>
-  &lt;/context-param>
+ * <pre>  &lt;init-param>
+    &lt;param-name>userInfoClass&lt;/param-name>
+ &lt;param-value>com.google.gwt.sample.expenses.server.domain.GaeUserInformation&lt;/param-value>
+  &lt;/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

Reply via email to