Revision: 9254
Author: [email protected]
Date: Thu Nov 18 23:14:02 2010
Log: Fixes issue
http://code.google.com/p/google-web-toolkit/issues/detail?id=5578
Restores the domain class upcasting behavior of RequestFactoryServlet,
which is relied upon by our UserInformation class.
Also fixes the fact that UserInformation never provided a finder
method, which we're now less forgiving of. Really, though,
UserInformation should be a value object, not a proxy at all. It acts
the way it does to hack around our lack of such things.
Also introduces unit tests to ensure that UserInformation and
LoggingService keep working.
Review at http://gwt-code-reviews.appspot.com/1098801
Review by: [email protected]
http://code.google.com/p/google-web-toolkit/source/detail?r=9254
Modified:
/trunk/user/src/com/google/gwt/requestfactory/server/Logging.java
/trunk/user/src/com/google/gwt/requestfactory/server/RequestFactoryInterfaceValidator.java
/trunk/user/src/com/google/gwt/requestfactory/server/UserInformation.java
/trunk/user/src/com/google/gwt/requestfactory/shared/LoggingRequest.java
/trunk/user/test/com/google/gwt/requestfactory/client/RequestFactoryTest.java
/trunk/user/test/com/google/gwt/requestfactory/server/SimpleFoo.java
/trunk/user/test/com/google/gwt/requestfactory/shared/SimpleFooRequest.java
/trunk/user/test/com/google/gwt/requestfactory/shared/SimpleRequestFactory.java
=======================================
--- /trunk/user/src/com/google/gwt/requestfactory/server/Logging.java Fri
Nov 5 04:25:19 2010
+++ /trunk/user/src/com/google/gwt/requestfactory/server/Logging.java Thu
Nov 18 23:14:02 2010
@@ -21,6 +21,8 @@
import com.google.gwt.logging.server.StackTraceDeobfuscator;
import com.google.gwt.user.client.rpc.RpcRequestBuilder;
+import javax.servlet.http.HttpServletRequest;
+
/**
* Server side object that handles log messages sent by
* {...@link com.google.gwt.requestfactory.client.RequestFactoryLogHandler}.
@@ -33,15 +35,22 @@
/**
* Logs a message.
*
- * @param logRecordJson a log record in JSON format.
+ * @param serializedLogRecordString a json serialized LogRecord, as
provided by
+ * {...@link
com.google.gwt.logging.client.JsonLogRecordClientUtil.logRecordAsJsonObject(LogRecord)}
* @throws RemoteLoggingException if logging fails
*/
public static void logMessage(String logRecordJson)
throws RemoteLoggingException {
- // if the header does not exist, we pass null, which is handled
gracefully
- // by the deobfuscation code.
- String strongName =
RequestFactoryServlet.getThreadLocalRequest().getHeader(
- RpcRequestBuilder.STRONG_NAME_HEADER);
+ /*
+ * if the header does not exist, we pass null, which is handled
gracefully
+ * by the deobfuscation code.
+ */
+ HttpServletRequest threadLocalRequest =
RequestFactoryServlet.getThreadLocalRequest();
+ String strongName = null;
+ if (threadLocalRequest != null) {
+ // can be null during tests
+ threadLocalRequest.getHeader(RpcRequestBuilder.STRONG_NAME_HEADER);
+ }
RemoteLoggingServiceUtil.logOnServer(logRecordJson, strongName,
deobfuscator, null);
}
=======================================
---
/trunk/user/src/com/google/gwt/requestfactory/server/RequestFactoryInterfaceValidator.java
Thu Nov 18 13:15:58 2010
+++
/trunk/user/src/com/google/gwt/requestfactory/server/RequestFactoryInterfaceValidator.java
Thu Nov 18 23:14:02 2010
@@ -326,7 +326,7 @@
if (!seen.add(name)) {
return;
}
- if (!"java/lang/Object".equals(superName)) {
+ if (!objectType.getInternalName().equals(superName)) {
RequestFactoryInterfaceValidator.this.visit(logger, superName,
this);
}
if (interfaces != null) {
@@ -384,7 +384,7 @@
private class SupertypeCollector extends EmptyVisitor {
private final ErrorContext logger;
private final Set<String> seen = new HashSet<String>();
- private final List<Type> supertypes = new ArrayList<Type>();
+ private final List<Type> supers = new ArrayList<Type>();
public SupertypeCollector(ErrorContext logger) {
this.logger = logger;
@@ -393,7 +393,7 @@
public List<Type> exec(Type type) {
RequestFactoryInterfaceValidator.this.visit(logger,
type.getInternalName(), this);
- return supertypes;
+ return supers;
}
@Override
@@ -402,8 +402,8 @@
if (!seen.add(name)) {
return;
}
- supertypes.add(Type.getObjectType(name));
- if (!"java/lang/Object".equals(name)) {
+ supers.add(Type.getObjectType(name));
+ if (!objectType.getInternalName().equals(name)) {
RequestFactoryInterfaceValidator.this.visit(logger, superName,
this);
}
if (interfaces != null) {
@@ -787,22 +787,46 @@
String clientTypeBinaryName) {
Type key =
Type.getObjectType(BinaryName.toInternalName(domainTypeBinaryName));
List<Type> found = domainToClientType.get(key);
+
+ /*
+ * If nothing was found look for proxyable supertypes the domain
object can
+ * be upcast to.
+ */
+ if (found == null || found.isEmpty()) {
+ List<Type> types = getSupertypes(parentLogger, key);
+ for (Type type : types) {
+ if (objectType.equals(type)) {
+ break;
+ }
+
+ found = domainToClientType.get(type);
+ if (found != null && !found.isEmpty()) {
+ break;
+ }
+ }
+ }
+
if (found == null || found.isEmpty()) {
return null;
}
+
+ Type typeToReturn = null;
+
// Common case
if (found.size() == 1) {
- return found.get(0).getClassName();
- }
-
- // Search for the first assignable type
- Type assignableTo =
Type.getObjectType(BinaryName.toInternalName(clientTypeBinaryName));
- for (Type t : found) {
- if (isAssignable(parentLogger, assignableTo, t)) {
- return t.getClassName();
+ typeToReturn = found.get(0);
+ } else {
+ // Search for the first assignable type
+ Type assignableTo =
Type.getObjectType(BinaryName.toInternalName(clientTypeBinaryName));
+ for (Type t : found) {
+ if (isAssignable(parentLogger, assignableTo, t)) {
+ typeToReturn = t;
+ break;
+ }
}
}
- return null;
+
+ return typeToReturn == null ? null : typeToReturn.getClassName();
}
/**
@@ -1099,7 +1123,7 @@
*/
private Type getReturnType(ErrorContext logger, RFMethod method) {
logger = logger.setMethod(method);
- final String[] returnType = {"java/lang/Object"};
+ final String[] returnType = { objectType.getInternalName() };
String signature = method.getSignature();
final int expectedCount;
@@ -1147,7 +1171,9 @@
if (toReturn != null) {
return toReturn;
}
+
logger = logger.setType(type);
+
toReturn = new SupertypeCollector(logger).exec(type);
supertypes.put(type, Collections.unmodifiableList(toReturn));
return toReturn;
=======================================
---
/trunk/user/src/com/google/gwt/requestfactory/server/UserInformation.java
Fri Nov 5 04:25:19 2010
+++
/trunk/user/src/com/google/gwt/requestfactory/server/UserInformation.java
Thu Nov 18 23:14:02 2010
@@ -18,36 +18,63 @@
/**
* A base class for providing authentication related information about the
user.
- * Services that want real authentication should subclass this class.
+ * 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 = "";
/**
- * Returns the current user information for a given redirect URL. 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.
+ * 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) {
- UserInformation userInfo = null;
+ currentUser.remove();
if (!"".equals(userInformationImplClass)) {
try {
- userInfo = (UserInformation)
Class.forName(userInformationImplClass).getConstructor(
- String.class).newInstance(redirectUrl);
+ currentUser.set((UserInformation) Class.forName(
+
userInformationImplClass).getConstructor(String.class).newInstance(
+ redirectUrl));
} catch (Exception e) {
e.printStackTrace();
}
}
- if (userInfo == null) {
- userInfo = new UserInformationSimpleImpl(redirectUrl);
- }
- return userInfo;
+ if (currentUser.get() == null) {
+ currentUser.set(new UserInformationSimpleImpl(redirectUrl));
+ }
+ return currentUser.get();
}
/**
=======================================
---
/trunk/user/src/com/google/gwt/requestfactory/shared/LoggingRequest.java
Thu Oct 14 18:28:29 2010
+++
/trunk/user/src/com/google/gwt/requestfactory/shared/LoggingRequest.java
Thu Nov 18 23:14:02 2010
@@ -30,7 +30,8 @@
/**
* Log a message on the server.
*
- * @param serializedLogRecordString a String
+ * @param serializedLogRecordString a json serialized LogRecord, as
provided by
+ * {...@link
com.google.gwt.logging.client.JsonLogRecordClientUtil.logRecordAsJsonObject(LogRecord)}
* @return a Void {...@link Request}
*/
Request<Void> logMessage(String serializedLogRecordString);
=======================================
---
/trunk/user/test/com/google/gwt/requestfactory/client/RequestFactoryTest.java
Thu Nov 18 13:15:58 2010
+++
/trunk/user/test/com/google/gwt/requestfactory/client/RequestFactoryTest.java
Thu Nov 18 23:14:02 2010
@@ -28,6 +28,7 @@
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;
@@ -381,6 +382,18 @@
}
});
}
+
+ public void testDomainUpcast() {
+ delayTestFinish(DELAY_TEST_FINISH);
+ simpleFooRequest().returnSimpleFooSubclass().fire(
+ new Receiver<SimpleFooProxy>() {
+ @Override
+ public void onSuccess(SimpleFooProxy response) {
+ assertEquals(42, response.getIntId().intValue());
+ finishTestAndReset();
+ }
+ });
+ }
public void testDummyCreate() {
delayTestFinish(DELAY_TEST_FINISH);
@@ -689,6 +702,26 @@
}
});
}
+
+ /**
+ * Make sure our stock RF logging service keeps receiving.
+ */
+ public void testLoggingService() {
+ String logRecordJson = new StringBuilder("{").append("\"level\":
\"ALL\", ")
+ .append("\"loggerName\": \"logger\", ")
+ .append("\"msg\": \"Hi mom\", ")
+ .append("\"timestamp\": \"1234567890\",")
+ .append("\"thrown\": {}")
+ .append("}")
+ .toString();
+
+ req.loggingRequest().logMessage(logRecordJson).fire(new
Receiver<Void>() {
+ @Override
+ public void onSuccess(Void response) {
+ finishTestAndReset();
+ }
+ });
+ }
/*
* tests that (a) any method can have a side effect that is handled
correctly.
@@ -1919,6 +1952,25 @@
}
});
}
+
+ /**
+ * 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() {
+ req.userInformationRequest().getCurrentUserInformation("").fire(
+ new Receiver<UserInformationProxy>() {
+ @Override
+ public void onSuccess(UserInformationProxy getResponse) {
+ assertEquals("Dummy Email", getResponse.getEmail());
+ assertEquals("Dummy User", getResponse.getName());
+ assertEquals("", getResponse.getLoginUrl());
+ assertEquals("", getResponse.getLogoutUrl());
+ }
+ });
+ }
/**
* Check if a graph of unpersisted objects can be echoed.
=======================================
--- /trunk/user/test/com/google/gwt/requestfactory/server/SimpleFoo.java
Thu Nov 18 13:15:58 2010
+++ /trunk/user/test/com/google/gwt/requestfactory/server/SimpleFoo.java
Thu Nov 18 23:14:02 2010
@@ -278,6 +278,11 @@
public static String returnNullString() {
return null;
}
+
+ public static SimpleFoo returnSimpleFooSubclass() {
+ return new SimpleFoo() {
+ };
+ }
public static SimpleValue returnValueProxy() {
SimpleValue toReturn = new SimpleValue();
=======================================
---
/trunk/user/test/com/google/gwt/requestfactory/shared/SimpleFooRequest.java
Thu Nov 18 13:15:58 2010
+++
/trunk/user/test/com/google/gwt/requestfactory/shared/SimpleFooRequest.java
Thu Nov 18 23:14:02 2010
@@ -94,6 +94,8 @@
Request<String> returnNullString();
+ Request<SimpleFooProxy> returnSimpleFooSubclass();
+
Request<SimpleValueProxy> returnValueProxy();
InstanceRequest<SimpleFooProxy, Integer> sum(List<Integer> values);
=======================================
---
/trunk/user/test/com/google/gwt/requestfactory/shared/SimpleRequestFactory.java
Fri Nov 5 04:25:19 2010
+++
/trunk/user/test/com/google/gwt/requestfactory/shared/SimpleRequestFactory.java
Thu Nov 18 23:14:02 2010
@@ -16,11 +16,16 @@
package com.google.gwt.requestfactory.shared;
/**
- * Creates SimpleFooRequests.
+ * Simple RequesetFactory interface with two domain objects, and our
standard
+ * UserInformation and Logging services.
*/
public interface SimpleRequestFactory extends RequestFactory {
- SimpleFooRequest simpleFooRequest();
+ LoggingRequest loggingRequest();
SimpleBarRequest simpleBarRequest();
-}
+
+ SimpleFooRequest simpleFooRequest();
+
+ UserInformationRequest userInformationRequest();
+}
--
http://groups.google.com/group/Google-Web-Toolkit-Contributors