Fixes issue 1138.  The root cause was that the RPC class and the ServerSerializationStreamReader used their respective this.class.getClassLoader() to get a classloader to resolve types during deserialization.  This worked well as long as the GWT code was in the same classloader as the application code.  In scenarios where the GWT code was moved into a shared classloader, like tomcat's shared classloader, we would fail to resolve classes.

This patch uses a solution based on context classloaders for the RPC class.  The reasons for the decision are as follows:

   1. Context classloaders are the mechanism used by JAXP, and JNDI.
   2. Context classloaders will be set correctly in servlet containers, which is our primary target environment. 
   3. Having worked the patch using context classloaders and explicitly passing in the classloader, the majority of the time the classloader that you would pass in to the RPC class would be the servlet impl's classloader which would be the contextclassloader or at least be accessible from the context classloader.  For most users,they will not need to worry about which classloader to use.
   4. There are workarounds for OSGi and similar environments which can ensure that the context classloader gets set correctly.

This patch was tested using a Tomcat instance with gwt-servlet.jar in the shared classloader.

Review by: scottb



git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@1232 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/user/server/rpc/RPC.java b/user/src/com/google/gwt/user/server/rpc/RPC.java
index f575400..f38d615 100644
--- a/user/src/com/google/gwt/user/server/rpc/RPC.java
+++ b/user/src/com/google/gwt/user/server/rpc/RPC.java
@@ -114,8 +114,10 @@
    *           conditions apply:
    *           <ul>
    *           <li>if the types in the encoded request cannot be deserialized</li>
-   *           <li><code>RPC.class.getClassLoader()</code> cannot load the
-   *           service interface requested in the encodedRequest</li>
+   *           <li>if the {@link ClassLoader} acquired from
+   *           <code>Thread.currentThread().getContextClassLoader()</code>
+   *           cannot load the service interface or any of the types specified
+   *           in the encodedRequest</li>
    *           <li>the requested interface is not assignable to
    *           {@link RemoteService}</li>
    *           <li>the service method requested in the encodedRequest is not a
@@ -155,8 +157,10 @@
    *           conditions apply:
    *           <ul>
    *           <li>if the types in the encoded request cannot be deserialized</li>
-   *           <li><code>RPC.class.getClassLoader()</code> cannot load the
-   *           service interface requested in the encodedRequest</li>
+   *           <li>if the {@link ClassLoader} acquired from
+   *           <code>Thread.currentThread().getContextClassLoader()</code>
+   *           cannot load the service interface or any of the types specified
+   *           in the encodedRequest</li>
    *           <li>the requested interface is not assignable to
    *           {@link RemoteService}</li>
    *           <li>the service method requested in the encodedRequest is not a
@@ -174,9 +178,11 @@
       throw new IllegalArgumentException("encodedRequest cannot be empty");
     }
 
+    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+
     try {
       ServerSerializationStreamReader streamReader = new ServerSerializationStreamReader(
-          serializableTypeOracle);
+          serializableTypeOracle, classLoader);
       streamReader.prepareToRead(encodedRequest);
 
       String serviceIntfName = streamReader.readString();
@@ -193,7 +199,7 @@
 
       Class serviceIntf;
       try {
-        serviceIntf = getClassFromSerializedName(serviceIntfName);
+        serviceIntf = getClassFromSerializedName(serviceIntfName, classLoader);
         if (!RemoteService.class.isAssignableFrom(serviceIntf)) {
           // The requested interface is not a RemoteService interface
           throw new IncompatibleRemoteServiceException(
@@ -215,7 +221,8 @@
       for (int i = 0; i < parameterTypes.length; i++) {
         String paramClassName = streamReader.readString();
         try {
-          parameterTypes[i] = getClassFromSerializedName(paramClassName);
+          parameterTypes[i] = getClassFromSerializedName(paramClassName,
+              classLoader);
         } catch (ClassNotFoundException e) {
           throw new IncompatibleRemoteServiceException("Parameter " + i
               + " of is of an unknown type '" + paramClassName + "'", e);
@@ -485,17 +492,18 @@
    * Returns the {@link Class} instance for the named class or primitive type.
    * 
    * @param serializedName the serialized name of a class or primitive type
+   * @param classLoader the classLoader used to load {@link Class}es
    * @return Class instance for the given type name
    * @throws ClassNotFoundException if the named type was not found
    */
-  private static Class getClassFromSerializedName(String serializedName)
-      throws ClassNotFoundException {
+  private static Class getClassFromSerializedName(String serializedName,
+      ClassLoader classLoader) throws ClassNotFoundException {
     Object value = TYPE_NAMES.get(serializedName);
     if (value != null) {
       return (Class) value;
     }
 
-    return Class.forName(serializedName, false, RPC.class.getClassLoader());
+    return Class.forName(serializedName, false, classLoader);
   }
 
   /**
diff --git a/user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamReader.java b/user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamReader.java
index 4339697..1c1b9e8 100644
--- a/user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamReader.java
+++ b/user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamReader.java
@@ -33,6 +33,8 @@
 public final class ServerSerializationStreamReader extends
     AbstractSerializationStreamReader {
 
+  private final ClassLoader classLoader;
+
   private ServerSerializableTypeOracle serializableTypeOracle;
 
   private String[] stringTable;
@@ -42,7 +44,9 @@
   private int tokenListIndex;
 
   public ServerSerializationStreamReader(
-      ServerSerializableTypeOracle serializableTypeOracle) {
+      ServerSerializableTypeOracle serializableTypeOracle,
+      ClassLoader classLoader) {
+    this.classLoader = classLoader;
     this.serializableTypeOracle = serializableTypeOracle;
   }
 
@@ -133,7 +137,7 @@
 
     try {
       Class instanceClass = Class.forName(serializedInstRef.getName(), false,
-          this.getClass().getClassLoader());
+          classLoader);
 
       if (!serializableTypeOracle.isSerializable(instanceClass)) {
         throw new SerializationException("Class '" + instanceClass.getName()