Adds pre-deserialization type checking for values in GWT RPC messages.
The server will now avoid deserializing types that mismatch the RPC
method that is being invoked, reducing the chance that an erroneous
message will cause uncaught deserialization errors on the server.


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@10518 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/build-tools/doctool/src/com/google/doctool/custom/FindPackages.java b/build-tools/doctool/src/com/google/doctool/custom/FindPackages.java
index 4014a43..03fecce 100644
--- a/build-tools/doctool/src/com/google/doctool/custom/FindPackages.java
+++ b/build-tools/doctool/src/com/google/doctool/custom/FindPackages.java
@@ -43,7 +43,8 @@
       "^com.google.gwt.resources.rg(.|$)",
       "^com.google.gwt.rpc.client.ast(.|$)", "^com.google.gwt.soyc(.|$)",
       "^com.google.gwt.validation(.|$)",
-      "^com.google.gwt.user.client.rpc.core.", "^javax.", "^junit.", "^org.",
+      "^com.google.gwt.user.\\w+.rpc.core.",
+      "^javax.", "^junit.", "^org.",
       ".impl(.|$)", ".rebind(.|$)"
   };
 
diff --git a/user/src/com/google/gwt/user/client/rpc/SerializedTypeViolationException.java b/user/src/com/google/gwt/user/client/rpc/SerializedTypeViolationException.java
new file mode 100644
index 0000000..f201cb4
--- /dev/null
+++ b/user/src/com/google/gwt/user/client/rpc/SerializedTypeViolationException.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2011 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.user.client.rpc;
+
+/**
+ * Exception that will be passed to the
+ * {@link AsyncCallback#onFailure(Throwable)} method when the value of an
+ * argument to a method in an RPC message is of the incorrect type.
+ * 
+ * <p>
+ * For example, a method may be expecting an Integer argument, while the value
+ * in the message is a HashMap. The most likely source of this message in a
+ * production system is a security attack where a man-in-the-middle has modified
+ * an RPC message by changing the value types within the message.
+ * </p>
+ * 
+ * <p>
+ * Note that on the client, the {@link #getCause()} always return
+ * <code>null</code>.
+ * </p>
+ */
+public class SerializedTypeViolationException extends SerializationException
+    implements IsSerializable {
+
+  /**
+   * Constructor used by RPC serialization. Note that the client side code will
+   * always get a generic error message.
+   */
+  public SerializedTypeViolationException() {
+    super();
+  }
+
+  /**
+   * Constructs an instance with the specified message.
+   */
+  public SerializedTypeViolationException(String msg) {
+    super(msg);
+  }
+
+  /**
+   * Constructs an instance with the specified message and cause.
+   */
+  public SerializedTypeViolationException(String msg, Throwable cause) {
+    super(msg, cause);
+  }
+}
diff --git a/user/src/com/google/gwt/user/client/rpc/core/java/util/Collection_CustomFieldSerializerBase.java b/user/src/com/google/gwt/user/client/rpc/core/java/util/Collection_CustomFieldSerializerBase.java
index c0006ad..fc851f6 100644
--- a/user/src/com/google/gwt/user/client/rpc/core/java/util/Collection_CustomFieldSerializerBase.java
+++ b/user/src/com/google/gwt/user/client/rpc/core/java/util/Collection_CustomFieldSerializerBase.java
@@ -22,7 +22,7 @@
 import java.util.Collection;
 
 /**
- * Custom field serializer for {@link java.util.ArrayList}.
+ * Custom field serializer for {@link java.util.Collection}, used by implementing classes.
  */
 @SuppressWarnings("unchecked")
 public final class Collection_CustomFieldSerializerBase {
diff --git a/user/src/com/google/gwt/user/client/rpc/impl/AbstractSerializationStreamReader.java b/user/src/com/google/gwt/user/client/rpc/impl/AbstractSerializationStreamReader.java
index 3766b5d..98a32f9 100644
--- a/user/src/com/google/gwt/user/client/rpc/impl/AbstractSerializationStreamReader.java
+++ b/user/src/com/google/gwt/user/client/rpc/impl/AbstractSerializationStreamReader.java
@@ -130,6 +130,18 @@
       throws SerializationException;
 
   /**
+   * Get the previously seen object at the given index which must be 1-based.
+   * 
+   * @param index a 1-based index into the seen objects
+   * 
+   * @return the object stored in the seen array at index - 1
+   */
+  protected final Object getDecodedObject(int index) {
+    // index is 1-based
+    return seenArray.get(index - 1);
+  }
+
+  /**
    * Gets a string out of the string table.
    * 
    * @param index the index of the string to get
@@ -137,12 +149,23 @@
    */
   protected abstract String getString(int index);
 
-  protected final void rememberDecodedObject(int index, Object o) {
+  /**
+   * Set an object in the seen list.
+   * 
+   * @param index a 1-based index into the seen objects
+   * @param o the object to remember
+   */
 
+  protected final void rememberDecodedObject(int index, Object o) {
     // index is 1-based
     seenArray.set(index - 1, o);
   }
 
+  /**
+   * Reserve an entry for an object in the seen list.
+   * 
+   * @return the index to be used in future for the object
+   */
   protected final int reserveDecodedObjectIndex() {
     seenArray.add(null);
 
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 e0a292b..5a9ff5f 100644
--- a/user/src/com/google/gwt/user/server/rpc/RPC.java
+++ b/user/src/com/google/gwt/user/server/rpc/RPC.java
@@ -20,13 +20,17 @@
 import com.google.gwt.user.client.rpc.RpcToken;
 import com.google.gwt.user.client.rpc.SerializationException;
 import com.google.gwt.user.client.rpc.impl.AbstractSerializationStream;
+import com.google.gwt.user.server.rpc.impl.DequeMap;
 import com.google.gwt.user.server.rpc.impl.LegacySerializationPolicy;
+import com.google.gwt.user.server.rpc.impl.SerializabilityUtil;
 import com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader;
 import com.google.gwt.user.server.rpc.impl.ServerSerializationStreamWriter;
 import com.google.gwt.user.server.rpc.impl.TypeNameObfuscator;
 
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -43,20 +47,20 @@
  * <h3>Canonical Example</h3> The following example demonstrates the canonical
  * way to use this class.
  * 
- * {@example
- * com.google.gwt.examples.rpc.server.CanonicalExample#processCall(String)}
+ * {@example com.google.gwt.examples.rpc.server.CanonicalExample#processCall(String)}
  * 
  * <h3>Advanced Example</h3> The following example shows a more advanced way of
  * using this class to create an adapter between GWT RPC entities and POJOs.
  * 
- * {@example com.google.gwt.examples.rpc.server.AdvancedExample#doPost(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)}
+ * {@example com.google.gwt.examples.rpc.server.AdvancedExample#doPost}
  */
 public final class RPC {
 
   /**
    * Maps primitive wrapper classes to their corresponding primitive class.
    */
-  private static final Map<Class<?>, Class<?>> PRIMITIVE_WRAPPER_CLASS_TO_PRIMITIVE_CLASS = new HashMap<Class<?>, Class<?>>();
+  private static final Map<Class<?>, Class<?>> PRIMITIVE_WRAPPER_CLASS_TO_PRIMITIVE_CLASS =
+      new HashMap<Class<?>, Class<?>>();
 
   /**
    * Static map of classes to sets of interfaces (e.g. classes). Optimizes
@@ -69,8 +73,7 @@
   static {
     PRIMITIVE_WRAPPER_CLASS_TO_PRIMITIVE_CLASS.put(Boolean.class, Boolean.TYPE);
     PRIMITIVE_WRAPPER_CLASS_TO_PRIMITIVE_CLASS.put(Byte.class, Byte.TYPE);
-    PRIMITIVE_WRAPPER_CLASS_TO_PRIMITIVE_CLASS.put(Character.class,
-        Character.TYPE);
+    PRIMITIVE_WRAPPER_CLASS_TO_PRIMITIVE_CLASS.put(Character.class, Character.TYPE);
     PRIMITIVE_WRAPPER_CLASS_TO_PRIMITIVE_CLASS.put(Double.class, Double.TYPE);
     PRIMITIVE_WRAPPER_CLASS_TO_PRIMITIVE_CLASS.put(Float.class, Float.TYPE);
     PRIMITIVE_WRAPPER_CLASS_TO_PRIMITIVE_CLASS.put(Integer.class, Integer.TYPE);
@@ -232,8 +235,8 @@
     ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
 
     try {
-      ServerSerializationStreamReader streamReader = new ServerSerializationStreamReader(
-          classLoader, serializationPolicyProvider);
+      ServerSerializationStreamReader streamReader =
+          new ServerSerializationStreamReader(classLoader, serializationPolicyProvider);
       streamReader.prepareToRead(encodedRequest);
 
       RpcToken rpcToken = null;
@@ -241,18 +244,16 @@
         // Read the RPC token
         rpcToken = (RpcToken) streamReader.deserializeValue(RpcToken.class);
       }
-            
+
       // Read the name of the RemoteService interface
-      String serviceIntfName = maybeDeobfuscate(streamReader,
-          streamReader.readString());
+      String serviceIntfName = maybeDeobfuscate(streamReader, streamReader.readString());
 
       if (type != null) {
         if (!implementsInterface(type, serviceIntfName)) {
           // The service does not implement the requested interface
-          throw new IncompatibleRemoteServiceException(
-              "Blocked attempt to access interface '" + serviceIntfName
-                  + "', which is not implemented by '" + printTypeName(type)
-                  + "'; this is either misconfiguration or a hack attempt");
+          throw new IncompatibleRemoteServiceException("Blocked attempt to access interface '"
+              + serviceIntfName + "', which is not implemented by '" + printTypeName(type)
+              + "'; this is either misconfiguration or a hack attempt");
         }
       }
 
@@ -265,30 +266,27 @@
           throw new IncompatibleRemoteServiceException(
               "Blocked attempt to access interface '"
                   + printTypeName(serviceIntf)
-                  + "', which doesn't extend RemoteService; this is either misconfiguration or a hack attempt");
+                  + "', which doesn't extend RemoteService; this is either "
+                  + "misconfiguration or a hack attempt");
         }
       } catch (ClassNotFoundException e) {
-        throw new IncompatibleRemoteServiceException(
-            "Could not locate requested interface '" + serviceIntfName
-                + "' in default classloader", e);
+        throw new IncompatibleRemoteServiceException("Could not locate requested interface '"
+            + serviceIntfName + "' in default classloader", e);
       }
 
       String serviceMethodName = streamReader.readString();
 
       int paramCount = streamReader.readInt();
       if (paramCount > streamReader.getNumberOfTokens()) {
-        throw new IncompatibleRemoteServiceException(
-            "Invalid number of parameters");
+        throw new IncompatibleRemoteServiceException("Invalid number of parameters");
       }
       Class<?>[] parameterTypes = new Class[paramCount];
 
       for (int i = 0; i < parameterTypes.length; i++) {
-        String paramClassName = maybeDeobfuscate(streamReader,
-            streamReader.readString());
+        String paramClassName = maybeDeobfuscate(streamReader, streamReader.readString());
 
         try {
-          parameterTypes[i] = getClassFromSerializedName(paramClassName,
-              classLoader);
+          parameterTypes[i] = getClassFromSerializedName(paramClassName, classLoader);
         } catch (ClassNotFoundException e) {
           throw new IncompatibleRemoteServiceException("Parameter " + i
               + " of is of an unknown type '" + paramClassName + "'", e);
@@ -298,18 +296,29 @@
       try {
         Method method = serviceIntf.getMethod(serviceMethodName, parameterTypes);
 
-        Object[] parameterValues = new Object[parameterTypes.length];
-        for (int i = 0; i < parameterValues.length; i++) {
-          parameterValues[i] = streamReader.deserializeValue(parameterTypes[i]);
+        // The parameter types we have are the non-parameterized versions in the
+        // RPC stream. For stronger message verification, get the parameterized
+        // types from the method declaration.
+        Type[] methodParameterTypes = method.getGenericParameterTypes();
+        DequeMap<Type, Type> resolvedTypes = new DequeMap<Type, Type>();
+
+        TypeVariable<Method>[] methodTypes = method.getTypeParameters();
+        for (TypeVariable<Method> methodType : methodTypes) {
+          SerializabilityUtil.resolveTypes(methodType, resolvedTypes);
         }
 
-        return new RPCRequest(method, parameterValues, rpcToken,
-            serializationPolicy, streamReader.getFlags());
+        Object[] parameterValues = new Object[parameterTypes.length];
+        for (int i = 0; i < parameterValues.length; i++) {
+          parameterValues[i] =
+              streamReader.deserializeValue(parameterTypes[i], methodParameterTypes[i],
+                  resolvedTypes);
+        }
 
+        return new RPCRequest(method, parameterValues, rpcToken, serializationPolicy, streamReader
+            .getFlags());
       } catch (NoSuchMethodException e) {
-        throw new IncompatibleRemoteServiceException(
-            formatMethodNotFoundErrorMessage(serviceIntf, serviceMethodName,
-                parameterTypes));
+        throw new IncompatibleRemoteServiceException(formatMethodNotFoundErrorMessage(serviceIntf,
+            serviceMethodName, parameterTypes));
       }
     } catch (SerializationException ex) {
       throw new IncompatibleRemoteServiceException(ex.getMessage(), ex);
@@ -331,10 +340,9 @@
    * @throws UnexpectedException if the result was an unexpected exception (a
    *           checked exception not declared in the serviceMethod's signature)
    */
-  public static String encodeResponseForFailure(Method serviceMethod,
-      Throwable cause) throws SerializationException {
-    return encodeResponseForFailure(serviceMethod, cause,
-        getDefaultSerializationPolicy());
+  public static String encodeResponseForFailure(Method serviceMethod, Throwable cause)
+      throws SerializationException {
+    return encodeResponseForFailure(serviceMethod, cause, getDefaultSerializationPolicy());
   }
 
   /**
@@ -362,16 +370,14 @@
    * @throws UnexpectedException if the result was an unexpected exception (a
    *           checked exception not declared in the serviceMethod's signature)
    */
-  public static String encodeResponseForFailure(Method serviceMethod,
-      Throwable cause, SerializationPolicy serializationPolicy)
-      throws SerializationException {
+  public static String encodeResponseForFailure(Method serviceMethod, Throwable cause,
+      SerializationPolicy serializationPolicy) throws SerializationException {
     return encodeResponseForFailure(serviceMethod, cause, serializationPolicy,
         AbstractSerializationStream.DEFAULT_FLAGS);
   }
 
-  public static String encodeResponseForFailure(Method serviceMethod,
-      Throwable cause, SerializationPolicy serializationPolicy, int flags)
-      throws SerializationException {
+  public static String encodeResponseForFailure(Method serviceMethod, Throwable cause,
+      SerializationPolicy serializationPolicy, int flags) throws SerializationException {
     if (cause == null) {
       throw new NullPointerException("cause cannot be null");
     }
@@ -380,15 +386,12 @@
       throw new NullPointerException("serializationPolicy");
     }
 
-    if (serviceMethod != null
-        && !RPCServletUtils.isExpectedException(serviceMethod, cause)) {
-      throw new UnexpectedException("Service method '"
-          + getSourceRepresentation(serviceMethod)
+    if (serviceMethod != null && !RPCServletUtils.isExpectedException(serviceMethod, cause)) {
+      throw new UnexpectedException("Service method '" + getSourceRepresentation(serviceMethod)
           + "' threw an unexpected exception: " + cause.toString(), cause);
     }
 
-    return encodeResponse(cause.getClass(), cause, true, flags,
-        serializationPolicy);
+    return encodeResponse(cause.getClass(), cause, true, flags, serializationPolicy);
   }
 
   /**
@@ -405,10 +408,9 @@
    * @throws NullPointerException if the service method is <code>null</code>
    * @throws SerializationException if the result cannot be serialized
    */
-  public static String encodeResponseForSuccess(Method serviceMethod,
-      Object object) throws SerializationException {
-    return encodeResponseForSuccess(serviceMethod, object,
-        getDefaultSerializationPolicy());
+  public static String encodeResponseForSuccess(Method serviceMethod, Object object)
+      throws SerializationException {
+    return encodeResponseForSuccess(serviceMethod, object, getDefaultSerializationPolicy());
   }
 
   /**
@@ -435,16 +437,14 @@
    *           serializationPolicy are <code>null</code>
    * @throws SerializationException if the result cannot be serialized
    */
-  public static String encodeResponseForSuccess(Method serviceMethod,
-      Object object, SerializationPolicy serializationPolicy)
-      throws SerializationException {
+  public static String encodeResponseForSuccess(Method serviceMethod, Object object,
+      SerializationPolicy serializationPolicy) throws SerializationException {
     return encodeResponseForSuccess(serviceMethod, object, serializationPolicy,
         AbstractSerializationStream.DEFAULT_FLAGS);
   }
 
-  public static String encodeResponseForSuccess(Method serviceMethod,
-      Object object, SerializationPolicy serializationPolicy, int flags)
-      throws SerializationException {
+  public static String encodeResponseForSuccess(Method serviceMethod, Object object,
+      SerializationPolicy serializationPolicy, int flags) throws SerializationException {
     if (serviceMethod == null) {
       throw new NullPointerException("serviceMethod cannot be null");
     }
@@ -462,17 +462,14 @@
         actualReturnType = object.getClass();
       }
 
-      if (actualReturnType == null
-          || !methodReturnType.isAssignableFrom(actualReturnType)) {
-        throw new IllegalArgumentException("Type '"
-            + printTypeName(object.getClass())
+      if (actualReturnType == null || !methodReturnType.isAssignableFrom(actualReturnType)) {
+        throw new IllegalArgumentException("Type '" + printTypeName(object.getClass())
             + "' does not match the return type in the method's signature: '"
             + getSourceRepresentation(serviceMethod) + "'");
       }
     }
 
-    return encodeResponse(methodReturnType, object, false, flags,
-        serializationPolicy);
+    return encodeResponse(methodReturnType, object, false, flags, serializationPolicy);
   }
 
   /**
@@ -506,10 +503,9 @@
    * @throws UnexpectedException if the serviceMethod throws a checked exception
    *           that is not declared in its signature
    */
-  public static String invokeAndEncodeResponse(Object target,
-      Method serviceMethod, Object[] args) throws SerializationException {
-    return invokeAndEncodeResponse(target, serviceMethod, args,
-        getDefaultSerializationPolicy());
+  public static String invokeAndEncodeResponse(Object target, Method serviceMethod, Object[] args)
+      throws SerializationException {
+    return invokeAndEncodeResponse(target, serviceMethod, args, getDefaultSerializationPolicy());
   }
 
   /**
@@ -545,17 +541,14 @@
    * @throws UnexpectedException if the serviceMethod throws a checked exception
    *           that is not declared in its signature
    */
-  public static String invokeAndEncodeResponse(Object target,
-      Method serviceMethod, Object[] args,
+  public static String invokeAndEncodeResponse(Object target, Method serviceMethod, Object[] args,
       SerializationPolicy serializationPolicy) throws SerializationException {
-    return invokeAndEncodeResponse(target, serviceMethod, args,
-        serializationPolicy, AbstractSerializationStream.DEFAULT_FLAGS);
+    return invokeAndEncodeResponse(target, serviceMethod, args, serializationPolicy,
+        AbstractSerializationStream.DEFAULT_FLAGS);
   }
 
-  public static String invokeAndEncodeResponse(Object target,
-      Method serviceMethod, Object[] args,
-      SerializationPolicy serializationPolicy, int flags)
-      throws SerializationException {
+  public static String invokeAndEncodeResponse(Object target, Method serviceMethod, Object[] args,
+      SerializationPolicy serializationPolicy, int flags) throws SerializationException {
     if (serviceMethod == null) {
       throw new NullPointerException("serviceMethod");
     }
@@ -568,16 +561,15 @@
     try {
       Object result = serviceMethod.invoke(target, args);
 
-      responsePayload = encodeResponseForSuccess(serviceMethod, result,
-          serializationPolicy, flags);
+      responsePayload = encodeResponseForSuccess(serviceMethod, result, serializationPolicy, flags);
     } catch (IllegalAccessException e) {
-      SecurityException securityException = new SecurityException(
-          formatIllegalAccessErrorMessage(target, serviceMethod));
+      SecurityException securityException =
+          new SecurityException(formatIllegalAccessErrorMessage(target, serviceMethod));
       securityException.initCause(e);
       throw securityException;
     } catch (IllegalArgumentException e) {
-      SecurityException securityException = new SecurityException(
-          formatIllegalArgumentErrorMessage(target, serviceMethod, args));
+      SecurityException securityException =
+          new SecurityException(formatIllegalArgumentErrorMessage(target, serviceMethod, args));
       securityException.initCause(e);
       throw securityException;
     } catch (InvocationTargetException e) {
@@ -585,8 +577,7 @@
       //
       Throwable cause = e.getCause();
 
-      responsePayload = encodeResponseForFailure(serviceMethod, cause,
-          serializationPolicy, flags);
+      responsePayload = encodeResponseForFailure(serviceMethod, cause, serializationPolicy, flags);
     }
 
     return responsePayload;
@@ -603,12 +594,11 @@
    * @return a string that encodes the response from a service method
    * @throws SerializationException if the object cannot be serialized
    */
-  private static String encodeResponse(Class<?> responseClass, Object object,
-      boolean wasThrown, int flags, SerializationPolicy serializationPolicy)
-      throws SerializationException {
+  private static String encodeResponse(Class<?> responseClass, Object object, boolean wasThrown,
+      int flags, SerializationPolicy serializationPolicy) throws SerializationException {
 
-    ServerSerializationStreamWriter stream = new ServerSerializationStreamWriter(
-        serializationPolicy);
+    ServerSerializationStreamWriter stream =
+        new ServerSerializationStreamWriter(serializationPolicy);
     stream.setFlags(flags);
 
     stream.prepareToWrite();
@@ -620,8 +610,7 @@
     return bufferStr;
   }
 
-  private static String formatIllegalAccessErrorMessage(Object target,
-      Method serviceMethod) {
+  private static String formatIllegalAccessErrorMessage(Object target, Method serviceMethod) {
     StringBuffer sb = new StringBuffer();
     sb.append("Blocked attempt to access inaccessible method '");
     sb.append(getSourceRepresentation(serviceMethod));
@@ -638,8 +627,8 @@
     return sb.toString();
   }
 
-  private static String formatIllegalArgumentErrorMessage(Object target,
-      Method serviceMethod, Object[] args) {
+  private static String formatIllegalArgumentErrorMessage(Object target, Method serviceMethod,
+      Object[] args) {
     StringBuffer sb = new StringBuffer();
     sb.append("Blocked attempt to invoke method '");
     sb.append(getSourceRepresentation(serviceMethod));
@@ -690,8 +679,8 @@
    * @return Class instance for the given type name
    * @throws ClassNotFoundException if the named type was not found
    */
-  private static Class<?> getClassFromSerializedName(String serializedName,
-      ClassLoader classLoader) throws ClassNotFoundException {
+  private static Class<?> getClassFromSerializedName(String serializedName, ClassLoader classLoader)
+      throws ClassNotFoundException {
     Class<?> value = TYPE_NAMES.get(serializedName);
     if (value != null) {
       return value;
@@ -768,8 +757,7 @@
   /**
    * Only called from implementsInterface().
    */
-  private static boolean implementsInterfaceRecursive(Class<?> clazz,
-      String intfName) {
+  private static boolean implementsInterfaceRecursive(Class<?> clazz, String intfName) {
     assert (clazz.isInterface());
 
     if (clazz.getName().equals(intfName)) {
@@ -792,8 +780,7 @@
    * the original identifier if deobfuscation is unnecessary or no mapping is
    * known.
    */
-  private static String maybeDeobfuscate(
-      ServerSerializationStreamReader streamReader, String name)
+  private static String maybeDeobfuscate(ServerSerializationStreamReader streamReader, String name)
       throws SerializationException {
     int index;
     if (streamReader.hasFlags(AbstractSerializationStream.FLAG_ELIDE_TYPE_NAMES)) {
diff --git a/user/src/com/google/gwt/user/server/rpc/ServerCustomFieldSerializer.java b/user/src/com/google/gwt/user/server/rpc/ServerCustomFieldSerializer.java
new file mode 100644
index 0000000..3376e54
--- /dev/null
+++ b/user/src/com/google/gwt/user/server/rpc/ServerCustomFieldSerializer.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2011 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.user.server.rpc;
+
+import com.google.gwt.user.client.rpc.CustomFieldSerializer;
+import com.google.gwt.user.client.rpc.SerializationException;
+import com.google.gwt.user.server.rpc.impl.DequeMap;
+import com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader;
+
+import java.lang.reflect.Type;
+
+/**
+ * An interface that may be implemented by server-side class-based custom field
+ * serializers.
+ * 
+ * Usage of this class will reduce the amount of server-side reflection during
+ * serialization and provide type safety.
+ * 
+ * @param <T> the type of the object being serialized
+ */
+public abstract class ServerCustomFieldSerializer<T> extends CustomFieldSerializer<T> {
+  /**
+   * Deserializes the content of the object from the
+   * {@link ServerSerializationStreamReader}, with type checking.
+   * 
+   * @param streamReader the {@link ServerSerializationStreamReader} to read the
+   *          object's content from
+   * @param instance the object instance to deserialize
+   * @param instanceClass the class of the instance for type checking purposes
+   * @param resolvedTypes map from generic types to actual types
+   * 
+   * @throws SerializationException if the deserialization operation is not
+   *           successful
+   */
+  public abstract void deserializeInstance(ServerSerializationStreamReader streamReader,
+      T instance, Class<?> instanceClass, DequeMap<Type, Type> resolvedTypes)
+      throws SerializationException;
+
+  /**
+   * Instantiates an object from the {@link ServerSerializationStreamReader},
+   * without type checking.
+   * 
+   * @param streamReader the {@link ServerSerializationStreamReader} to read the
+   *          object's content from
+   * @return an object that has been loaded from the
+   *         {@link ServerSerializationStreamReader}
+   * 
+   * @throws SerializationException if the instantiation operation is not
+   *           successful
+   */
+  public T instantiateInstance(ServerSerializationStreamReader streamReader)
+      throws SerializationException {
+    return super.instantiateInstance(streamReader);
+  }
+
+  /**
+   * Instantiates an object from the {@link ServerSerializationStreamReader},
+   * with type checking.
+   * <p>
+   * Most of the time, this can be left unimplemented and the framework will
+   * instantiate the instance itself. This is typically used when the object
+   * being deserialized is immutable, hence it has to be created with its state
+   * already set.
+   * <p>
+   * If this is overridden, the
+   * {@link CustomFieldSerializer#hasCustomInstantiateInstance()} method must
+   * return <code>true</code> in order for the framework to know to call it.
+   * 
+   * @param streamReader the {@link ServerSerializationStreamReader} to read the
+   *          object's content from
+   * @param instanceClass the class of the instance for type checking purposes
+   * @param resolvedTypes map from generic types to actual types
+   * 
+   * @return an object that has been loaded from the
+   *         {@link ServerSerializationStreamReader}
+   * 
+   * @throws SerializationException if the instantiation operation is not
+   *           successful
+   */
+  @SuppressWarnings("unused")
+  public T instantiateInstance(ServerSerializationStreamReader streamReader,
+      Class<?> instanceClass, DequeMap<Type, Type> resolvedTypes)
+      throws SerializationException {
+    return super.instantiateInstance(streamReader);
+  }
+
+}
diff --git a/user/src/com/google/gwt/user/server/rpc/core/java/lang/Object_Array_ServerCustomFieldSerializer.java b/user/src/com/google/gwt/user/server/rpc/core/java/lang/Object_Array_ServerCustomFieldSerializer.java
new file mode 100644
index 0000000..74ec8e3
--- /dev/null
+++ b/user/src/com/google/gwt/user/server/rpc/core/java/lang/Object_Array_ServerCustomFieldSerializer.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2011 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.user.server.rpc.core.java.lang;
+
+import com.google.gwt.user.client.rpc.SerializationException;
+import com.google.gwt.user.client.rpc.SerializationStreamReader;
+import com.google.gwt.user.client.rpc.SerializationStreamWriter;
+import com.google.gwt.user.client.rpc.core.java.lang.Object_Array_CustomFieldSerializer;
+import com.google.gwt.user.server.rpc.ServerCustomFieldSerializer;
+import com.google.gwt.user.server.rpc.impl.DequeMap;
+import com.google.gwt.user.server.rpc.impl.SerializabilityUtil;
+import com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader;
+
+import java.lang.reflect.Type;
+
+/**
+ * Server-side Custom Serializer for arrays of {@link java.lang.Object}.
+ */
+public class Object_Array_ServerCustomFieldSerializer
+    extends ServerCustomFieldSerializer<Object[]> {
+
+  public static void deserialize(ServerSerializationStreamReader streamReader,
+      Object[] instance, Class<?> instanceClass, DequeMap<Type, Type> resolvedTypes)
+      throws SerializationException {
+    Type componentType =
+        SerializabilityUtil.findInstanceParameters(instanceClass, resolvedTypes)[0];
+    for (int itemIndex = 0; itemIndex < instance.length; ++itemIndex) {
+      instance[itemIndex] = streamReader.readObject(componentType, resolvedTypes);
+    }
+  }
+
+  @Override
+  public void deserializeInstance(SerializationStreamReader streamReader,
+      Object[] instance) throws SerializationException {
+    Object_Array_CustomFieldSerializer.deserialize(streamReader, instance);
+  }
+
+  @Override
+  public void deserializeInstance(ServerSerializationStreamReader streamReader,
+      Object[] instance, Class<?> instanceClass, DequeMap<Type, Type> resolvedTypes)
+      throws SerializationException {
+    deserialize(streamReader, instance, instanceClass, resolvedTypes);
+  }
+
+  @Override
+  public void serializeInstance(SerializationStreamWriter streamWriter,
+      Object[] instance) throws SerializationException {
+    Object_Array_CustomFieldSerializer.serialize(streamWriter, instance);
+  }
+}
diff --git a/user/src/com/google/gwt/user/server/rpc/core/java/util/ArrayList_ServerCustomFieldSerializer.java b/user/src/com/google/gwt/user/server/rpc/core/java/util/ArrayList_ServerCustomFieldSerializer.java
new file mode 100644
index 0000000..32b968f
--- /dev/null
+++ b/user/src/com/google/gwt/user/server/rpc/core/java/util/ArrayList_ServerCustomFieldSerializer.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2011 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.user.server.rpc.core.java.util;
+
+import com.google.gwt.user.client.rpc.SerializationException;
+import com.google.gwt.user.client.rpc.SerializationStreamReader;
+import com.google.gwt.user.client.rpc.SerializationStreamWriter;
+import com.google.gwt.user.client.rpc.core.java.util.ArrayList_CustomFieldSerializer;
+import com.google.gwt.user.server.rpc.ServerCustomFieldSerializer;
+import com.google.gwt.user.server.rpc.impl.DequeMap;
+import com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader;
+
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+
+/**
+ * Custom field serializer for {@link java.util.ArrayList}.
+ */
+@SuppressWarnings("rawtypes")
+public final class ArrayList_ServerCustomFieldSerializer extends
+    ServerCustomFieldSerializer<ArrayList> {
+
+  public static void deserialize(ServerSerializationStreamReader streamReader, ArrayList instance,
+      Class<?> instanceClass, DequeMap<Type, Type> resolvedTypes) throws SerializationException {
+    Collection_ServerCustomFieldSerializerBase.deserialize(streamReader, instance, instanceClass,
+        resolvedTypes);
+  }
+
+  @Override
+  public void deserializeInstance(SerializationStreamReader streamReader, ArrayList instance)
+      throws SerializationException {
+    ArrayList_CustomFieldSerializer.deserialize(streamReader, instance);
+  }
+
+  @Override
+  public void deserializeInstance(ServerSerializationStreamReader streamReader, ArrayList instance,
+      Class<?> instanceClass, DequeMap<Type, Type> resolvedTypes) throws SerializationException {
+    deserialize(streamReader, instance, instanceClass, resolvedTypes);
+  }
+
+  @Override
+  public void serializeInstance(SerializationStreamWriter streamWriter, ArrayList instance)
+      throws SerializationException {
+    ArrayList_CustomFieldSerializer.serialize(streamWriter, instance);
+  }
+}
diff --git a/user/src/com/google/gwt/user/server/rpc/core/java/util/Arrays.java b/user/src/com/google/gwt/user/server/rpc/core/java/util/Arrays.java
new file mode 100644
index 0000000..ba6149b
--- /dev/null
+++ b/user/src/com/google/gwt/user/server/rpc/core/java/util/Arrays.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2011 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.user.server.rpc.core.java.util;
+
+import com.google.gwt.user.client.rpc.SerializationException;
+import com.google.gwt.user.client.rpc.SerializationStreamReader;
+import com.google.gwt.user.client.rpc.SerializationStreamWriter;
+import com.google.gwt.user.server.rpc.ServerCustomFieldSerializer;
+import com.google.gwt.user.server.rpc.impl.DequeMap;
+import com.google.gwt.user.server.rpc.impl.SerializabilityUtil;
+import com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.Type;
+import java.util.List;
+
+/**
+ * Dummy class for nesting the server-side custom serializer.
+ */
+public final class Arrays {
+
+  /**
+   * Server-side Custom field serializer for {@link java.util.Arrays.ArrayList}.
+   */
+  public static final class ArrayList_ServerCustomFieldSerializer extends
+      ServerCustomFieldSerializer<List> {
+
+    public static String concreteType() {
+      return java.util.Arrays.asList().getClass().getName();
+    }
+
+    /*
+     * Note: the reason this implementation differs from that of a standard List
+     * (which serializes a number and then each element) is the requirement that
+     * the underlying array retain its correct type across the wire. This gives
+     * toArray() results the correct type, and can generate internal
+     * ArrayStoreExceptions.
+     * 
+     * The type checking is messy because we need some way of converting the
+     * List<X> or related type that we are expecting into the array type that we
+     * are about to try to read. You can't create objects of class Type
+     * directly, so we need to create a dummy array and then use it's class as a
+     * type.
+     */
+    public static List<?> instantiate(ServerSerializationStreamReader streamReader,
+        Class<?> instanceClass, DequeMap<Type, Type> resolvedTypes)
+        throws SerializationException {
+
+      Type[] actualTypes =
+        SerializabilityUtil.findInstanceParameters(instanceClass, resolvedTypes);
+      if (actualTypes == null || actualTypes.length < 1) {
+        return com.google.gwt.user.client.rpc.core.java.util.Arrays.ArrayList_CustomFieldSerializer
+            .instantiate(streamReader);
+      }
+      
+      Class<?> componentClass = SerializabilityUtil.getClassFromType(actualTypes[0], resolvedTypes);
+      if (componentClass == null) {
+        return com.google.gwt.user.client.rpc.core.java.util.Arrays.ArrayList_CustomFieldSerializer
+            .instantiate(streamReader);
+      }
+      
+      Object expectedArray = Array.newInstance(componentClass, 0);
+      Object[] array = (Object[]) streamReader.readObject(expectedArray.getClass(), resolvedTypes);
+      return java.util.Arrays.asList(array);
+    }
+
+    @Override
+    public void deserializeInstance(SerializationStreamReader streamReader, List instance)
+        throws SerializationException {
+      com.google.gwt.user.client.rpc.core.java.util.Arrays.ArrayList_CustomFieldSerializer
+          .deserialize(streamReader, instance);
+    }
+
+    @Override
+    public void deserializeInstance(ServerSerializationStreamReader streamReader, List instance,
+        Class<?> instanceClass, DequeMap<Type, Type> actualParameterTypes)
+        throws SerializationException {
+      // Handled in instantiateInstance.
+    }
+
+    @Override
+    public boolean hasCustomInstantiateInstance() {
+      return true;
+    }
+
+    @Override
+    public List instantiateInstance(SerializationStreamReader streamReader)
+        throws SerializationException {
+      return com.google.gwt.user.client.rpc.core.java.util.Arrays.ArrayList_CustomFieldSerializer
+          .instantiate(streamReader);
+    }
+
+    @Override
+    public List instantiateInstance(ServerSerializationStreamReader streamReader,
+        Class<?> instanceClass, DequeMap<Type, Type> resolvedTypes) throws SerializationException {
+      return instantiate(streamReader, instanceClass, resolvedTypes);
+    }
+
+    @Override
+    public void serializeInstance(SerializationStreamWriter streamWriter, List instance)
+        throws SerializationException {
+      com.google.gwt.user.client.rpc.core.java.util.Arrays.ArrayList_CustomFieldSerializer
+          .serialize(streamWriter, instance);
+    }
+  }
+}
diff --git a/user/src/com/google/gwt/user/server/rpc/core/java/util/Collection_ServerCustomFieldSerializerBase.java b/user/src/com/google/gwt/user/server/rpc/core/java/util/Collection_ServerCustomFieldSerializerBase.java
new file mode 100644
index 0000000..962cf43
--- /dev/null
+++ b/user/src/com/google/gwt/user/server/rpc/core/java/util/Collection_ServerCustomFieldSerializerBase.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2011 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.user.server.rpc.core.java.util;
+
+import com.google.gwt.user.client.rpc.SerializationException;
+import com.google.gwt.user.client.rpc.core.java.util.Collection_CustomFieldSerializerBase;
+import com.google.gwt.user.server.rpc.impl.DequeMap;
+import com.google.gwt.user.server.rpc.impl.SerializabilityUtil;
+import com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader;
+
+import java.lang.reflect.Type;
+import java.util.Collection;
+
+/**
+ * Custom field serializer for {@link java.util.Collection}, used by
+ * implementing classes.
+ */
+public final class Collection_ServerCustomFieldSerializerBase {
+
+  @SuppressWarnings({"rawtypes", "unchecked"})
+  public static void deserialize(ServerSerializationStreamReader streamReader, Collection instance,
+      Class<?> actualClass, DequeMap<Type, Type> resolvedTypes) throws SerializationException {
+    Type[] actualTypes = SerializabilityUtil.findInstanceParameters(actualClass, resolvedTypes);
+    if (actualTypes == null || actualTypes.length < 1) {
+      Collection_CustomFieldSerializerBase.deserialize(streamReader, instance);
+      return;
+    }
+
+    int size = streamReader.readInt();
+    for (int i = 0; i < size; ++i) {
+      Object obj = streamReader.readObject(actualTypes[0], resolvedTypes);
+      instance.add(obj);
+    }
+  }
+}
diff --git a/user/src/com/google/gwt/user/server/rpc/core/java/util/Collections.java b/user/src/com/google/gwt/user/server/rpc/core/java/util/Collections.java
new file mode 100644
index 0000000..aa54331
--- /dev/null
+++ b/user/src/com/google/gwt/user/server/rpc/core/java/util/Collections.java
@@ -0,0 +1,97 @@
+/*
+ * 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.user.server.rpc.core.java.util;
+
+import com.google.gwt.user.client.rpc.SerializationException;
+import com.google.gwt.user.client.rpc.SerializationStreamReader;
+import com.google.gwt.user.client.rpc.SerializationStreamWriter;
+import com.google.gwt.user.server.rpc.ServerCustomFieldSerializer;
+import com.google.gwt.user.server.rpc.impl.DequeMap;
+import com.google.gwt.user.server.rpc.impl.SerializabilityUtil;
+import com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader;
+
+import java.lang.reflect.Type;
+import java.util.List;
+
+/**
+ * Dummy class for nesting the custom serializer.
+ */
+public final class Collections {
+
+  /**
+   * Custom field serializer for {@link java.util.Collections.SingletonList}.
+   */
+  @SuppressWarnings("rawtypes")
+  public static final class SingletonList_ServerCustomFieldSerializer extends
+      ServerCustomFieldSerializer<List> {
+
+    public static String concreteType() {
+      return java.util.Collections.singletonList(null).getClass().getName();
+    }
+
+    public static List instantiate(ServerSerializationStreamReader streamReader,
+        Class<?> instanceClass, DequeMap<Type, Type> resolvedTypes)
+        throws SerializationException {
+      Type[] actualTypes =
+          SerializabilityUtil.findInstanceParameters(instanceClass, resolvedTypes);
+      if (actualTypes == null || actualTypes.length < 1) {
+        return com.google.gwt.user.client.rpc.core.java.util.Collections.SingletonList_CustomFieldSerializer.instantiate(streamReader);
+      }
+
+      return java.util.Collections.singletonList(streamReader.readObject(actualTypes[0],
+          resolvedTypes));
+    }
+
+    @SuppressWarnings("unused")
+    @Override
+    public void deserializeInstance(SerializationStreamReader streamReader, List instance)
+        throws SerializationException {
+      // Handled in instantiate.
+    }
+
+    @SuppressWarnings("unused")
+    @Override
+    public void deserializeInstance(ServerSerializationStreamReader streamReader, List instance,
+        Class<?> instanceClass, DequeMap<Type, Type> resolvedTypes)
+        throws SerializationException {
+      // Handled in instantiate.
+    }
+
+    @Override
+    public boolean hasCustomInstantiateInstance() {
+      return true;
+    }
+
+    @Override
+    public List instantiateInstance(SerializationStreamReader streamReader)
+        throws SerializationException {
+      return com.google.gwt.user.client.rpc.core.java.util.Collections.SingletonList_CustomFieldSerializer.instantiate(streamReader);
+    }
+
+    @Override
+    public List instantiateInstance(ServerSerializationStreamReader streamReader,
+        Class<?> instanceClass, DequeMap<Type, Type> resolvedTypes)
+        throws SerializationException {
+      return instantiate(streamReader, instanceClass, resolvedTypes);
+    }
+
+    @Override
+    public void serializeInstance(SerializationStreamWriter streamWriter, List instance)
+        throws SerializationException {
+      com.google.gwt.user.client.rpc.core.java.util.Collections.SingletonList_CustomFieldSerializer.serialize(streamWriter, instance);
+    }
+  }
+}
diff --git a/user/src/com/google/gwt/user/server/rpc/core/java/util/HashMap_ServerCustomFieldSerializer.java b/user/src/com/google/gwt/user/server/rpc/core/java/util/HashMap_ServerCustomFieldSerializer.java
new file mode 100644
index 0000000..b4d6bc6
--- /dev/null
+++ b/user/src/com/google/gwt/user/server/rpc/core/java/util/HashMap_ServerCustomFieldSerializer.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2011 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.user.server.rpc.core.java.util;
+
+import com.google.gwt.user.client.rpc.SerializationException;
+import com.google.gwt.user.client.rpc.SerializationStreamReader;
+import com.google.gwt.user.client.rpc.SerializationStreamWriter;
+import com.google.gwt.user.client.rpc.core.java.util.Map_CustomFieldSerializerBase;
+import com.google.gwt.user.server.rpc.ServerCustomFieldSerializer;
+import com.google.gwt.user.server.rpc.impl.DequeMap;
+import com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader;
+
+import java.lang.reflect.Type;
+import java.util.HashMap;
+
+/**
+ * Custom field serializer for {@link java.util.HashMap}.
+ */
+@SuppressWarnings("rawtypes")
+public final class HashMap_ServerCustomFieldSerializer extends
+    ServerCustomFieldSerializer<HashMap> {
+
+  public static void deserialize(ServerSerializationStreamReader streamReader,
+      HashMap instance, Class<?> instanceClass, DequeMap<Type, Type> resolvedTypes)
+      throws SerializationException {
+    Map_ServerCustomFieldSerializerBase.deserialize(streamReader, instance, instanceClass,
+        resolvedTypes);
+  }
+
+  @Override
+  public void deserializeInstance(SerializationStreamReader streamReader,
+      HashMap instance) throws SerializationException {
+    Map_CustomFieldSerializerBase.deserialize(streamReader, instance);
+  }
+
+  @Override
+  public void deserializeInstance(ServerSerializationStreamReader streamReader,
+      HashMap instance, Class<?> instanceClass, DequeMap<Type, Type> resolvedTypes)
+      throws SerializationException {
+    deserialize(streamReader, instance, instanceClass, resolvedTypes);
+  }
+
+  @Override
+  public void serializeInstance(SerializationStreamWriter streamWriter,
+      HashMap instance) throws SerializationException {
+    Map_CustomFieldSerializerBase.serialize(streamWriter, instance);
+  }
+}
diff --git a/user/src/com/google/gwt/user/server/rpc/core/java/util/HashSet_ServerCustomFieldSerializer.java b/user/src/com/google/gwt/user/server/rpc/core/java/util/HashSet_ServerCustomFieldSerializer.java
new file mode 100644
index 0000000..d20210f
--- /dev/null
+++ b/user/src/com/google/gwt/user/server/rpc/core/java/util/HashSet_ServerCustomFieldSerializer.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2011 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.user.server.rpc.core.java.util;
+
+import com.google.gwt.user.client.rpc.SerializationException;
+import com.google.gwt.user.client.rpc.SerializationStreamReader;
+import com.google.gwt.user.client.rpc.SerializationStreamWriter;
+import com.google.gwt.user.client.rpc.core.java.util.Collection_CustomFieldSerializerBase;
+import com.google.gwt.user.server.rpc.ServerCustomFieldSerializer;
+import com.google.gwt.user.server.rpc.impl.DequeMap;
+import com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader;
+
+import java.lang.reflect.Type;
+import java.util.HashSet;
+
+/**
+ * Custom field serializer for {@link java.util.HashSet}.
+ */
+@SuppressWarnings("rawtypes")
+public final class HashSet_ServerCustomFieldSerializer extends
+    ServerCustomFieldSerializer<HashSet> {
+
+  public static void deserialize(ServerSerializationStreamReader streamReader,
+      HashSet instance, Class<?> instanceClass, DequeMap<Type, Type> resolvedTypes)
+      throws SerializationException {
+    Collection_ServerCustomFieldSerializerBase.deserialize(streamReader, instance, instanceClass,
+        resolvedTypes);
+  }
+
+  @Override
+  public void deserializeInstance(SerializationStreamReader streamReader,
+      HashSet instance) throws SerializationException {
+    Collection_CustomFieldSerializerBase.deserialize(streamReader, instance);
+  }
+
+  @Override
+  public void deserializeInstance(ServerSerializationStreamReader streamReader,
+      HashSet instance, Class<?> instanceClass, DequeMap<Type, Type> resolvedTypes)
+      throws SerializationException {
+    deserialize(streamReader, instance, instanceClass, resolvedTypes);
+  }
+
+  @Override
+  public void serializeInstance(SerializationStreamWriter streamWriter,
+      HashSet instance) throws SerializationException {
+    Collection_CustomFieldSerializerBase.serialize(streamWriter, instance);
+  }
+}
diff --git a/user/src/com/google/gwt/user/server/rpc/core/java/util/IdentityHashMap_ServerCustomFieldSerializer.java b/user/src/com/google/gwt/user/server/rpc/core/java/util/IdentityHashMap_ServerCustomFieldSerializer.java
new file mode 100644
index 0000000..c2818ac
--- /dev/null
+++ b/user/src/com/google/gwt/user/server/rpc/core/java/util/IdentityHashMap_ServerCustomFieldSerializer.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2011 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.user.server.rpc.core.java.util;
+
+import com.google.gwt.user.client.rpc.SerializationException;
+import com.google.gwt.user.client.rpc.SerializationStreamReader;
+import com.google.gwt.user.client.rpc.SerializationStreamWriter;
+import com.google.gwt.user.client.rpc.core.java.util.Map_CustomFieldSerializerBase;
+import com.google.gwt.user.server.rpc.ServerCustomFieldSerializer;
+import com.google.gwt.user.server.rpc.impl.DequeMap;
+import com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader;
+
+import java.lang.reflect.Type;
+import java.util.IdentityHashMap;
+
+/**
+ * Custom field serializer for {@link java.util.HashMap}.
+ */
+@SuppressWarnings("rawtypes")
+public final class IdentityHashMap_ServerCustomFieldSerializer extends
+    ServerCustomFieldSerializer<IdentityHashMap> {
+
+  public static void deserialize(ServerSerializationStreamReader streamReader,
+      IdentityHashMap instance, Class<?> instanceClass, DequeMap<Type, Type> resolvedTypes)
+      throws SerializationException {
+    Map_ServerCustomFieldSerializerBase.deserialize(streamReader, instance, instanceClass,
+        resolvedTypes);
+  }
+
+  @Override
+  public void deserializeInstance(SerializationStreamReader streamReader,
+      IdentityHashMap instance) throws SerializationException {
+    Map_CustomFieldSerializerBase.deserialize(streamReader, instance);
+  }
+
+  @Override
+  public void deserializeInstance(ServerSerializationStreamReader streamReader,
+      IdentityHashMap instance, Class<?> instanceClass, DequeMap<Type, Type> resolvedTypes)
+      throws SerializationException {
+    deserialize(streamReader, instance, instanceClass, resolvedTypes);
+  }
+
+  @Override
+  public void serializeInstance(SerializationStreamWriter streamWriter,
+      IdentityHashMap instance) throws SerializationException {
+    Map_CustomFieldSerializerBase.serialize(streamWriter, instance);
+  }
+}
diff --git a/user/src/com/google/gwt/user/server/rpc/core/java/util/LinkedHashMap_ServerCustomFieldSerializer.java b/user/src/com/google/gwt/user/server/rpc/core/java/util/LinkedHashMap_ServerCustomFieldSerializer.java
new file mode 100644
index 0000000..9ab2f8b
--- /dev/null
+++ b/user/src/com/google/gwt/user/server/rpc/core/java/util/LinkedHashMap_ServerCustomFieldSerializer.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2011 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.user.server.rpc.core.java.util;
+
+import com.google.gwt.user.client.rpc.SerializationException;
+import com.google.gwt.user.client.rpc.SerializationStreamReader;
+import com.google.gwt.user.client.rpc.SerializationStreamWriter;
+import com.google.gwt.user.client.rpc.core.java.util.LinkedHashMap_CustomFieldSerializer;
+import com.google.gwt.user.server.rpc.ServerCustomFieldSerializer;
+import com.google.gwt.user.server.rpc.impl.DequeMap;
+import com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader;
+
+import java.lang.reflect.Type;
+import java.util.LinkedHashMap;
+
+/**
+ * Custom field serializer for {@link java.util.LinkedHashMap} for the server
+ * (uses reflection).
+ */
+@SuppressWarnings("rawtypes")
+public final class LinkedHashMap_ServerCustomFieldSerializer extends
+    ServerCustomFieldSerializer<LinkedHashMap> {
+
+  public static void deserialize(ServerSerializationStreamReader streamReader,
+      LinkedHashMap instance, Class<?> instanceClass, DequeMap<Type, Type> resolvedTypes)
+      throws SerializationException {
+    Map_ServerCustomFieldSerializerBase.deserialize(streamReader, instance, instanceClass,
+        resolvedTypes);
+  }
+
+  @Override
+  public void deserializeInstance(SerializationStreamReader streamReader, LinkedHashMap instance)
+      throws SerializationException {
+    LinkedHashMap_CustomFieldSerializer.deserialize(streamReader, instance);
+  }
+
+  @Override
+  public void deserializeInstance(ServerSerializationStreamReader streamReader,
+      LinkedHashMap instance, Class<?> instanceClass, DequeMap<Type, Type> resolvedTypes)
+      throws SerializationException {
+    deserialize(streamReader, instance, instanceClass, resolvedTypes);
+  }
+
+  @Override
+  public boolean hasCustomInstantiateInstance() {
+    return true;
+  }
+
+  @Override
+  public LinkedHashMap instantiateInstance(SerializationStreamReader streamReader)
+      throws SerializationException {
+    return LinkedHashMap_CustomFieldSerializer.instantiate(streamReader);
+  }
+
+  @Override
+  public LinkedHashMap instantiateInstance(ServerSerializationStreamReader streamReader,
+      Class<?> instanceClass, DequeMap<Type, Type> resolvedTypes)
+      throws SerializationException {
+    return LinkedHashMap_CustomFieldSerializer.instantiate(streamReader);
+  }
+
+  @Override
+  public void serializeInstance(SerializationStreamWriter streamWriter, LinkedHashMap instance)
+      throws SerializationException {
+    LinkedHashMap_CustomFieldSerializer.serialize(streamWriter, instance);
+  }
+}
diff --git a/user/src/com/google/gwt/user/server/rpc/core/java/util/LinkedList_ServerCustomFieldSerializer.java b/user/src/com/google/gwt/user/server/rpc/core/java/util/LinkedList_ServerCustomFieldSerializer.java
new file mode 100644
index 0000000..7a318b9
--- /dev/null
+++ b/user/src/com/google/gwt/user/server/rpc/core/java/util/LinkedList_ServerCustomFieldSerializer.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2011 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.user.server.rpc.core.java.util;
+
+import com.google.gwt.user.client.rpc.SerializationException;
+import com.google.gwt.user.client.rpc.SerializationStreamReader;
+import com.google.gwt.user.client.rpc.SerializationStreamWriter;
+import com.google.gwt.user.client.rpc.core.java.util.Collection_CustomFieldSerializerBase;
+import com.google.gwt.user.server.rpc.ServerCustomFieldSerializer;
+import com.google.gwt.user.server.rpc.impl.DequeMap;
+import com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader;
+
+import java.lang.reflect.Type;
+import java.util.LinkedList;
+
+/**
+ * Custom field serializer for {@link java.util.ArrayList}.
+ */
+@SuppressWarnings("rawtypes")
+public final class LinkedList_ServerCustomFieldSerializer extends
+    ServerCustomFieldSerializer<LinkedList> {
+
+  public static void deserialize(ServerSerializationStreamReader streamReader, LinkedList instance,
+      Class<?> instanceClass, DequeMap<Type, Type> resolvedTypes) throws SerializationException {
+    Collection_ServerCustomFieldSerializerBase.deserialize(streamReader, instance, instanceClass,
+        resolvedTypes);
+  }
+
+  @Override
+  public void deserializeInstance(SerializationStreamReader streamReader, LinkedList instance)
+      throws SerializationException {
+    Collection_CustomFieldSerializerBase.deserialize(streamReader, instance);
+  }
+
+  @Override
+  public void deserializeInstance(ServerSerializationStreamReader streamReader,
+      LinkedList instance, Class<?> instanceClass, DequeMap<Type, Type> resolvedTypes)
+      throws SerializationException {
+    deserialize(streamReader, instance, instanceClass, resolvedTypes);
+  }
+
+  @Override
+  public void serializeInstance(SerializationStreamWriter streamWriter, LinkedList instance)
+      throws SerializationException {
+    Collection_CustomFieldSerializerBase.serialize(streamWriter, instance);
+  }
+}
diff --git a/user/src/com/google/gwt/user/server/rpc/core/java/util/Map_ServerCustomFieldSerializerBase.java b/user/src/com/google/gwt/user/server/rpc/core/java/util/Map_ServerCustomFieldSerializerBase.java
new file mode 100644
index 0000000..2fe7368
--- /dev/null
+++ b/user/src/com/google/gwt/user/server/rpc/core/java/util/Map_ServerCustomFieldSerializerBase.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2011 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.user.server.rpc.core.java.util;
+
+import com.google.gwt.user.client.rpc.SerializationException;
+import com.google.gwt.user.client.rpc.core.java.util.Map_CustomFieldSerializerBase;
+import com.google.gwt.user.server.rpc.impl.DequeMap;
+import com.google.gwt.user.server.rpc.impl.SerializabilityUtil;
+import com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader;
+
+import java.lang.reflect.Type;
+import java.util.Map;
+
+/**
+ * Server-side Custom field serializer for {@link java.util.HashMap}.
+ */
+public final class Map_ServerCustomFieldSerializerBase {
+
+  @SuppressWarnings({"rawtypes", "unchecked"})
+  public static void deserialize(ServerSerializationStreamReader streamReader, Map instance,
+      Class<?> instanceClass, DequeMap<Type, Type> resolvedTypes)
+      throws SerializationException {
+    Type[] actualTypes =
+        SerializabilityUtil.findInstanceParameters(instanceClass, resolvedTypes);
+    if (actualTypes == null || actualTypes.length < 2) {
+      Map_CustomFieldSerializerBase.deserialize(streamReader, instance);
+      return;
+    }
+
+    int size = streamReader.readInt();
+    for (int i = 0; i < size; ++i) {
+      Object key = streamReader.readObject(actualTypes[0], resolvedTypes);
+      Object value = streamReader.readObject(actualTypes[1], resolvedTypes);
+
+      instance.put(key, value);
+    }
+  }
+
+}
\ No newline at end of file
diff --git a/user/src/com/google/gwt/user/server/rpc/core/java/util/TreeMap_ServerCustomFieldSerializer.java b/user/src/com/google/gwt/user/server/rpc/core/java/util/TreeMap_ServerCustomFieldSerializer.java
new file mode 100644
index 0000000..3bd896e
--- /dev/null
+++ b/user/src/com/google/gwt/user/server/rpc/core/java/util/TreeMap_ServerCustomFieldSerializer.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2011 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.user.server.rpc.core.java.util;
+
+import com.google.gwt.user.client.rpc.SerializationException;
+import com.google.gwt.user.client.rpc.SerializationStreamReader;
+import com.google.gwt.user.client.rpc.SerializationStreamWriter;
+import com.google.gwt.user.client.rpc.core.java.util.TreeMap_CustomFieldSerializer;
+import com.google.gwt.user.server.rpc.ServerCustomFieldSerializer;
+import com.google.gwt.user.server.rpc.impl.DequeMap;
+import com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader;
+
+import java.lang.reflect.Type;
+import java.util.Comparator;
+import java.util.TreeMap;
+
+/**
+ * Server-side Custom field serializer for {@link java.util.TreeMap}.
+ */
+@SuppressWarnings({"unchecked", "rawtypes"})
+public class TreeMap_ServerCustomFieldSerializer extends ServerCustomFieldSerializer<TreeMap> {
+
+  public static void deserialize(ServerSerializationStreamReader streamReader, TreeMap instance,
+      Class<?> instanceClass, DequeMap<Type, Type> resolvedTypes) throws SerializationException {
+    Map_ServerCustomFieldSerializerBase.deserialize(streamReader, instance, instanceClass,
+        resolvedTypes);
+  }
+
+  @SuppressWarnings("unused")
+  public static TreeMap instantiate(ServerSerializationStreamReader streamReader,
+      Class<?> instanceClass, DequeMap<Type, Type> resolvedTypes) throws SerializationException {
+    return new TreeMap((Comparator) streamReader.readObject(Comparator.class, resolvedTypes));
+  }
+
+  @Override
+  public void deserializeInstance(SerializationStreamReader streamReader, TreeMap instance)
+      throws SerializationException {
+    TreeMap_CustomFieldSerializer.deserialize(streamReader, instance);
+  }
+
+  @Override
+  public void deserializeInstance(ServerSerializationStreamReader streamReader, TreeMap instance,
+      Class<?> instanceClass, DequeMap<Type, Type> resolvedTypes) throws SerializationException {
+    deserialize(streamReader, instance, instanceClass, resolvedTypes);
+  }
+
+  @Override
+  public boolean hasCustomInstantiateInstance() {
+    return true;
+  }
+
+  @Override
+  public TreeMap instantiateInstance(SerializationStreamReader streamReader)
+      throws SerializationException {
+    return TreeMap_CustomFieldSerializer.instantiate(streamReader);
+  }
+
+  @Override
+  public TreeMap instantiateInstance(ServerSerializationStreamReader streamReader,
+      Class<?> instanceClass, DequeMap<Type, Type> resolvedTypes)
+      throws SerializationException {
+    return instantiate(streamReader, instanceClass, resolvedTypes);
+  }
+
+  @Override
+  public void serializeInstance(SerializationStreamWriter streamWriter, TreeMap instance)
+      throws SerializationException {
+    TreeMap_CustomFieldSerializer.serialize(streamWriter, instance);
+  }
+}
diff --git a/user/src/com/google/gwt/user/server/rpc/core/java/util/TreeSet_ServerCustomFieldSerializer.java b/user/src/com/google/gwt/user/server/rpc/core/java/util/TreeSet_ServerCustomFieldSerializer.java
new file mode 100644
index 0000000..311ec91
--- /dev/null
+++ b/user/src/com/google/gwt/user/server/rpc/core/java/util/TreeSet_ServerCustomFieldSerializer.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2011 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.user.server.rpc.core.java.util;
+
+import com.google.gwt.user.client.rpc.SerializationException;
+import com.google.gwt.user.client.rpc.SerializationStreamReader;
+import com.google.gwt.user.client.rpc.SerializationStreamWriter;
+import com.google.gwt.user.client.rpc.core.java.util.Collection_CustomFieldSerializerBase;
+import com.google.gwt.user.client.rpc.core.java.util.TreeSet_CustomFieldSerializer;
+import com.google.gwt.user.server.rpc.ServerCustomFieldSerializer;
+import com.google.gwt.user.server.rpc.impl.DequeMap;
+import com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader;
+
+import java.lang.reflect.Type;
+import java.util.Comparator;
+import java.util.TreeSet;
+
+/**
+ * Custom field serializer for {@link java.util.TreeMap}.
+ */
+@SuppressWarnings({"unchecked", "rawtypes"})
+public class TreeSet_ServerCustomFieldSerializer extends ServerCustomFieldSerializer<TreeSet> {
+
+  public static void deserialize(ServerSerializationStreamReader streamReader, TreeSet instance,
+      Class<?> instanceClass, DequeMap<Type, Type> resolvedTypes) throws SerializationException {
+    Collection_ServerCustomFieldSerializerBase.deserialize(streamReader, instance, instanceClass,
+        resolvedTypes);
+  }
+
+  @SuppressWarnings({"cast", "unused"})
+  public static TreeSet instantiate(ServerSerializationStreamReader streamReader,
+      Class<?> instanceClass, DequeMap<Type, Type> resolvedTypes) throws SerializationException {
+    return new TreeSet((Comparator) streamReader.readObject((Type) Comparator.class,
+        resolvedTypes));
+  }
+
+  @Override
+  public void deserializeInstance(SerializationStreamReader streamReader, TreeSet instance)
+      throws SerializationException {
+    Collection_CustomFieldSerializerBase.deserialize(streamReader, instance);
+  }
+
+  @Override
+  public void deserializeInstance(ServerSerializationStreamReader streamReader, TreeSet instance,
+      Class<?> instanceClass, DequeMap<Type, Type> resolvedTypes) throws SerializationException {
+    deserialize(streamReader, instance, instanceClass, resolvedTypes);
+  }
+
+  @Override
+  public boolean hasCustomInstantiateInstance() {
+    return true;
+  }
+
+  @Override
+  public TreeSet instantiateInstance(SerializationStreamReader streamReader)
+      throws SerializationException {
+    return TreeSet_CustomFieldSerializer.instantiate(streamReader);
+  }
+
+  @Override
+  public TreeSet instantiateInstance(ServerSerializationStreamReader streamReader,
+      Class<?> instanceClass, DequeMap<Type, Type> resolvedTypes) throws SerializationException {
+    return instantiate(streamReader, instanceClass, resolvedTypes);
+  }
+
+  @Override
+  public void serializeInstance(SerializationStreamWriter streamWriter, TreeSet instance)
+      throws SerializationException {
+    TreeSet_CustomFieldSerializer.serialize(streamWriter, instance);
+  }
+}
diff --git a/user/src/com/google/gwt/user/server/rpc/core/java/util/Vector_ServerCustomFieldSerializer.java b/user/src/com/google/gwt/user/server/rpc/core/java/util/Vector_ServerCustomFieldSerializer.java
new file mode 100644
index 0000000..e1e460b
--- /dev/null
+++ b/user/src/com/google/gwt/user/server/rpc/core/java/util/Vector_ServerCustomFieldSerializer.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2011 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.user.server.rpc.core.java.util;
+
+import com.google.gwt.user.client.rpc.SerializationException;
+import com.google.gwt.user.client.rpc.SerializationStreamReader;
+import com.google.gwt.user.client.rpc.SerializationStreamWriter;
+import com.google.gwt.user.client.rpc.core.java.util.Collection_CustomFieldSerializerBase;
+import com.google.gwt.user.server.rpc.ServerCustomFieldSerializer;
+import com.google.gwt.user.server.rpc.impl.DequeMap;
+import com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader;
+
+import java.lang.reflect.Type;
+import java.util.Vector;
+
+/**
+ * Custom field serializer for {@link java.util.Vector}.
+ */
+@SuppressWarnings("rawtypes")
+public final class Vector_ServerCustomFieldSerializer extends ServerCustomFieldSerializer<Vector> {
+
+  public static void deserialize(ServerSerializationStreamReader streamReader, Vector instance,
+      Class<?> instanceClass, DequeMap<Type, Type> resolvedTypes) throws SerializationException {
+    Collection_ServerCustomFieldSerializerBase.deserialize(streamReader, instance, instanceClass,
+        resolvedTypes);
+  }
+
+  @Override
+  public void deserializeInstance(SerializationStreamReader streamReader, Vector instance)
+      throws SerializationException {
+    Collection_CustomFieldSerializerBase.deserialize(streamReader, instance);
+  }
+
+  @Override
+  public void deserializeInstance(ServerSerializationStreamReader streamReader, Vector instance,
+      Class<?> instanceClass, DequeMap<Type, Type> resolvedTypes) throws SerializationException {
+    deserialize(streamReader, instance, instanceClass, resolvedTypes);
+  }
+
+  @Override
+  public void serializeInstance(SerializationStreamWriter streamWriter, Vector instance)
+      throws SerializationException {
+    Collection_CustomFieldSerializerBase.serialize(streamWriter, instance);
+  }
+}
diff --git a/user/src/com/google/gwt/user/server/rpc/core/java/util/logging/LogRecord_ServerCustomFieldSerializer.java b/user/src/com/google/gwt/user/server/rpc/core/java/util/logging/LogRecord_ServerCustomFieldSerializer.java
new file mode 100644
index 0000000..d89246a
--- /dev/null
+++ b/user/src/com/google/gwt/user/server/rpc/core/java/util/logging/LogRecord_ServerCustomFieldSerializer.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2011 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.user.server.rpc.core.java.util.logging;
+
+import com.google.gwt.core.client.impl.SerializableThrowable;
+import com.google.gwt.user.client.rpc.SerializationException;
+import com.google.gwt.user.client.rpc.SerializationStreamReader;
+import com.google.gwt.user.client.rpc.SerializationStreamWriter;
+import com.google.gwt.user.client.rpc.core.java.util.logging.LogRecord_CustomFieldSerializer;
+import com.google.gwt.user.server.rpc.ServerCustomFieldSerializer;
+import com.google.gwt.user.server.rpc.impl.DequeMap;
+import com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader;
+
+import java.lang.reflect.Type;
+import java.util.logging.LogRecord;
+
+/**
+ * Custom serializer for LogRecord.
+ */
+public class LogRecord_ServerCustomFieldSerializer extends ServerCustomFieldSerializer<LogRecord> {
+  @SuppressWarnings("unused")
+  public static void deserialize(ServerSerializationStreamReader streamReader, LogRecord instance,
+      Class<?> instanceClass, DequeMap<Type, Type> resolvedTypes) throws SerializationException {
+    String loggerName = streamReader.readString();
+    Long millis = streamReader.readLong();
+    Object throwable = streamReader.readObject(Throwable.class, resolvedTypes);
+
+    instance.setLoggerName(loggerName);
+    instance.setMillis(millis);
+    if (throwable != null && throwable instanceof SerializableThrowable) {
+      instance.setThrown(((SerializableThrowable) throwable).getThrowable());
+    }
+  }
+
+  @Override
+  public void deserializeInstance(SerializationStreamReader streamReader, LogRecord instance)
+      throws SerializationException {
+    LogRecord_CustomFieldSerializer.deserialize(streamReader, instance);
+  }
+
+  @Override
+  public void deserializeInstance(ServerSerializationStreamReader streamReader, LogRecord instance,
+      Class<?> instanceClass, DequeMap<Type, Type> resolvedTypes) throws SerializationException {
+    deserialize(streamReader, instance, instanceClass, resolvedTypes);
+  }
+
+  @Override
+  public boolean hasCustomInstantiateInstance() {
+    return true;
+  }
+
+  @Override
+  public LogRecord instantiateInstance(ServerSerializationStreamReader reader)
+      throws SerializationException {
+    return LogRecord_CustomFieldSerializer.instantiate(reader);
+  }
+
+  @Override
+  public void serializeInstance(SerializationStreamWriter writer, LogRecord lr)
+      throws SerializationException {
+    LogRecord_CustomFieldSerializer.serialize(writer, lr);
+  }
+}
diff --git a/user/src/com/google/gwt/user/server/rpc/impl/DequeMap.java b/user/src/com/google/gwt/user/server/rpc/impl/DequeMap.java
new file mode 100644
index 0000000..f8e6aeb
--- /dev/null
+++ b/user/src/com/google/gwt/user/server/rpc/impl/DequeMap.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2011 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.user.server.rpc.impl;
+
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.HashMap;
+
+/**
+ * MultiMap with stack semantics for the values.
+ * 
+ * Initial insertion of a given key results in a new stack containing the value.
+ * Subsequent puts for the same key add the value to the stack. Removing a key
+ * removes its value from the stack.
+ * 
+ * @param <K> The key to use in the map
+ * @param <V> The value to put in the deque
+ */
+public class DequeMap<K, V> {
+  /**
+   * The default initial capacity for the Deque objects.
+   */
+  static final int DEFAULT_INITIAL_DEQUE_CAPACITY = 3;
+
+  /**
+   * The default initial capacity for the Deque objects.
+   */
+  static final int DEFAULT_INITIAL_MAP_CAPACITY = 3;
+
+  /**
+   * The initial capacity for the Deque objects.
+   */
+  int dequeCapacity = DEFAULT_INITIAL_DEQUE_CAPACITY;
+
+  /**
+   * The map used to hold data.
+   * 
+   * Note that we do not extend map because we require control over the
+   * addition and removal of elements from the map.
+   */
+  HashMap<K, Deque<V>> map = null;
+
+  /**
+   * Constructs an empty <tt>DequeMap</tt> with the default initial capacities
+   * (3 and 3) and the default map load factor (0.75).
+   */
+  public DequeMap() {
+    this(DEFAULT_INITIAL_MAP_CAPACITY, DEFAULT_INITIAL_DEQUE_CAPACITY);
+  }
+
+  /**
+   * Constructs an empty <tt>DequeMap</tt> with the specified initial capacities
+   * and the default map load factor (0.75).
+   * 
+   * @param initialMapCapacity the initial map capacity.
+   * @param initialDequeCapacity the initial deque capacity.
+   * @throws IllegalArgumentException if the initial capacity is negative.
+   */
+  public DequeMap(int initialMapCapacity, int initialDequeCapacity) {
+    map = new HashMap<K, Deque<V>>(initialMapCapacity);
+    dequeCapacity = initialDequeCapacity;
+  }
+
+  /**
+   * Add a value for a key to the map.
+   * 
+   * If the key already exists, the value is added to the head of the queue at
+   * that key. Otherwise, a new queue containing the value is created and
+   * inserted into the map at the key.
+   * 
+   * @param key key with which the specified value is to be associated
+   * @param value the value to push for the key
+   */
+  public void add(K key, V value) {
+    Deque<V> deque = map.get(key);
+    if (deque == null) {
+      deque = new ArrayDeque<V>(dequeCapacity);
+      deque.addFirst(value);
+      map.put(key, deque);
+    } else {
+      deque.addFirst(value);
+    }
+  }
+
+  /**
+   * Get the most recent value for a key.
+   * 
+   * @param key key to get
+   * @return the value at the head of the stack for the key, or null if the key
+   *         is unknown.
+   */
+  public V get(K key) {
+    Deque<V> deque = map.get(key);
+    if (deque == null) {
+      return null;
+    }
+    return deque.peekFirst();
+  }
+
+  /**
+   * Remove the most recent value for a key.
+   * 
+   * @param key key to get
+   * @return the value at the head of the stack for the key, or null if the key
+   *         is unknown.
+   */
+  public V remove(K key) {
+    Deque<V> deque = map.get(key);
+    if (deque == null) {
+      return null;
+    }
+    return deque.pollFirst();
+  }
+}
diff --git a/user/src/com/google/gwt/user/server/rpc/impl/SerializabilityUtil.java b/user/src/com/google/gwt/user/server/rpc/impl/SerializabilityUtil.java
index 46cf9be..a66c332 100644
--- a/user/src/com/google/gwt/user/server/rpc/impl/SerializabilityUtil.java
+++ b/user/src/com/google/gwt/user/server/rpc/impl/SerializabilityUtil.java
@@ -17,15 +17,21 @@
 
 import com.google.gwt.core.client.GWT;
 import com.google.gwt.user.client.rpc.CustomFieldSerializer;
+import com.google.gwt.user.client.rpc.GwtTransient;
 import com.google.gwt.user.client.rpc.SerializationException;
 import com.google.gwt.user.client.rpc.SerializationStreamReader;
 import com.google.gwt.user.client.rpc.SerializationStreamWriter;
-import com.google.gwt.user.client.rpc.GwtTransient;
 import com.google.gwt.user.server.rpc.SerializationPolicy;
+import com.google.gwt.user.server.rpc.ServerCustomFieldSerializer;
 
 import java.io.UnsupportedEncodingException;
 import java.lang.reflect.Field;
+import java.lang.reflect.GenericArrayType;
 import java.lang.reflect.Modifier;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+import java.lang.reflect.WildcardType;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Comparator;
@@ -58,11 +64,13 @@
    * ClassLoader (and thus, this Map). Access must be synchronized.
    * 
    * NOTE: after synchronizing on this field, it's possible to additionally
-   * synchronize on {@link #classCustomSerializerCache} or
-   * {@link #classSerializableFieldsCache}, so be aware deadlock potential when
-   * changing this code.
+   * synchronize on {@link #classCustomSerializerCache},
+   * {@link #classSerializableFieldsCache} or
+   * {@link #classServerCustomSerializerCache}, so be aware deadlock potential
+   * when changing this code.
    */
-  private static final Map<Class<?>, String> classCRC32Cache = new IdentityHashMap<Class<?>, String>();
+  private static final Map<Class<?>, String> classCRC32Cache =
+      new IdentityHashMap<Class<?>, String>();
 
   /**
    * A permanent cache of all serializable fields on classes. This is safe to do
@@ -72,7 +80,8 @@
    * NOTE: to prevent deadlock, you may NOT synchronize {@link #classCRC32Cache}
    * after synchronizing on this field.
    */
-  private static final Map<Class<?>, Field[]> classSerializableFieldsCache = new IdentityHashMap<Class<?>, Field[]>();
+  private static final Map<Class<?>, Field[]> classSerializableFieldsCache =
+      new IdentityHashMap<Class<?>, Field[]>();
 
   /**
    * A permanent cache of all which classes onto custom field serializers. This
@@ -83,41 +92,61 @@
    * NOTE: to prevent deadlock, you may NOT synchronize {@link #classCRC32Cache}
    * after synchronizing on this field.
    */
-  private static final Map<Class<?>, Class<?>> classCustomSerializerCache = new IdentityHashMap<Class<?>, Class<?>>();
+  private static final Map<Class<?>, Class<?>> classCustomSerializerCache =
+      new IdentityHashMap<Class<?>, Class<?>>();
+
+  /**
+   * A permanent cache of all which classes onto server-side custom field
+   * serializers. This is safe to do because a Class is guaranteed not to change
+   * within the lifetime of a ClassLoader (and thus, this Map). Access must be
+   * synchronized.
+   * 
+   * NOTE: to prevent deadlock, you may NOT synchronize {@link #classCRC32Cache}
+   * after synchronizing on this field.
+   */
+  private static final Map<Class<?>, Class<?>> classServerCustomSerializerCache =
+      new IdentityHashMap<Class<?>, Class<?>>();
 
   /**
    * Map of {@link Class} objects to singleton instances of that
    * {@link CustomFieldSerializer}.
    */
-  private static final Map<Class<?>, CustomFieldSerializer<?>>
-      CLASS_TO_SERIALIZER_INSTANCE =
+  private static final Map<Class<?>, CustomFieldSerializer<?>> CLASS_TO_SERIALIZER_INSTANCE =
       new IdentityHashMap<Class<?>, CustomFieldSerializer<?>>();
 
+  private static final String JRE_SERVER_SERIALIZER_PACKAGE = "com.google.gwt.user.server.rpc.core";
   private static final String JRE_SERIALIZER_PACKAGE = "com.google.gwt.user.client.rpc.core";
 
   /**
-   * A re-usable, non-functional {@link CustomFieldSerializer} for when the
-   * Custom Field Serializer does not implement the
-   * {@link CustomFieldSerializer} interface.
+   * A re-usable, non-functional {@link ServerCustomFieldSerializer} for when
+   * the Server Custom Field Serializer does not implement the
+   * {@link ServerCustomFieldSerializer} interface.
    */
-  private static final CustomFieldSerializer<?> NO_SUCH_SERIALIZER =
-      new CustomFieldSerializer<Object>() {
+  private static final ServerCustomFieldSerializer<?> NO_SUCH_SERIALIZER =
+      new ServerCustomFieldSerializer<Object>() {
         @Override
-        public void deserializeInstance(SerializationStreamReader
-            streamReader, Object instance) {
+        public void deserializeInstance(SerializationStreamReader streamReader, Object instance) {
           throw new AssertionError("This should never be called.");
         }
 
         @Override
-        public void serializeInstance(SerializationStreamWriter streamWriter,
-            Object instance) {
+        public void deserializeInstance(ServerSerializationStreamReader streamReader,
+            Object instance, Class<?> instanceClass, DequeMap<Type, Type> resolvedTypes)
+            throws SerializationException {
+          throw new SerializationException("This should never be called.");
+        }
+
+        @Override
+        public void serializeInstance(SerializationStreamWriter streamWriter, Object instance) {
           throw new AssertionError("This should never be called.");
         }
       };
 
-  private static final Map<String, String> SERIALIZED_PRIMITIVE_TYPE_NAMES = new HashMap<String, String>();
+  private static final Map<String, String> SERIALIZED_PRIMITIVE_TYPE_NAMES =
+      new HashMap<String, String>();
 
-  private static final Set<Class<?>> TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES = new HashSet<Class<?>>();
+  private static final Set<Class<?>> TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES =
+      new HashSet<Class<?>>();
 
   static {
 
@@ -152,6 +181,7 @@
       Class<?> clazz = Class.forName("junit.framework.AssertionFailedError");
       TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES.add(clazz);
     } catch (ClassNotFoundException dontCare) {
+      // Empty because we don't care
     }
   }
 
@@ -159,8 +189,6 @@
    * Returns the fields of a particular class that can be considered for
    * serialization. The returned list will be sorted into a canonical order to
    * ensure consistent answers.
-   * 
-   * TODO: this method needs a better name, I think.
    */
   public static Field[] applyFieldSerializationPolicy(Class<?> clazz) {
     Field[] serializableFields;
@@ -177,8 +205,7 @@
         serializableFields = fieldList.toArray(new Field[fieldList.size()]);
 
         // sort the fields by name
-        Arrays.sort(serializableFields, 0, serializableFields.length,
-            FIELD_COMPARATOR);
+        Arrays.sort(serializableFields, 0, serializableFields.length, FIELD_COMPARATOR);
 
         classSerializableFieldsCache.put(clazz, serializableFields);
       }
@@ -188,7 +215,9 @@
 
   public static SerializedInstanceReference decodeSerializedInstanceReference(
       String encodedSerializedInstanceReference) {
-    final String[] components = encodedSerializedInstanceReference.split(SerializedInstanceReference.SERIALIZED_REFERENCE_SEPARATOR);
+    final String[] components =
+        encodedSerializedInstanceReference
+            .split(SerializedInstanceReference.SERIALIZED_REFERENCE_SEPARATOR);
     return new SerializedInstanceReference() {
       public String getName() {
         return components.length > 0 ? components[0] : "";
@@ -200,12 +229,102 @@
     };
   }
 
-  public static String encodeSerializedInstanceReference(Class<?> instanceType, SerializationPolicy policy) {
-    return instanceType.getName()
-        + SerializedInstanceReference.SERIALIZED_REFERENCE_SEPARATOR
+  public static String encodeSerializedInstanceReference(Class<?> instanceType,
+      SerializationPolicy policy) {
+    return instanceType.getName() + SerializedInstanceReference.SERIALIZED_REFERENCE_SEPARATOR
         + getSerializationSignature(instanceType, policy);
   }
-  
+
+  /**
+   * Return the concrete type that a generic type maps to, if known.
+   * 
+   * @param genericType The generic type to resolve.
+   * @param resolvedTypes A map of generic types to actual types.
+   * @return The actual type, which may be of any subclass of Type.
+   */
+  public static Type findActualType(Type genericType, DequeMap<Type, Type> resolvedTypes) {
+    Type result = genericType;
+    // Look for things that TypeVariables are mapped to, but stop if mapped
+    // to itself. We map a TypeVariable to itself when we wish to explicitly
+    // mark it as unmapped.
+    while (result instanceof TypeVariable<?> &&
+        resolvedTypes.get(result) != result &&
+        resolvedTypes.get(result) != null) {
+      result = resolvedTypes.get(result);
+    }
+
+    return result;
+  }
+
+  /**
+   * Attempt to find the actual types for the generic parameters of an instance,
+   * given the types we have resolved from the method signature.
+   * 
+   * @param instanceClass The instance for which we want actual generic
+   *          parameter types.
+   * @param resolvedTypes The types that have been resolved to actual values
+   * @return An array of types representing the actual declared types for the
+   *         parameters of the instanceClass. Some may be of type TypeVariable,
+   *         in which case they could not be resolved.
+   */
+  public static Type[] findInstanceParameters(Class<?> instanceClass,
+      DequeMap<Type, Type> resolvedTypes) {
+    TypeVariable<?>[] instanceTypes = instanceClass.getTypeParameters();
+    Type[] foundParameters = Arrays.copyOf(instanceTypes, instanceTypes.length, Type[].class);
+
+    if (resolvedTypes == null) {
+      return foundParameters;
+    }
+
+    for (int i = 0; i < foundParameters.length; ++i) {
+      // Check if we already know about this type.
+      Type capturedType = findActualType(foundParameters[i], resolvedTypes);
+      if (capturedType != foundParameters[i]) {
+        foundParameters[i] = capturedType;
+        continue;
+      }
+
+      if (instanceClass.getGenericSuperclass() != null) {
+        Type superParameter =
+            findInstanceParameter(instanceTypes[i], instanceClass.getGenericSuperclass(),
+                resolvedTypes);
+        if (!(superParameter instanceof TypeVariable)) {
+          foundParameters[i] = superParameter;
+          continue;
+        }
+      }
+
+      Type[] interfaceTypes = instanceClass.getGenericInterfaces();
+      for (Type interfaceType : interfaceTypes) {
+        Type interfaceParameter =
+            findInstanceParameter(instanceTypes[i], interfaceType, resolvedTypes);
+        if (!(interfaceParameter instanceof TypeVariable)) {
+          foundParameters[i] = interfaceParameter;
+          break;
+        }
+      }
+    }
+
+    return foundParameters;
+  }
+
+  /**
+   * Find the Class that a given type refers to.
+   * 
+   * @param type The type of interest
+   * @return The Class that type represents
+   */
+  public static Class<?> getClassFromType(Type type, DequeMap<Type, Type> resolvedTypes) {
+    Type actualType = findActualType(type, resolvedTypes);
+    if (actualType instanceof Class) {
+      return (Class<?>) actualType;
+    }
+    if (type instanceof ParameterizedType) {
+      return getClassFromType(((ParameterizedType) actualType).getRawType(), resolvedTypes);
+    }
+    return null;
+  }
+
   public static String getSerializationSignature(Class<?> instanceType,
       SerializationPolicy policy) {
     String result;
@@ -216,8 +335,7 @@
         try {
           generateSerializationSignature(instanceType, crc, policy);
         } catch (UnsupportedEncodingException e) {
-          throw new RuntimeException(
-              "Could not compute the serialization signature", e);
+          throw new RuntimeException("Could not compute the serialization signature", e);
         }
         result = Long.toString(crc.getValue());
         classCRC32Cache.put(instanceType, result);
@@ -236,8 +354,9 @@
 
   /**
    * Returns the {@link Class} which can serialize the given instance type, or
-   * <code>null</code> if this class has no custom field serializer. Note that
-   * arrays never have custom field serializers.
+   * <code>null</code> if this class has no custom field serializer.
+   * 
+   * Note that arrays never have custom field serializers.
    */
   public static Class<?> hasCustomFieldSerializer(Class<?> instanceType) {
     assert (instanceType != null);
@@ -249,7 +368,7 @@
     synchronized (classCustomSerializerCache) {
       result = classCustomSerializerCache.get(instanceType);
       if (result == null) {
-        result = computeHasCustomFieldSerializer(instanceType);
+        result = computeHasCustomFieldSerializer(instanceType, false);
         if (result == null) {
           /*
            * Use (result == instanceType) as a sentinel value when the class has
@@ -265,36 +384,147 @@
     return (result == instanceType) ? null : result;
   }
 
+  /**
+   * Returns the server-side {@link Class} which can serialize the given
+   * instance type, or <code>null</code> if this class has no type-checking
+   * custom field serializer.
+   * 
+   * Note that arrays never have custom field serializers.
+   */
+  public static Class<?> hasServerCustomFieldSerializer(Class<?> instanceType) {
+    assert (instanceType != null);
+    if (instanceType.isArray()) {
+      return null;
+    }
+
+    Class<?> result;
+    synchronized (classServerCustomSerializerCache) {
+      result = classServerCustomSerializerCache.get(instanceType);
+      if (result == null) {
+        result = computeHasCustomFieldSerializer(instanceType, true);
+        if (result == null) {
+          /*
+           * Use (result == instanceType) as a sentinel value when the class has
+           * no custom field serializer. We avoid using null as the sentinel
+           * value, because that would necessitate an additional containsKey()
+           * check in the most common case, inside the synchronized block.
+           */
+          result = instanceType;
+        }
+        classServerCustomSerializerCache.put(instanceType, result);
+      }
+    }
+    return (result == instanceType) ? null : result;
+  }
+
+  /**
+   * Remove all of the actual types that arose from the the given type.
+   * 
+   * This method should always be called after a corresponding call to
+   * resolveTypes.
+   * 
+   * @param methodType The type we wish to assign this instance to
+   * @param resolvedTypes The types that have been resolved to actual values
+   */
+  public static void releaseTypes(Type methodType, DequeMap<Type, Type> resolvedTypes) {
+    SerializabilityUtil.resolveTypesWorker(methodType, resolvedTypes, false);
+  }
+
+  /**
+   * Find all the actual types we can from the information in the given type,
+   * and put the mapping from TypeVariable objects to actual types into the
+   * resolved types map.
+   * 
+   * The method releaseTypes should always be called after a call to this
+   * method, unless the resolved types map is about to be discarded.
+   * 
+   * @param methodType The type we wish to assign this instance to
+   * @param resolvedTypes The types that have been resolved to actual values
+   */
+  public static void resolveTypes(Type methodType, DequeMap<Type, Type> resolvedTypes) {
+    SerializabilityUtil.resolveTypesWorker(methodType, resolvedTypes, true);
+  }
+
+  /**
+   * Determine whether or not an instance can be assigned to the type expected
+   * by a method.
+   * 
+   * @param instanceClass The instance to check
+   * @param expectedType The type we wish to assign this instance to
+   * @param resolvedTypes The types that have been resolved to actual values
+   * 
+   * @return True if the instance may be assigned to the type; otherwise false.
+   */
+  static boolean isInstanceAssignableToType(Class<?> instanceClass, Type expectedType,
+      DequeMap<Type, Type> resolvedTypes) {
+    if (expectedType == null) {
+      return true;
+    }
+    Type actualType = findActualType(expectedType, resolvedTypes);
+
+    if (actualType instanceof TypeVariable) {
+      Type[] typeVariableBounds = ((TypeVariable<?>) actualType).getBounds();
+      for (Type boundType : typeVariableBounds) {
+        if (!isInstanceAssignableToType(instanceClass, boundType, resolvedTypes)) {
+          return false;
+        }
+      }
+      return true;
+    }
+    if (actualType instanceof ParameterizedType) {
+      ParameterizedType paramType = (ParameterizedType) actualType;
+      if (!((Class<?>) paramType.getRawType()).isAssignableFrom(instanceClass)) {
+        return false;
+      }
+    } else if (actualType instanceof GenericArrayType) {
+      if (!(instanceClass.isArray())) {
+        return false;
+      }
+      Type expectedComponentType = ((GenericArrayType) actualType).getGenericComponentType();
+      Class<?> instanceComponentClass = instanceClass.getComponentType();
+      return isInstanceAssignableToType(instanceComponentClass, expectedComponentType,
+          resolvedTypes);
+    } else if (actualType instanceof WildcardType) {
+      if (!isInstanceAssignableToWildcard(instanceClass, (WildcardType) actualType,
+          resolvedTypes)) {
+        return false;
+      }
+    } else if (!((Class<?>) actualType).isAssignableFrom(instanceClass)) {
+      return false;
+    }
+
+    return true;
+  }
+
   static boolean isNotStaticTransientOrFinal(Field field) {
     /*
-     * Only serialize fields that are not static, transient (including @GwtTransient), or final.
+     * Only serialize fields that are not static, transient (including
+     * @GwtTransient), or final.
      */
     int fieldModifiers = field.getModifiers();
-    return !Modifier.isStatic(fieldModifiers)
-        && !Modifier.isTransient(fieldModifiers)
-        && !field.isAnnotationPresent(GwtTransient.class)
-        && !Modifier.isFinal(fieldModifiers);
+    return !Modifier.isStatic(fieldModifiers) && !Modifier.isTransient(fieldModifiers)
+        && !field.isAnnotationPresent(GwtTransient.class) && !Modifier.isFinal(fieldModifiers);
   }
 
   /**
    * Loads a {@link CustomFieldSerializer} from a class that may implement that
    * interface.
-   *
+   * 
    * @param customSerializerClass the Custom Field Serializer class
-   *
+   * 
    * @return an instance the class provided if it implements
    *         {@link CustomFieldSerializer} or {@code null} if it does not
-   *
-   * @throws SerializationException if the load process encounters an
-   *         unexpected problem
+   * 
+   * @throws SerializationException if the load process encounters an unexpected
+   *           problem
    */
-  static CustomFieldSerializer<?> loadCustomFieldSerializer(
-      final Class<?> customSerializerClass) throws SerializationException {
+  static CustomFieldSerializer<?> loadCustomFieldSerializer(final Class<?> customSerializerClass)
+      throws SerializationException {
     /**
      * Note that neither reading or writing to the CLASS_TO_SERIALIZER_INSTANCE
-     * is synchronized for performance reasons.  This could cause get misses,
-     * put misses and the same CustomFieldSerializer to be instantiated more
-     * than once, but none of these are critical operations as
+     * is synchronized for performance reasons. This could cause get misses, put
+     * misses and the same CustomFieldSerializer to be instantiated more than
+     * once, but none of these are critical operations as
      * CLASS_TO_SERIALIZER_INSTANCE is only a performance improving cache.
      */
     CustomFieldSerializer<?> customFieldSerializer =
@@ -302,8 +532,7 @@
     if (customFieldSerializer == null) {
       if (CustomFieldSerializer.class.isAssignableFrom(customSerializerClass)) {
         try {
-          customFieldSerializer =
-              (CustomFieldSerializer<?>) customSerializerClass.newInstance();
+          customFieldSerializer = (CustomFieldSerializer<?>) customSerializerClass.newInstance();
         } catch (InstantiationException e) {
           throw new SerializationException(e);
 
@@ -313,8 +542,7 @@
       } else {
         customFieldSerializer = NO_SUCH_SERIALIZER;
       }
-      CLASS_TO_SERIALIZER_INSTANCE.put(customSerializerClass,
-          customFieldSerializer);
+      CLASS_TO_SERIALIZER_INSTANCE.put(customSerializerClass, customFieldSerializer);
     }
     if (customFieldSerializer == NO_SUCH_SERIALIZER) {
       return null;
@@ -326,7 +554,8 @@
   /**
    * This method treats arrays in a special way.
    */
-  private static Class<?> computeHasCustomFieldSerializer(Class<?> instanceType) {
+  private static Class<?> computeHasCustomFieldSerializer(Class<?> instanceType,
+      Boolean typeChecked) {
     assert (instanceType != null);
     String qualifiedTypeName = instanceType.getName();
     /*
@@ -335,28 +564,51 @@
      * want to load classes through the
      * CompilingClassLoader$MultiParentClassLoader, not the system classloader.
      */
-    ClassLoader classLoader = GWT.isClient()
-        ? SerializabilityUtil.class.getClassLoader()
-        : Thread.currentThread().getContextClassLoader();
+    ClassLoader classLoader =
+        GWT.isClient() ? SerializabilityUtil.class.getClassLoader() : Thread.currentThread()
+            .getContextClassLoader();
+
+    if (typeChecked) {
+      /*
+       * Look for a server-specific version of the custom field serializer.
+       * Server-side versions do additional type checking before deserializing a
+       * class, offering protection against certain malicious attacks on the
+       * server via RPC.
+       */
+      String serverSerializerName = qualifiedTypeName + "_ServerCustomFieldSerializer";
+      serverSerializerName = serverSerializerName.replaceFirst("client", "server");
+      Class<?> serverCustomSerializer = getCustomFieldSerializer(classLoader, serverSerializerName);
+      if (serverCustomSerializer != null) {
+        return serverCustomSerializer;
+      }
+
+      // Try with the regular name
+      serverCustomSerializer =
+          getCustomFieldSerializer(classLoader, JRE_SERVER_SERIALIZER_PACKAGE + "."
+              + serverSerializerName);
+      if (serverCustomSerializer != null) {
+        return serverCustomSerializer;
+      }
+    }
+
+    // Look for client side serializers.
     String simpleSerializerName = qualifiedTypeName + "_CustomFieldSerializer";
-    Class<?> customSerializer = getCustomFieldSerializer(classLoader,
-        simpleSerializerName);
+    Class<?> customSerializer = getCustomFieldSerializer(classLoader, simpleSerializerName);
     if (customSerializer != null) {
       return customSerializer;
     }
 
     // Try with the regular name
-    Class<?> customSerializerClass = getCustomFieldSerializer(classLoader,
-        JRE_SERIALIZER_PACKAGE + "." + simpleSerializerName);
-    if (customSerializerClass != null) {
-      return customSerializerClass;
+    customSerializer =
+        getCustomFieldSerializer(classLoader, JRE_SERIALIZER_PACKAGE + "." + simpleSerializerName);
+    if (customSerializer != null) {
+      return customSerializer;
     }
 
     return null;
   }
 
-  private static boolean excludeImplementationFromSerializationSignature(
-      Class<?> instanceType) {
+  private static boolean excludeImplementationFromSerializationSignature(Class<?> instanceType) {
     if (TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES.contains(instanceType)) {
       return true;
     }
@@ -382,8 +634,75 @@
     }
   }
 
-  private static void generateSerializationSignature(Class<?> instanceType,
-      CRC32 crc, SerializationPolicy policy) throws UnsupportedEncodingException {
+  /**
+   * Attempt to find known type for TypeVariable type from an instance.
+   * 
+   * @param foundParameter The currently known parameter, which must be of type
+   *          TypeVariable
+   * @param instanceType The instance that we need to check for information
+   *          about the type
+   * @param resolvedTypes The map of known relationships between Type objects
+   * @return A new value for the foundParameter, if we find one
+   */
+  private static Type findInstanceParameter(Type foundParameter, Type instanceType,
+      DequeMap<Type, Type> resolvedTypes) {
+    // See what we know about the types that are matched to this type.
+
+    if (instanceType instanceof GenericArrayType) {
+      return findInstanceParameter(foundParameter, ((GenericArrayType) instanceType)
+          .getGenericComponentType(), resolvedTypes);
+    } else if (instanceType instanceof ParameterizedType) {
+      ParameterizedType paramType = (ParameterizedType) instanceType;
+      Type rawType = paramType.getRawType();
+
+      if (rawType instanceof Class) {
+        Class<?> rawClass = (Class<?>) rawType;
+        TypeVariable<?>[] classGenericTypes = rawClass.getTypeParameters();
+        Type[] actualTypes = paramType.getActualTypeArguments();
+
+        for (int i = 0; i < actualTypes.length; ++i) {
+          if (actualTypes[i] == foundParameter) {
+            // Check if we already know about this type.
+            Type capturedType = findActualType(classGenericTypes[i], resolvedTypes);
+            if (capturedType != classGenericTypes[i]) {
+              return capturedType;
+            }
+
+            if (rawClass.getGenericSuperclass() != null) {
+              Type superParameter =
+                  findInstanceParameter(classGenericTypes[i], rawClass.getGenericSuperclass(),
+                      resolvedTypes);
+              if (!(superParameter instanceof TypeVariable)) {
+                return superParameter;
+              }
+            }
+            Type[] rawInterfaces = rawClass.getGenericInterfaces();
+            for (Type interfaceType : rawInterfaces) {
+              Type interfaceParameter =
+                  findInstanceParameter(classGenericTypes[i], interfaceType, resolvedTypes);
+              if (!(interfaceParameter instanceof TypeVariable)) {
+                return interfaceParameter;
+              }
+            }
+          }
+        }
+      }
+    } else if (instanceType instanceof WildcardType) {
+      WildcardType wildcardType = (WildcardType) instanceType;
+      Type[] upperBounds = wildcardType.getUpperBounds();
+      for (Type boundType : upperBounds) {
+        Type wildcardParameter = findInstanceParameter(foundParameter, boundType, resolvedTypes);
+        if (!(wildcardParameter instanceof TypeVariable)) {
+          return wildcardParameter;
+        }
+      }
+    }
+
+    return foundParameter;
+  }
+
+  private static void generateSerializationSignature(Class<?> instanceType, CRC32 crc,
+      SerializationPolicy policy) throws UnsupportedEncodingException {
     crc.update(getSerializedTypeName(instanceType).getBytes(DEFAULT_ENCODING));
 
     if (excludeImplementationFromSerializationSignature(instanceType)) {
@@ -401,13 +720,12 @@
       for (Field field : fields) {
         assert (field != null);
         /**
-         * If clientFieldNames is non-null, use only the fields listed there
-         * to generate the signature.  Otherwise, use all known fields.
+         * If clientFieldNames is non-null, use only the fields listed there to
+         * generate the signature. Otherwise, use all known fields.
          */
         if ((clientFieldNames == null) || clientFieldNames.contains(field.getName())) {
           crc.update(field.getName().getBytes(DEFAULT_ENCODING));
-          crc.update(getSerializedTypeName(field.getType()).getBytes(
-              DEFAULT_ENCODING));
+          crc.update(getSerializedTypeName(field.getType()).getBytes(DEFAULT_ENCODING));
         }
       }
 
@@ -421,11 +739,142 @@
   private static Class<?> getCustomFieldSerializer(ClassLoader classLoader,
       String qualifiedSerialzierName) {
     try {
-      Class<?> customSerializerClass = Class.forName(qualifiedSerialzierName,
-          false, classLoader);
+      Class<?> customSerializerClass = Class.forName(qualifiedSerialzierName, false, classLoader);
       return customSerializerClass;
     } catch (ClassNotFoundException e) {
       return null;
     }
   }
+
+  /**
+   * Determine if an instance class may be assigned to a wildcard type.
+   * 
+   * @param instanceClass The instance class that we wish to assign
+   * @param wildcardType The wildcard type we wish to assign to
+   * @param resolvedTypes The type variables that we have actual types for
+   * @return True when the instance may be assigned to the wildcard; false
+   *         otherwise.
+   */
+  private static boolean isInstanceAssignableToWildcard(Class<?> instanceClass,
+      WildcardType wildcardType, DequeMap<Type, Type> resolvedTypes) {
+    Type[] lowerBounds = wildcardType.getLowerBounds();
+    for (Type type : lowerBounds) {
+      /* Require instance to be a superclass of type, or type itself. */
+      if (!isInstanceSuperOfType(instanceClass, type, resolvedTypes)) {
+        return false;
+      }
+    }
+
+    Type[] upperBounds = wildcardType.getUpperBounds();
+    for (Type type : upperBounds) {
+      /* Require instanceClass to be a subclass of type. */
+      if (!isInstanceAssignableToType(instanceClass, type, resolvedTypes)) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  /**
+   * Determine if an instance class is a superclass of a given type, in the
+   * generic wildcard sense of super.
+   * 
+   * @param instanceClass The instance class that we wish to assign
+   * @param boundType The type that must represent a subclass of instance class,
+   *          or instance class itself.
+   * @param resolvedTypes The type variables that we have actual types for
+   * @return True when the instance may be used in ? super boundType; false
+   *         otherwise.
+   */
+  private static boolean isInstanceSuperOfType(Class<?> instanceClass, Type boundType,
+      DequeMap<Type, Type> resolvedTypes) {
+    Class<?> boundClass = getClassFromType(boundType, resolvedTypes);
+    if (boundClass == null) {
+      // Can't verify against an unknown class, so just assume it's acceptable.
+      return true;
+    }
+
+    while (boundClass != null) {
+      if (instanceClass == boundClass) {
+        return true;
+      }
+      boundClass = boundClass.getSuperclass();
+    }
+
+    return false;
+  }
+
+  private static void resolveTypesWorker(Type methodType, DequeMap<Type, Type> resolvedTypes,
+      boolean addTypes) {
+    if (methodType instanceof GenericArrayType) {
+      SerializabilityUtil.resolveTypesWorker(((GenericArrayType) methodType)
+          .getGenericComponentType(), resolvedTypes, addTypes);
+    } else if (methodType instanceof ParameterizedType) {
+      ParameterizedType paramType = (ParameterizedType) methodType;
+      Type rawType = paramType.getRawType();
+      if (rawType instanceof Class) {
+        Class<?> rawClass = (Class<?>) paramType.getRawType();
+        TypeVariable<?>[] classGenericTypes = rawClass.getTypeParameters();
+        Type[] actualTypes = paramType.getActualTypeArguments();
+
+        for (int i = 0; i < actualTypes.length; ++i) {
+          TypeVariable<?> variableType = classGenericTypes[i];
+          if (addTypes) {
+            resolvedTypes.add(variableType, actualTypes[i]);
+          } else {
+            resolvedTypes.remove(variableType);
+          }
+        }
+
+        Class<?> superClass = rawClass.getSuperclass();
+        if (superClass != null) {
+          Type superGenericType = rawClass.getGenericSuperclass();
+          SerializabilityUtil.resolveTypesWorker(superGenericType, resolvedTypes, addTypes);
+        }
+
+        Type[] interfaceTypes = rawClass.getGenericInterfaces();
+        for (Type interfaceType : interfaceTypes) {
+          SerializabilityUtil.resolveTypesWorker(interfaceType, resolvedTypes, addTypes);
+        }
+      }
+    } else if (methodType instanceof WildcardType) {
+      WildcardType wildcardType = (WildcardType) methodType;
+      Type[] lowerBounds = wildcardType.getLowerBounds();
+      for (Type type : lowerBounds) {
+        SerializabilityUtil.resolveTypesWorker(type, resolvedTypes, addTypes);
+      }
+      Type[] upperBounds = wildcardType.getUpperBounds();
+      for (Type type : upperBounds) {
+        SerializabilityUtil.resolveTypesWorker(type, resolvedTypes, addTypes);
+      }
+    } else if (methodType instanceof TypeVariable) {
+      Type[] bounds = ((TypeVariable<?>) methodType).getBounds();
+      for (Type type : bounds) {
+        SerializabilityUtil.resolveTypesWorker(type, resolvedTypes, addTypes);
+      }
+    } else if (methodType instanceof Class) {
+      Class<?> classType = (Class<?>) methodType;
+      
+      // A type that is of instance Class, with TypeParameters, must be a raw
+      // class, so strip off any parameters in the map.
+      Type[] classParams = classType.getTypeParameters();
+      for (Type classParamType : classParams) {
+        if (addTypes) {
+          resolvedTypes.add(classParamType, classParamType);
+        } else {
+          resolvedTypes.remove(classParamType);
+        }
+      }
+
+      Type superGenericType = classType.getGenericSuperclass();
+      if (superGenericType != null) {
+        SerializabilityUtil.resolveTypesWorker(superGenericType, resolvedTypes, addTypes);
+      }
+
+      Type[] interfaceTypes = classType.getGenericInterfaces();
+      for (Type interfaceType : interfaceTypes) {
+        SerializabilityUtil.resolveTypesWorker(interfaceType, resolvedTypes, addTypes);
+      }
+    }
+  }
 }
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 f50faf8..080ebf3 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
@@ -18,11 +18,13 @@
 import com.google.gwt.user.client.rpc.CustomFieldSerializer;
 import com.google.gwt.user.client.rpc.IncompatibleRemoteServiceException;
 import com.google.gwt.user.client.rpc.SerializationException;
+import com.google.gwt.user.client.rpc.SerializedTypeViolationException;
 import com.google.gwt.user.client.rpc.impl.AbstractSerializationStreamReader;
 import com.google.gwt.user.server.Base64Utils;
 import com.google.gwt.user.server.rpc.RPC;
 import com.google.gwt.user.server.rpc.SerializationPolicy;
 import com.google.gwt.user.server.rpc.SerializationPolicyProvider;
+import com.google.gwt.user.server.rpc.ServerCustomFieldSerializer;
 
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
@@ -30,9 +32,11 @@
 import java.lang.reflect.Array;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
+import java.lang.reflect.GenericArrayType;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
+import java.lang.reflect.Type;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.IdentityHashMap;
@@ -44,8 +48,7 @@
  * For internal use only. Used for server call serialization. This class is
  * carefully matched with the client-side version.
  */
-public final class ServerSerializationStreamReader extends
-    AbstractSerializationStreamReader {
+public final class ServerSerializationStreamReader extends AbstractSerializationStreamReader {
 
   /**
    * Used to accumulate elements while deserializing array types. The generic
@@ -84,77 +87,78 @@
   private enum ValueReader {
     BOOLEAN {
       @Override
-      Object readValue(ServerSerializationStreamReader stream)
-          throws SerializationException {
+      Object readValue(ServerSerializationStreamReader stream) throws SerializationException {
         return stream.readBoolean();
       }
     },
     BYTE {
       @Override
-      Object readValue(ServerSerializationStreamReader stream)
-          throws SerializationException {
+      Object readValue(ServerSerializationStreamReader stream) throws SerializationException {
         return stream.readByte();
       }
     },
     CHAR {
       @Override
-      Object readValue(ServerSerializationStreamReader stream)
-          throws SerializationException {
+      Object readValue(ServerSerializationStreamReader stream) throws SerializationException {
         return stream.readChar();
       }
     },
     DOUBLE {
       @Override
-      Object readValue(ServerSerializationStreamReader stream)
-          throws SerializationException {
+      Object readValue(ServerSerializationStreamReader stream) throws SerializationException {
         return stream.readDouble();
       }
     },
     FLOAT {
       @Override
-      Object readValue(ServerSerializationStreamReader stream)
-          throws SerializationException {
+      Object readValue(ServerSerializationStreamReader stream) throws SerializationException {
         return stream.readFloat();
       }
     },
     INT {
       @Override
-      Object readValue(ServerSerializationStreamReader stream)
-          throws SerializationException {
+      Object readValue(ServerSerializationStreamReader stream) throws SerializationException {
         return stream.readInt();
       }
     },
     LONG {
       @Override
-      Object readValue(ServerSerializationStreamReader stream)
-          throws SerializationException {
+      Object readValue(ServerSerializationStreamReader stream) throws SerializationException {
         return stream.readLong();
       }
     },
     OBJECT {
       @Override
-      Object readValue(ServerSerializationStreamReader stream)
-          throws SerializationException {
+      Object readValue(ServerSerializationStreamReader stream) throws SerializationException {
         return stream.readObject();
       }
+
+      @Override
+      Object readValue(ServerSerializationStreamReader stream, Type expectedType,
+          DequeMap<Type, Type> resolvedTypes) throws SerializationException {
+        return stream.readObject(expectedType, resolvedTypes);
+      }
     },
     SHORT {
       @Override
-      Object readValue(ServerSerializationStreamReader stream)
-          throws SerializationException {
+      Object readValue(ServerSerializationStreamReader stream) throws SerializationException {
         return stream.readShort();
       }
     },
     STRING {
       @Override
-      Object readValue(ServerSerializationStreamReader stream)
-          throws SerializationException {
+      Object readValue(ServerSerializationStreamReader stream) throws SerializationException {
         return stream.readString();
       }
     };
 
-    abstract Object readValue(ServerSerializationStreamReader stream)
-        throws SerializationException;
+    abstract Object readValue(ServerSerializationStreamReader stream) throws SerializationException;
+
+    @SuppressWarnings("unused")
+    Object readValue(ServerSerializationStreamReader stream, Type expectedType,
+        DequeMap<Type, Type> resolvedTypes) throws SerializationException {
+      return readValue(stream);
+    }
   }
 
   /**
@@ -253,6 +257,12 @@
       }
 
       @Override
+      protected Object readSingleValue(ServerSerializationStreamReader stream, Type expectedType,
+          DequeMap<Type, Type> resolvedTypes) throws SerializationException {
+        return stream.readObject(expectedType, resolvedTypes);
+      }
+
+      @Override
       protected void setSingleValue(Object array, int index, Object value) {
         Array.set(array, index, value);
       }
@@ -282,8 +292,14 @@
       }
     };
 
-    protected abstract Object readSingleValue(
-        ServerSerializationStreamReader stream) throws SerializationException;
+    protected abstract Object readSingleValue(ServerSerializationStreamReader stream)
+        throws SerializationException;
+
+    @SuppressWarnings("unused")
+    protected Object readSingleValue(ServerSerializationStreamReader stream, Type expectedType,
+        DequeMap<Type, Type> resolvedTypes) throws SerializationException {
+      return readSingleValue(stream);
+    }
 
     protected abstract void setSingleValue(Object array, int index, Object value);
 
@@ -294,9 +310,8 @@
     protected Object toArray(Class<?> componentType, BoundedList<Object> buffer)
         throws SerializationException {
       if (buffer.getExpectedSize() != buffer.size()) {
-        throw new SerializationException(
-            "Inconsistent number of elements received. Received "
-                + buffer.size() + " but expecting " + buffer.getExpectedSize());
+        throw new SerializationException("Inconsistent number of elements received. Received "
+            + buffer.size() + " but expecting " + buffer.getExpectedSize());
       }
 
       Object arr = Array.newInstance(componentType, buffer.size());
@@ -308,25 +323,36 @@
       return arr;
     }
 
-    Object read(ServerSerializationStreamReader stream,
-        BoundedList<Object> instance) throws SerializationException {
+    Object read(ServerSerializationStreamReader stream, BoundedList<Object> instance)
+        throws SerializationException {
       for (int i = 0, n = instance.getExpectedSize(); i < n; ++i) {
         instance.add(readSingleValue(stream));
       }
 
       return toArray(instance.getComponentType(), instance);
     }
+
+    Object read(ServerSerializationStreamReader stream, BoundedList<Object> instance,
+        Type expectedType, DequeMap<Type, Type> resolvedTypes) throws SerializationException {
+      for (int i = 0, n = instance.getExpectedSize(); i < n; ++i) {
+        instance.add(readSingleValue(stream, expectedType, resolvedTypes));
+      }
+
+      return toArray(instance.getComponentType(), instance);
+    }
   }
 
   /**
    * Map of {@link Class} objects to {@link ValueReader}s.
    */
-  private static final Map<Class<?>, ValueReader> CLASS_TO_VALUE_READER = new IdentityHashMap<Class<?>, ValueReader>();
+  private static final Map<Class<?>, ValueReader> CLASS_TO_VALUE_READER =
+      new IdentityHashMap<Class<?>, ValueReader>();
 
   /**
    * Map of {@link Class} objects to {@link VectorReader}s.
    */
-  private static final Map<Class<?>, VectorReader> CLASS_TO_VECTOR_READER = new IdentityHashMap<Class<?>, VectorReader>();
+  private static final Map<Class<?>, VectorReader> CLASS_TO_VECTOR_READER =
+      new IdentityHashMap<Class<?>, VectorReader>();
 
   private final ClassLoader classLoader;
 
@@ -335,10 +361,11 @@
   private final SerializationPolicyProvider serializationPolicyProvider;
 
   /**
-   * Used to look up setter methods of the form 'void Class.setXXX(T value)' given a
-   * Class type and a field name XXX corresponding to a field of type T.
+   * Used to look up setter methods of the form 'void Class.setXXX(T value)'
+   * given a Class type and a field name XXX corresponding to a field of type T.
    */
-  private final Map<Class<?>, Map<String, Method>> settersByClass = new HashMap<Class<?>, Map<String, Method>>();
+  private final Map<Class<?>, Map<String, Method>> settersByClass =
+      new HashMap<Class<?>, Map<String, Method>>();
 
   private String[] stringTable;
 
@@ -376,8 +403,8 @@
     this.serializationPolicyProvider = serializationPolicyProvider;
   }
 
-  public Object deserializeValue(Class<?> type) throws SerializationException {
-    ValueReader valueReader = CLASS_TO_VALUE_READER.get(type);
+  public Object deserializeValue(Class<?> rpcType) throws SerializationException {
+    ValueReader valueReader = CLASS_TO_VALUE_READER.get(rpcType);
     if (valueReader != null) {
       return valueReader.readValue(this);
     } else {
@@ -386,6 +413,17 @@
     }
   }
 
+  public Object deserializeValue(Class<?> rpcType, Type methodType,
+      DequeMap<Type, Type> resolvedTypes) throws SerializationException {
+    ValueReader valueReader = CLASS_TO_VALUE_READER.get(rpcType);
+    if (valueReader != null) {
+      return valueReader.readValue(this, methodType, resolvedTypes);
+    } else {
+      // Arrays of primitive or reference types need to go through readObject.
+      return ValueReader.OBJECT.readValue(this, methodType, resolvedTypes);
+    }
+  }
+
   public int getNumberOfTokens() {
     return tokenList.size();
   }
@@ -410,21 +448,18 @@
       // Didn't find any separator, assume an older version with different
       // separators and get the version as the sequence of digits at the
       // beginning of the encoded string.
-      while (idx < encodedTokens.length()
-          && Character.isDigit(encodedTokens.charAt(idx))) {
+      while (idx < encodedTokens.length() && Character.isDigit(encodedTokens.charAt(idx))) {
         ++idx;
       }
       if (idx == 0) {
         throw new IncompatibleRemoteServiceException(
             "Malformed or old RPC message received - expecting version between "
-                + SERIALIZATION_STREAM_MIN_VERSION + " and "
-                + SERIALIZATION_STREAM_VERSION);
+                + SERIALIZATION_STREAM_MIN_VERSION + " and " + SERIALIZATION_STREAM_VERSION);
       } else {
         int version = Integer.valueOf(encodedTokens.substring(0, idx));
         throw new IncompatibleRemoteServiceException("Expecting version between "
-            + SERIALIZATION_STREAM_MIN_VERSION + " and "
-            + SERIALIZATION_STREAM_VERSION + " from client, got " + version
-            + ".");
+            + SERIALIZATION_STREAM_MIN_VERSION + " and " + SERIALIZATION_STREAM_VERSION
+            + " from client, got " + version + ".");
       }
     }
 
@@ -434,15 +469,14 @@
     if (getVersion() < SERIALIZATION_STREAM_MIN_VERSION
         || getVersion() > SERIALIZATION_STREAM_VERSION) {
       throw new IncompatibleRemoteServiceException("Expecting version between "
-          + SERIALIZATION_STREAM_MIN_VERSION + " and "
-          + SERIALIZATION_STREAM_VERSION + " from client, got " + getVersion()
-          + ".");
+          + SERIALIZATION_STREAM_MIN_VERSION + " and " + SERIALIZATION_STREAM_VERSION
+          + " from client, got " + getVersion() + ".");
     }
-    
+
     // Check the flags
     if (!areFlagsValid()) {
-      throw new IncompatibleRemoteServiceException("Got an unknown flag from "
-          + "client: " + getFlags());
+      throw new IncompatibleRemoteServiceException("Got an unknown flag from " + "client: "
+          + getFlags());
     }
 
     // Read the type name table
@@ -452,12 +486,11 @@
     String moduleBaseURL = readString();
     String strongName = readString();
     if (serializationPolicyProvider != null) {
-      serializationPolicy = serializationPolicyProvider.getSerializationPolicy(
-          moduleBaseURL, strongName);
+      serializationPolicy =
+          serializationPolicyProvider.getSerializationPolicy(moduleBaseURL, strongName);
 
       if (serializationPolicy == null) {
-        throw new NullPointerException(
-            "serializationPolicyProvider.getSerializationPolicy()");
+        throw new NullPointerException("serializationPolicyProvider.getSerializationPolicy()");
       }
     }
   }
@@ -471,8 +504,7 @@
     try {
       return Byte.parseByte(value);
     } catch (NumberFormatException e) {
-      throw getNumberFormatException(value, "byte",
-          Byte.MIN_VALUE, Byte.MAX_VALUE);
+      throw getNumberFormatException(value, "byte", Byte.MIN_VALUE, Byte.MAX_VALUE);
     }
   }
 
@@ -494,8 +526,7 @@
     try {
       return Integer.parseInt(value);
     } catch (NumberFormatException e) {
-      throw getNumberFormatException(value, "int",
-          Integer.MIN_VALUE, Integer.MAX_VALUE);
+      throw getNumberFormatException(value, "int", Integer.MIN_VALUE, Integer.MAX_VALUE);
     }
   }
 
@@ -507,13 +538,32 @@
     }
   }
 
+  public Object readObject(Type expectedType, DequeMap<Type, Type> resolvedTypes)
+      throws SerializationException {
+    int token = readInt();
+
+    if (token < 0) {
+      // Negative means a previous object
+      // Transform negative 1-based to 0-based.
+      return getDecodedObject(-token);
+    }
+
+    // Positive means a new object
+    String typeSignature = getString(token);
+    if (typeSignature == null) {
+      // a null string means a null instance
+      return null;
+    }
+
+    return deserialize(typeSignature, expectedType, resolvedTypes);
+  }
+
   public short readShort() throws SerializationException {
     String value = extract();
     try {
       return Short.parseShort(value);
     } catch (NumberFormatException e) {
-      throw getNumberFormatException(value, "short",
-          Short.MIN_VALUE, Short.MAX_VALUE);
+      throw getNumberFormatException(value, "short", Short.MIN_VALUE, Short.MAX_VALUE);
     }
   }
 
@@ -522,8 +572,12 @@
   }
 
   @Override
-  protected Object deserialize(String typeSignature)
-      throws SerializationException {
+  protected Object deserialize(String typeSignature) throws SerializationException {
+    return deserialize(typeSignature, null, null);
+  }
+
+  protected Object deserialize(String typeSignature, Type expectedType,
+      DequeMap<Type, Type> resolvedTypes) throws SerializationException {
     Object instance = null;
     try {
       Class<?> instanceClass;
@@ -535,30 +589,53 @@
         } else {
           throw new SerializationException(
               "The GWT module was compiled with RPC type name elision enabled, but "
-                  + getSerializationPolicy().getClass().getName()
-                  + " does not implement " + TypeNameObfuscator.class.getName());
+                  + getSerializationPolicy().getClass().getName() + " does not implement "
+                  + TypeNameObfuscator.class.getName());
         }
       } else {
-        SerializedInstanceReference serializedInstRef = SerializabilityUtil.decodeSerializedInstanceReference(typeSignature);
-        instanceClass = Class.forName(serializedInstRef.getName(), false,
-            classLoader);
+        SerializedInstanceReference serializedInstRef =
+            SerializabilityUtil.decodeSerializedInstanceReference(typeSignature);
+        instanceClass = Class.forName(serializedInstRef.getName(), false, classLoader);
         validateTypeVersions(instanceClass, serializedInstRef);
       }
 
+      if (resolvedTypes == null) {
+        // We can find ourselves with a null resolvedTypes map if a class
+        // has a non-type-checking serializer that tries to deserialize a field.
+        // In such cases there is field type information from the class, but no
+        // resolvedTypes because we did not pass it through the non-type
+        // checking serializer. Create a map, so that from this point forward
+        // we have some way of type checking.
+        resolvedTypes = new DequeMap<Type, Type>();
+      }
+      
+      if (expectedType != null) {
+        SerializabilityUtil.resolveTypes(expectedType, resolvedTypes);
+        if (!SerializabilityUtil.isInstanceAssignableToType(instanceClass, expectedType,
+            resolvedTypes)) {
+          throw new SerializedTypeViolationException("Attempt to deserialize an object of type "
+              + instanceClass.toString() + " when an object of type "
+              + SerializabilityUtil.findActualType(expectedType, resolvedTypes).toString()
+              + " is expected");
+        }
+      }
+
       assert (serializationPolicy != null);
 
       serializationPolicy.validateDeserialize(instanceClass);
 
-      Class<?> customSerializer = SerializabilityUtil.hasCustomFieldSerializer(instanceClass);
+      Class<?> customSerializer = SerializabilityUtil.hasServerCustomFieldSerializer(instanceClass);
 
       int index = reserveDecodedObjectIndex();
 
-      instance = instantiate(customSerializer, instanceClass);
+      instance = instantiate(customSerializer, instanceClass, expectedType, resolvedTypes);
 
       rememberDecodedObject(index, instance);
 
-      Object replacement = deserializeImpl(customSerializer, instanceClass,
-          instance);
+      Object replacement =
+          deserializeImpl(customSerializer, instanceClass, instance, expectedType, resolvedTypes);
+
+      SerializabilityUtil.releaseTypes(expectedType, resolvedTypes);
 
       // It's possible that deserializing an object requires the original proxy
       // object to be replaced.
@@ -599,8 +676,8 @@
    * Deserialize an instance that is an array. Will default to deserializing as
    * an Object vector if the instance is not a primitive vector.
    * 
-   * @param instanceClass
-   * @param instance
+   * @param instanceClass the class we are deserializing
+   * @param instance the object to deserialize into
    * @throws SerializationException
    */
   @SuppressWarnings("unchecked")
@@ -617,8 +694,33 @@
     }
   }
 
-  private void deserializeClass(Class<?> instanceClass, Object instance)
-      throws SerializationException, IllegalAccessException,
+  /**
+   * Deserialize an instance that is an array, with type checking.
+   * 
+   * Will default to deserializing as an Object vector if the instance is not a
+   * primitive vector.
+   * 
+   * @param instanceClass the class we are deserializing
+   * @param instance the object to deserialize into
+   * @param expectedType the parameterized type the system is expected to find
+   * @throws SerializationException
+   */
+  @SuppressWarnings("unchecked")
+  private Object deserializeArray(Class<?> instanceClass, Object instance, Type expectedType,
+      DequeMap<Type, Type> resolvedTypes) throws SerializationException {
+    assert (instanceClass.isArray());
+
+    BoundedList<Object> buffer = (BoundedList<Object>) instance;
+    VectorReader instanceReader = CLASS_TO_VECTOR_READER.get(instanceClass);
+    if (instanceReader != null) {
+      return instanceReader.read(this, buffer, expectedType, resolvedTypes);
+    } else {
+      return VectorReader.OBJECT_VECTOR.read(this, buffer, expectedType, resolvedTypes);
+    }
+  }
+
+  private void deserializeClass(Class<?> instanceClass, Object instance, Type expectedType,
+      DequeMap<Type, Type> resolvedTypes) throws SerializationException, IllegalAccessException,
       NoSuchMethodException, InvocationTargetException, ClassNotFoundException {
     /**
      * A map from field names to corresponding setter methods. The reference
@@ -628,10 +730,11 @@
     Map<String, Method> setters = null;
 
     /**
-     * A list of fields of this class known to the client. If null, assume the class is not
-     * enhanced and don't attempt to deal with server-only fields.
+     * A list of fields of this class known to the client. If null, assume the
+     * class is not enhanced and don't attempt to deal with server-only fields.
      */
-    Set<String> clientFieldNames = serializationPolicy.getClientFieldNamesForEnhancedClass(instanceClass);
+    Set<String> clientFieldNames =
+        serializationPolicy.getClientFieldNamesForEnhancedClass(instanceClass);
     if (clientFieldNames != null) {
       // Read and set server-only instance fields encoded in the RPC data
       try {
@@ -662,12 +765,12 @@
     Field[] serializableFields = SerializabilityUtil.applyFieldSerializationPolicy(instanceClass);
     for (Field declField : serializableFields) {
       assert (declField != null);
-      if ((clientFieldNames != null)
-          && !clientFieldNames.contains(declField.getName())) {
+      if ((clientFieldNames != null) && !clientFieldNames.contains(declField.getName())) {
         continue;
       }
 
-      Object value = deserializeValue(declField.getType());
+      Type declGenericType = declField.getGenericType();
+      Object value = deserializeValue(declField.getType(), declGenericType, resolvedTypes);
 
       String fieldName = declField.getName();
       Method setter;
@@ -682,8 +785,7 @@
         setter.invoke(instance, value);
       } else {
         boolean isAccessible = declField.isAccessible();
-        boolean needsAccessOverride = !isAccessible
-            && !Modifier.isPublic(declField.getModifiers());
+        boolean needsAccessOverride = !isAccessible && !Modifier.isPublic(declField.getModifiers());
         if (needsAccessOverride) {
           // Override access restrictions
           declField.setAccessible(true);
@@ -695,33 +797,47 @@
 
     Class<?> superClass = instanceClass.getSuperclass();
     if (serializationPolicy.shouldDeserializeFields(superClass)) {
-      deserializeImpl(SerializabilityUtil.hasCustomFieldSerializer(superClass),
-          superClass, instance);
+      deserializeImpl(SerializabilityUtil.hasServerCustomFieldSerializer(superClass), superClass,
+          instance, expectedType, resolvedTypes);
     }
   }
 
-  private Object deserializeImpl(Class<?> customSerializer,
-      Class<?> instanceClass, Object instance) throws NoSuchMethodException,
-      IllegalArgumentException, IllegalAccessException,
+  private Object deserializeImpl(Class<?> customSerializer, Class<?> instanceClass,
+      Object instance, Type expectedType, DequeMap<Type, Type> resolvedTypes)
+      throws NoSuchMethodException, IllegalArgumentException, IllegalAccessException,
       InvocationTargetException, SerializationException, ClassNotFoundException {
 
     if (customSerializer != null) {
       @SuppressWarnings("unchecked")
       CustomFieldSerializer<Object> customFieldSerializer =
-          (CustomFieldSerializer<Object>)
-              SerializabilityUtil.loadCustomFieldSerializer(customSerializer);
+          (CustomFieldSerializer<Object>) SerializabilityUtil
+              .loadCustomFieldSerializer(customSerializer);
       if (customFieldSerializer == null) {
-        deserializeWithCustomFieldDeserializer(customSerializer, instanceClass,
-            instance);
+        deserializeWithCustomFieldDeserializer(customSerializer, instanceClass, instance,
+            resolvedTypes);
+      } else if (customFieldSerializer instanceof ServerCustomFieldSerializer) {
+        ServerCustomFieldSerializer<Object> serverCFS =
+            (ServerCustomFieldSerializer<Object>) customFieldSerializer;
+        serverCFS.deserializeInstance(this, instance, instanceClass, resolvedTypes);
       } else {
         customFieldSerializer.deserializeInstance(this, instance);
       }
     } else if (instanceClass.isArray()) {
-      instance = deserializeArray(instanceClass, instance);
+      if (expectedType == null) {
+        return deserializeArray(instanceClass, instance);
+      }
+      Type actualExpectedType = SerializabilityUtil.findActualType(expectedType, resolvedTypes);
+      if (actualExpectedType instanceof GenericArrayType) {
+        Type arrayType = ((GenericArrayType) actualExpectedType).getGenericComponentType();
+        return deserializeArray(instanceClass, instance, arrayType, resolvedTypes);
+      } else if (((Class<?>) actualExpectedType).getComponentType() != null) {
+        Class<?> arrayType = ((Class<?>) actualExpectedType).getComponentType();
+        return deserializeArray(instanceClass, instance, arrayType, resolvedTypes);
+      }
     } else if (instanceClass.isEnum()) {
       // Enums are deserialized when they are instantiated
     } else {
-      deserializeClass(instanceClass, instance);
+      deserializeClass(instanceClass, instance, expectedType, resolvedTypes);
     }
 
     return instance;
@@ -729,8 +845,7 @@
 
   private void deserializeStringTable() throws SerializationException {
     int typeNameCount = readInt();
-    BoundedList<String> buffer = new BoundedList<String>(String.class,
-        typeNameCount);
+    BoundedList<String> buffer = new BoundedList<String>(String.class, typeNameCount);
     for (int typeNameIndex = 0; typeNameIndex < typeNameCount; ++typeNameIndex) {
       String str = extract();
       // Change quoted characters back.
@@ -741,8 +856,7 @@
         while (idx >= 0) {
           buf.append(str.substring(pos, idx));
           if (++idx == str.length()) {
-            throw new SerializationException("Unmatched backslash: \"" + str
-                + "\"");
+            throw new SerializationException("Unmatched backslash: \"" + str + "\"");
           }
           char ch = str.charAt(idx);
           pos = idx + 1;
@@ -758,18 +872,17 @@
               break;
             case 'u':
               try {
-                ch = (char) Integer.parseInt(str.substring(idx + 1, idx + 5),
-                    16);
+                ch = (char) Integer.parseInt(str.substring(idx + 1, idx + 5), 16);
               } catch (NumberFormatException e) {
-                throw new SerializationException(
-                    "Invalid Unicode escape sequence in \"" + str + "\"");
+                throw new SerializationException("Invalid Unicode escape sequence in \"" + str
+                    + "\"");
               }
               buf.append(ch);
               pos += 4;
               break;
             default:
-              throw new SerializationException("Unexpected escape character "
-                  + ch + " after backslash: \"" + str + "\"");
+              throw new SerializationException("Unexpected escape character " + ch
+                  + " after backslash: \"" + str + "\"");
           }
           idx = str.indexOf('\\', pos);
         }
@@ -787,13 +900,18 @@
     stringTable = buffer.toArray(new String[buffer.getExpectedSize()]);
   }
 
-  private void deserializeWithCustomFieldDeserializer(
-      Class<?> customSerializer, Class<?> instanceClass, Object instance)
-      throws NoSuchMethodException, IllegalAccessException,
-      InvocationTargetException {
+  private void deserializeWithCustomFieldDeserializer(Class<?> customSerializer,
+      Class<?> instanceClass, Object instance, DequeMap<Type, Type> resolvedTypes)
+      throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
     assert (!instanceClass.isArray());
 
     for (Method method : customSerializer.getMethods()) {
+      if ("deserializeChecked".equals(method.getName())) {
+        method.invoke(null, this, instance, instanceClass, resolvedTypes);
+        return;
+      }
+    }
+    for (Method method : customSerializer.getMethods()) {
       if ("deserialize".equals(method.getName())) {
         method.invoke(null, this, instance);
         return;
@@ -809,20 +927,19 @@
       throw new SerializationException("Too few tokens in RPC request", e);
     }
   }
-  
+
   /**
-   * Returns a suitable NumberFormatException with an explanatory message
-   * when a numerical value cannot be parsed according to its expected
-   * type.
-   *  
+   * Returns a suitable NumberFormatException with an explanatory message when a
+   * numerical value cannot be parsed according to its expected type.
+   * 
    * @param value the value as read from the RPC stream
    * @param type the name of the expected type
    * @param minValue the smallest valid value for the expected type
    * @param maxValue the largest valid value for the expected type
    * @return a NumberFormatException with an explanatory message
    */
-  private NumberFormatException getNumberFormatException(String value,
-      String type, double minValue, double maxValue) {
+  private NumberFormatException getNumberFormatException(String value, String type,
+      double minValue, double maxValue) {
     String message = "a non-numerical value";
     try {
       // Check the field contents in order to produce a more comprehensible
@@ -834,10 +951,11 @@
         message = "a fractional value";
       }
     } catch (NumberFormatException e2) {
+      // Fall through with the default message.
     }
 
-    return new NumberFormatException("Expected type '" + type + "' but received " +
-        message + ": " + value);
+    return new NumberFormatException("Expected type '" + type + "' but received " + message + ": "
+        + value);
   }
 
   /**
@@ -847,7 +965,8 @@
    * @param instanceClass the class to query
    * @return a Map from Strings to Methods such that the name <code>XXX</code>
    *         (corresponding to the field <code>T XXX</code>) maps to the method
-   *         <code>void setXXX(T value)</code>, or null if no such method exists.
+   *         <code>void setXXX(T value)</code>, or null if no such method
+   *         exists.
    */
   private Map<String, Method> getSetters(Class<?> instanceClass) {
     synchronized (settersByClass) {
@@ -858,12 +977,12 @@
         // Iterate over each field and locate a suitable setter method
         Field[] fields = instanceClass.getDeclaredFields();
         for (Field field : fields) {
-          // Consider non-final, non-static, non-transient (or @GwtTransient) fields only
+          // Consider non-final, non-static, non-transient (or @GwtTransient)
+          // fields only
           if (SerializabilityUtil.isNotStaticTransientOrFinal(field)) {
             String fieldName = field.getName();
-            String setterName = "set"
-              + Character.toUpperCase(fieldName.charAt(0))
-              + fieldName.substring(1);
+            String setterName =
+                "set" + Character.toUpperCase(fieldName.charAt(0)) + fieldName.substring(1);
             try {
               Method setter = instanceClass.getMethod(setterName, field.getType());
               setters.put(fieldName, setter);
@@ -880,22 +999,28 @@
     }
   }
 
-  private Object instantiate(Class<?> customSerializer, Class<?> instanceClass)
-      throws InstantiationException, IllegalAccessException,
-      IllegalArgumentException, InvocationTargetException,
-      NoSuchMethodException, SerializationException {
+  private Object instantiate(Class<?> customSerializer, Class<?> instanceClass, Type expectedType,
+      DequeMap<Type, Type> resolvedTypes) throws InstantiationException, IllegalAccessException,
+      IllegalArgumentException, InvocationTargetException, NoSuchMethodException,
+      SerializationException {
     if (customSerializer != null) {
       CustomFieldSerializer<?> customFieldSerializer =
           SerializabilityUtil.loadCustomFieldSerializer(customSerializer);
       if (customFieldSerializer == null) {
-        for (Method method : customSerializer.getMethods()) {
-          if ("instantiate".equals(method.getName())) {
-            return method.invoke(null, this);
-          }
+        Object result = instantiateWithCustomFieldInstantiator(customSerializer, instanceClass, resolvedTypes);
+        if (result != null) {
+          return result;
         }
-        // Ok to not have one.
+        // Ok to not have a custom instantiate.
       } else if (customFieldSerializer.hasCustomInstantiateInstance()) {
-        return customFieldSerializer.instantiateInstance(this);
+        if (expectedType != null &&
+            (customFieldSerializer instanceof ServerCustomFieldSerializer)) {
+          ServerCustomFieldSerializer<?> serverCFS =
+              (ServerCustomFieldSerializer<?>) customFieldSerializer;
+          return serverCFS.instantiateInstance(this, instanceClass, resolvedTypes);
+        } else {
+          return customFieldSerializer.instantiateInstance(this);
+        }
       }
     }
 
@@ -915,21 +1040,35 @@
     }
   }
 
-  private void validateTypeVersions(Class<?> instanceClass,
-      SerializedInstanceReference serializedInstRef)
-      throws SerializationException {
-    String clientTypeSignature = serializedInstRef.getSignature();
-    if (clientTypeSignature.length() == 0) {
-      throw new SerializationException("Missing type signature for "
-          + instanceClass.getName());
+  private Object instantiateWithCustomFieldInstantiator(Class<?> customSerializer,
+      Class<?> instanceClass, DequeMap<Type, Type> resolvedTypes)
+      throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
+    for (Method method : customSerializer.getMethods()) {
+      if ("instantiateChecked".equals(method.getName())) {
+        return method.invoke(null, this, instanceClass, resolvedTypes);
+      }
     }
 
-    String serverTypeSignature = SerializabilityUtil.getSerializationSignature(
-        instanceClass, serializationPolicy);
+    for (Method method : customSerializer.getMethods()) {
+      if ("instantiate".equals(method.getName())) {
+        return method.invoke(null, this);
+      }
+    }
+    return null;
+  }
+
+  private void validateTypeVersions(Class<?> instanceClass,
+      SerializedInstanceReference serializedInstRef) throws SerializationException {
+    String clientTypeSignature = serializedInstRef.getSignature();
+    if (clientTypeSignature.length() == 0) {
+      throw new SerializationException("Missing type signature for " + instanceClass.getName());
+    }
+
+    String serverTypeSignature =
+        SerializabilityUtil.getSerializationSignature(instanceClass, serializationPolicy);
 
     if (!clientTypeSignature.equals(serverTypeSignature)) {
-      throw new SerializationException("Invalid type signature for "
-          + instanceClass.getName());
+      throw new SerializationException("Invalid type signature for " + instanceClass.getName());
     }
   }
 }
diff --git a/user/test/com/google/gwt/user/RPCSuite.gwt.xml b/user/test/com/google/gwt/user/RPCSuite.gwt.xml
index 938877b..2ce1a8a 100644
--- a/user/test/com/google/gwt/user/RPCSuite.gwt.xml
+++ b/user/test/com/google/gwt/user/RPCSuite.gwt.xml
@@ -49,5 +49,7 @@
     class='com.google.gwt.user.server.rpc.UnicodeEscapingServiceImpl' />
   <servlet path='/recursiveclass'
     class='com.google.gwt.user.server.rpc.RecursiveClassTestServiceImpl' />
+  <servlet path='/typecheckedobjects'
+    class='com.google.gwt.user.server.rpc.TypeCheckedObjectsTestServiceImpl' />
 
 </module>
diff --git a/user/test/com/google/gwt/user/RPCSuite.java b/user/test/com/google/gwt/user/RPCSuite.java
index 4252113..dc3f5e6 100644
--- a/user/test/com/google/gwt/user/RPCSuite.java
+++ b/user/test/com/google/gwt/user/RPCSuite.java
@@ -43,6 +43,7 @@
 import com.google.gwt.user.client.rpc.RecursiveClassTest;
 import com.google.gwt.user.client.rpc.RpcTokenTest;
 import com.google.gwt.user.client.rpc.RunTimeSerializationErrorsTest;
+import com.google.gwt.user.client.rpc.TypeCheckedObjectsTest;
 import com.google.gwt.user.client.rpc.UnicodeEscapingTest;
 import com.google.gwt.user.client.rpc.UnicodeEscapingTestWithTypeObfuscation;
 import com.google.gwt.user.client.rpc.ValueTypesTest;
@@ -112,6 +113,7 @@
     suite.addTestSuite(UnicodeEscapingTest.class);
     suite.addTestSuite(RunTimeSerializationErrorsTest.class);
     suite.addTestSuite(RecursiveClassTest.class);
+    suite.addTestSuite(TypeCheckedObjectsTest.class);
     suite.addTestSuite(XsrfProtectionTest.class);
 
     // This test turns on the type-elision feature of RPC
@@ -121,7 +123,8 @@
     suite.addTestSuite(CollectionsTestWithTypeObfuscation.class);
     suite.addTestSuite(CustomFieldSerializerTestWithTypeObfuscation.class);
     suite.addTestSuite(ObjectGraphTestWithTypeObfuscation.class);
-    suite.addTestSuite(com.google.gwt.user.client.rpc.RemoteServiceServletTestWithTypeObfuscation.class);
+    suite.addTestSuite(
+        com.google.gwt.user.client.rpc.RemoteServiceServletTestWithTypeObfuscation.class);
     suite.addTestSuite(UnicodeEscapingTestWithTypeObfuscation.class);
 
     // Client-side test cases for deRPC system
diff --git a/user/test/com/google/gwt/user/client/rpc/TestSetFactory.java b/user/test/com/google/gwt/user/client/rpc/TestSetFactory.java
index cdb4ea0..cae6954 100644
--- a/user/test/com/google/gwt/user/client/rpc/TestSetFactory.java
+++ b/user/test/com/google/gwt/user/client/rpc/TestSetFactory.java
@@ -57,8 +57,7 @@
     public boolean equals(Object obj) {
       if (obj instanceof MarkerBase && obj.getClass() == this.getClass()) {
         MarkerBase other = (MarkerBase) obj;
-        return value == other.value
-            || (value != null && value.equals(other.value));
+        return value == other.value || (value != null && value.equals(other.value));
       }
       return false;
     }
@@ -352,7 +351,7 @@
   }
 
   /**
-   * TODO: document me.
+   * Class to test serialization of cycles in the object graph.
    */
   public static class SerializableDoublyLinkedNode implements IsSerializable {
     protected String data;
@@ -432,7 +431,7 @@
   }
 
   /**
-   * TODO: document me.
+   * Serializable class used to test arrays that are referenced more than once.
    */
   public static class SerializableWithTwoArrays implements IsSerializable {
     String[] one;
@@ -440,16 +439,21 @@
   }
 
   /**
-   * TODO: document me.
+   * A non-serializable class for testing.
    */
   public static class UnserializableNode {
   }
 
-  static class ReverseSorter<T extends Comparable<T>> implements Comparator<T>,
-      Serializable {
+  /**
+   * A custom sorter for testing: it sorts in reverse order.
+   * 
+   * @param <T> The type to be sorted.
+   */
+  public static class ReverseSorter<T extends Comparable<T>> implements Comparator<T>,
+      IsSerializable {
 
     // for gwt-serialization
-    ReverseSorter() {
+    public ReverseSorter() {
     }
 
     public int compare(T a, T b) {
@@ -492,25 +496,25 @@
   }
 
   public static List<MarkerTypeArraysAsList> createArraysAsList() {
-    return Arrays.asList(new MarkerTypeArraysAsList("foo"),
-        new MarkerTypeArraysAsList("bar"), new MarkerTypeArraysAsList("baz"),
-        new MarkerTypeArraysAsList("bal"), new MarkerTypeArraysAsList("w00t"));
+    return Arrays.asList(new MarkerTypeArraysAsList("foo"), new MarkerTypeArraysAsList("bar"),
+        new MarkerTypeArraysAsList("baz"), new MarkerTypeArraysAsList("bal"),
+        new MarkerTypeArraysAsList("w00t"));
   }
 
   public static Boolean[] createBooleanArray() {
-    return new Boolean[] {
+    return new Boolean[]{
         Boolean.valueOf(true), Boolean.valueOf(false), Boolean.valueOf(true),
         Boolean.valueOf(false)};
   }
 
   public static Byte[] createByteArray() {
-    return new Byte[] {
-        new Byte(Byte.MAX_VALUE), new Byte(Byte.MIN_VALUE),
-        new Byte(Byte.MAX_VALUE), new Byte(Byte.MIN_VALUE)};
+    return new Byte[]{
+        new Byte(Byte.MAX_VALUE), new Byte(Byte.MIN_VALUE), new Byte(Byte.MAX_VALUE),
+        new Byte(Byte.MIN_VALUE)};
   }
 
   public static Character[] createCharArray() {
-    return new Character[] {
+    return new Character[]{
         new Character(Character.MAX_VALUE), new Character(Character.MIN_VALUE),
         new Character(Character.MAX_VALUE), new Character(Character.MIN_VALUE)};
   }
@@ -521,14 +525,13 @@
 
   @SuppressWarnings("deprecation")
   public static Date[] createDateArray() {
-    return new Date[] {
-        new Date(1992 - 1900, 9, 18), new Date(1997 - 1900, 6, 6)};
+    return new Date[]{new Date(1992 - 1900, 9, 18), new Date(1997 - 1900, 6, 6)};
   }
 
   public static Double[] createDoubleArray() {
-    return new Double[] {
-        new Double(Double.MAX_VALUE), new Double(Double.MIN_VALUE),
-        new Double(Double.MAX_VALUE), new Double(Double.MIN_VALUE)};
+    return new Double[]{
+        new Double(Double.MAX_VALUE), new Double(Double.MIN_VALUE), new Double(Double.MAX_VALUE),
+        new Double(Double.MIN_VALUE)};
   }
 
   public static List<MarkerTypeEmptyList> createEmptyList() {
@@ -544,14 +547,13 @@
   }
 
   public static Enum<?>[] createEnumArray() {
-    return new Enum<?>[] {
-        MarkerTypeEnum.A, MarkerTypeEnum.B, MarkerTypeEnum.C, MarkerTypeEnum.A,};
+    return new Enum<?>[]{MarkerTypeEnum.A, MarkerTypeEnum.B, MarkerTypeEnum.C, MarkerTypeEnum.A};
   }
 
   public static Float[] createFloatArray() {
-    return new Float[] {
-        new Float(Float.MAX_VALUE), new Float(Float.MIN_VALUE),
-        new Float(Float.MAX_VALUE), new Float(Float.MIN_VALUE)};
+    return new Float[]{
+        new Float(Float.MAX_VALUE), new Float(Float.MIN_VALUE), new Float(Float.MAX_VALUE),
+        new Float(Float.MIN_VALUE)};
   }
 
   public static HashMap<MarkerTypeHashMapKey, MarkerTypeHashMapValue> createHashMap() {
@@ -575,7 +577,8 @@
     return set;
   }
 
-  public static IdentityHashMap<MarkerTypeEnum, MarkerTypeIdentityHashMapValue> createIdentityHashMapEnumKey() {
+  public static IdentityHashMap<MarkerTypeEnum, MarkerTypeIdentityHashMapValue>
+  createIdentityHashMapEnumKey() {
     IdentityHashMap<MarkerTypeEnum, MarkerTypeIdentityHashMapValue> map =
         new IdentityHashMap<MarkerTypeEnum, MarkerTypeIdentityHashMapValue>();
     /*
@@ -588,7 +591,9 @@
     return map;
   }
 
-  public static IdentityHashMap<MarkerTypeIdentityHashMapKey, MarkerTypeIdentityHashMapValue> createIdentityHashMap() {
+  public static
+  IdentityHashMap<MarkerTypeIdentityHashMapKey, MarkerTypeIdentityHashMapValue>
+  createIdentityHashMap() {
     IdentityHashMap<MarkerTypeIdentityHashMapKey, MarkerTypeIdentityHashMapValue> map =
         new IdentityHashMap<MarkerTypeIdentityHashMapKey, MarkerTypeIdentityHashMapValue>();
     /*
@@ -606,12 +611,13 @@
   }
 
   public static Integer[] createIntegerArray() {
-    return new Integer[] {
+    return new Integer[]{
         new Integer(Integer.MAX_VALUE), new Integer(Integer.MIN_VALUE),
         new Integer(Integer.MAX_VALUE), new Integer(Integer.MIN_VALUE)};
   }
 
-  public static LinkedHashMap<MarkerTypeLinkedHashMapKey, MarkerTypeLinkedHashMapValue> createLinkedHashMap() {
+  public static
+  LinkedHashMap<MarkerTypeLinkedHashMapKey, MarkerTypeLinkedHashMapValue>createLinkedHashMap() {
     LinkedHashMap<MarkerTypeLinkedHashMapKey, MarkerTypeLinkedHashMapValue> map =
         new LinkedHashMap<MarkerTypeLinkedHashMapKey, MarkerTypeLinkedHashMapValue>();
     map.put(new MarkerTypeLinkedHashMapKey("foo"), new MarkerTypeLinkedHashMapValue("foo"));
@@ -646,21 +652,21 @@
     long a = 16123432898849345L;
     long b = 78234569989880099L;
     long c = -64289238928934943L;
-    
+
     // Create values that are not compile-time constants
     for (int i = 0; i < 10; i++) {
       a ^= b;
       b ^= c;
       c ^= a;
     }
-    
-    return new Long[] {
-        new Long(Long.MAX_VALUE), new Long(Long.MIN_VALUE),
-        new Long(Long.MAX_VALUE), new Long(Long.MIN_VALUE),
-        new Long(a), new Long(b), new Long(c)};
+
+    return new Long[]{
+        new Long(Long.MAX_VALUE), new Long(Long.MIN_VALUE), new Long(Long.MAX_VALUE),
+        new Long(Long.MIN_VALUE), new Long(a), new Long(b), new Long(c)};
   }
 
-  public static LinkedHashMap<MarkerTypeLinkedHashMapKey, MarkerTypeLinkedHashMapValue> createLRULinkedHashMap() {
+  public static LinkedHashMap<MarkerTypeLinkedHashMapKey, MarkerTypeLinkedHashMapValue>
+  createLRULinkedHashMap() {
     LinkedHashMap<MarkerTypeLinkedHashMapKey, MarkerTypeLinkedHashMapValue> map =
         new LinkedHashMap<MarkerTypeLinkedHashMapKey, MarkerTypeLinkedHashMapValue>(100, 1.0f, true);
     map.put(new MarkerTypeLinkedHashMapKey("foo"), new MarkerTypeLinkedHashMapValue("foo"));
@@ -672,56 +678,47 @@
   }
 
   public static boolean[] createPrimitiveBooleanArray() {
-    return new boolean[] {true, true, false, false, true, false};
+    return new boolean[]{true, true, false, false, true, false};
   }
 
   public static byte[] createPrimitiveByteArray() {
-    return new byte[] {
-        Byte.MAX_VALUE, Byte.MIN_VALUE, Byte.MAX_VALUE, Byte.MIN_VALUE};
+    return new byte[]{Byte.MAX_VALUE, Byte.MIN_VALUE, Byte.MAX_VALUE, Byte.MIN_VALUE};
   }
 
   public static char[] createPrimitiveCharArray() {
-    return new char[] {
-        Character.MAX_VALUE, Character.MIN_VALUE, Character.MAX_VALUE,
-        Character.MIN_VALUE};
+    return new char[]{
+        Character.MAX_VALUE, Character.MIN_VALUE, Character.MAX_VALUE, Character.MIN_VALUE};
   }
 
   public static double[] createPrimitiveDoubleArray() {
-    return new double[] {
-        Double.MAX_VALUE, Double.MIN_VALUE, Double.MAX_VALUE, Double.MIN_VALUE};
+    return new double[]{Double.MAX_VALUE, Double.MIN_VALUE, Double.MAX_VALUE, Double.MIN_VALUE};
   }
 
   public static float[] createPrimitiveFloatArray() {
-    return new float[] {
-        Float.MAX_VALUE, Float.MIN_VALUE, Float.MAX_VALUE, Float.MIN_VALUE};
+    return new float[]{Float.MAX_VALUE, Float.MIN_VALUE, Float.MAX_VALUE, Float.MIN_VALUE};
   }
 
   public static int[] createPrimitiveIntegerArray() {
-    return new int[] {
-        Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE,
-        Integer.MIN_VALUE};
+    return new int[]{Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE};
   }
 
   public static long[] createPrimitiveLongArray() {
     long a = 16123432898849345L;
     long b = 78234569989880099L;
     long c = -64289238928934943L;
-    
+
     // Create values that are not compile-time constants
     for (int i = 0; i < 10; i++) {
       a ^= b;
       b ^= c;
       c ^= a;
     }
-    
-    return new long[] {
-        Long.MAX_VALUE, Long.MIN_VALUE, Long.MAX_VALUE, Long.MIN_VALUE,
-        a, b, c};
+
+    return new long[]{Long.MAX_VALUE, Long.MIN_VALUE, Long.MAX_VALUE, Long.MIN_VALUE, a, b, c};
   }
 
   public static short[] createPrimitiveShortArray() {
-    return new short[] {
-        Short.MAX_VALUE, Short.MIN_VALUE, Short.MAX_VALUE, Short.MIN_VALUE};
+    return new short[]{Short.MAX_VALUE, Short.MIN_VALUE, Short.MAX_VALUE, Short.MIN_VALUE};
   }
 
   public static SerializablePrivateNoArg createPrivateNoArg() {
@@ -729,9 +726,9 @@
   }
 
   public static Short[] createShortArray() {
-    return new Short[] {
-        new Short(Short.MAX_VALUE), new Short(Short.MIN_VALUE),
-        new Short(Short.MAX_VALUE), new Short(Short.MIN_VALUE)};
+    return new Short[]{
+        new Short(Short.MAX_VALUE), new Short(Short.MIN_VALUE), new Short(Short.MAX_VALUE),
+        new Short(Short.MIN_VALUE)};
   }
 
   public static List<MarkerTypeSingleton> createSingletonList() {
@@ -739,16 +736,15 @@
   }
 
   public static java.sql.Date[] createSqlDateArray() {
-    return new java.sql.Date[] {
-        new java.sql.Date(500L), new java.sql.Date(500000000L)};
+    return new java.sql.Date[]{new java.sql.Date(500L), new java.sql.Date(500000000L)};
   }
 
   public static Time[] createSqlTimeArray() {
-    return new Time[] {new Time(500L), new Time(5000000L)};
+    return new Time[]{new Time(500L), new Time(5000000L)};
   }
 
   public static Timestamp[] createSqlTimestampArray() {
-    return new Timestamp[] {new Timestamp(500L), new Timestamp(5000000L)};
+    return new Timestamp[]{new Timestamp(500L), new Timestamp(5000000L)};
   }
 
   /*
@@ -756,13 +752,12 @@
    * to make sure they are handled properly.
    */
   public static String[] createStringArray() {
-    return new String[] {
-        null, "", "one", "two", "toString", "watch", "prototype", "eval",
-        "valueOf", "constructor", "__proto__"};
+    return new String[]{
+        null, "", "one", "two", "toString", "watch", "prototype", "eval", "valueOf", "constructor",
+        "__proto__"};
   }
 
-  public static TreeMap<String, MarkerTypeTreeMap> createTreeMap(
-      boolean defaultComparator) {
+  public static TreeMap<String, MarkerTypeTreeMap> createTreeMap(boolean defaultComparator) {
     TreeMap<String, MarkerTypeTreeMap> map;
     if (defaultComparator) {
       map = new TreeMap<String, MarkerTypeTreeMap>();
@@ -777,14 +772,12 @@
     return map;
   }
 
-  public static TreeSet<MarkerTypeTreeSet> createTreeSet(
-      boolean defaultComparator) {
+  public static TreeSet<MarkerTypeTreeSet> createTreeSet(boolean defaultComparator) {
     TreeSet<MarkerTypeTreeSet> set;
     if (defaultComparator) {
       set = new TreeSet<MarkerTypeTreeSet>();
     } else {
-      set = new TreeSet<MarkerTypeTreeSet>(
-          new ReverseSorter<MarkerTypeTreeSet>());
+      set = new TreeSet<MarkerTypeTreeSet>(new ReverseSorter<MarkerTypeTreeSet>());
     }
     set.add(new MarkerTypeTreeSet("foo"));
     set.add(new MarkerTypeTreeSet("bar"));
diff --git a/user/test/com/google/gwt/user/client/rpc/TypeCheckedGenericClass.java b/user/test/com/google/gwt/user/client/rpc/TypeCheckedGenericClass.java
new file mode 100644
index 0000000..334ed91
--- /dev/null
+++ b/user/test/com/google/gwt/user/client/rpc/TypeCheckedGenericClass.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2011 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.user.client.rpc;
+
+import java.util.HashMap;
+
+/**
+ * A class with a server custom fields serializer that does not inherit from
+ * {@link com.google.gwt.user.server.rpc.ServerCustomFieldSerializer}, but that
+ * does include type checking instantiate and deserialize methods.
+ * 
+ * This class has the ability to record a marker field. Its intended use is for
+ * a server side instantiate/deserialize to set the marker to verify that the
+ * instantiate/deserialize was invoked correctly.
+ * 
+ * @param <X> the key type for the hash map field
+ * @param <Y> the value type for the hash map field
+ */
+public class TypeCheckedGenericClass<X, Y> implements IsSerializable {
+  /**
+   * Public hash map to insert test elements.
+   */
+  public HashMap<X, Y> hashField = null;
+
+  private X markerKey = null;
+  private Y markerValue = null;
+
+  public TypeCheckedGenericClass() {
+    hashField = new HashMap<X, Y>();
+  }
+
+  public void setMarker(X key, Y value) {
+    markerKey = key;
+    markerValue = value;
+  }
+
+  public X getMarkerKey() {
+    return markerKey;
+  }
+
+  public Y getMarkerValue() {
+    return markerValue;
+  }
+}
diff --git a/user/test/com/google/gwt/user/client/rpc/TypeCheckedGenericClass_CustomFieldSerializer.java b/user/test/com/google/gwt/user/client/rpc/TypeCheckedGenericClass_CustomFieldSerializer.java
new file mode 100644
index 0000000..bad96aa
--- /dev/null
+++ b/user/test/com/google/gwt/user/client/rpc/TypeCheckedGenericClass_CustomFieldSerializer.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2011 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.user.client.rpc;
+
+import java.util.HashMap;
+
+/**
+ * This class is defined outside of the TypeCheckedObjectTestSetFactory
+ * because of a bug where custom field serializers cannot be inner classes. Once
+ * we fix this bug we can move this class into the test set factory.
+ */
+@SuppressWarnings("rawtypes")
+public class TypeCheckedGenericClass_CustomFieldSerializer {
+  @SuppressWarnings("unchecked")
+  public static void deserialize(SerializationStreamReader streamReader,
+      TypeCheckedGenericClass instance) throws SerializationException {
+    Object markerKey = streamReader.readObject();
+    Object markerValue = streamReader.readObject();
+    instance.setMarker(markerKey, markerValue);
+
+    instance.hashField = (HashMap) streamReader.readObject();
+  }
+  
+  @SuppressWarnings("unused")
+  public static TypeCheckedGenericClass instantiate(SerializationStreamReader streamReader) {
+    return new TypeCheckedGenericClass();
+  }
+
+  public static void serialize(SerializationStreamWriter streamWriter,
+      TypeCheckedGenericClass instance) throws SerializationException {
+    streamWriter.writeObject(instance.getMarkerKey());
+    streamWriter.writeObject(instance.getMarkerValue());
+    streamWriter.writeObject(instance.hashField);
+  }
+}
\ No newline at end of file
diff --git a/user/test/com/google/gwt/user/client/rpc/TypeCheckedObjectsTest.java b/user/test/com/google/gwt/user/client/rpc/TypeCheckedObjectsTest.java
new file mode 100644
index 0000000..f07b436
--- /dev/null
+++ b/user/test/com/google/gwt/user/client/rpc/TypeCheckedObjectsTest.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright 2011 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.user.client.rpc;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.user.client.rpc.TypeCheckedObjectsTestSetFactory.TypeCheckedFieldClass;
+import com.google.gwt.user.client.rpc.TypeCheckedObjectsTestSetFactory.TypeCheckedSuperClass;
+
+import junit.framework.AssertionFailedError;
+
+/**
+ * Test for RPC serialization of type checked objects.
+ * 
+ * Type checked objects are those that are verified as being of the correct type
+ * before they are deserialized, thus catching certain attacks that occur
+ * through deserialization.
+ * 
+ * Test Cases: - Type checked generic class with a server-side custom serializer
+ * that is NOT derived from ServerCustomFieldSerializer but which does define
+ * instantiateChecked and deserializeChecked, to verify that such methods are
+ * found and called. - Generic class that has no custom field serializer but
+ * which does include fields that do have type checked serializers, to verify
+ * that such serializers are still used. - Generic class that has no custom
+ * field serializer but which does extend a class with type checked serializers,
+ * to verify that such serializers are still used.
+ */
+public class TypeCheckedObjectsTest extends RpcTestBase {
+
+  private TypeCheckedObjectsTestServiceAsync typeCheckedObjectsTestService;
+
+  public void testInvalidCheckedFieldSerializer() {
+    TypeCheckedObjectsTestServiceAsync service = getServiceAsync();
+    delayTestFinishForRpc();
+    service.echo(TypeCheckedObjectsTestSetFactory.createInvalidCheckedFieldClass(),
+        new AsyncCallback<TypeCheckedFieldClass<Integer, String>>() {
+          @Override
+          public void onFailure(Throwable caught) {
+            // Expected in this case
+            assertTrue(caught instanceof SerializationException);
+            finishTest();
+          }
+
+          @Override
+          public void onSuccess(TypeCheckedFieldClass<Integer, String> result) {
+            fail("testInvalidCheckedFieldSerializer is expected to throw an assertion");
+          }
+        });
+  }
+
+  public void testInvalidCheckedSerializer() {
+    TypeCheckedObjectsTestServiceAsync service = getServiceAsync();
+    delayTestFinishForRpc();
+    service.echo(TypeCheckedObjectsTestSetFactory.createInvalidCheckedGenericClass(),
+        new AsyncCallback<TypeCheckedGenericClass<Integer, String>>() {
+          @Override
+          public void onFailure(Throwable caught) {
+            // Expected in this case
+            assertTrue(caught instanceof SerializationException);
+            finishTest();
+          }
+
+          @Override
+          public void onSuccess(TypeCheckedGenericClass<Integer, String> result) {
+            fail("testInvalidCheckedSerializer is expected to throw an assertion");
+          }
+        });
+  }
+
+  public void testInvalidCheckedSuperSerializer() {
+    TypeCheckedObjectsTestServiceAsync service = getServiceAsync();
+    delayTestFinishForRpc();
+    service.echo(TypeCheckedObjectsTestSetFactory.createInvalidCheckedSuperClass(),
+        new AsyncCallback<TypeCheckedSuperClass<Integer, String>>() {
+          @Override
+          public void onFailure(Throwable caught) {
+            // Expected in this case
+            assertTrue(caught instanceof SerializationException);
+            finishTest();
+          }
+
+          @Override
+          public void onSuccess(TypeCheckedSuperClass<Integer, String> result) {
+            fail("testInvalidCheckedSerializer is expected to throw an assertion");
+          }
+        });
+  }
+
+  public void testInvalidUncheckedSerializer() {
+    TypeCheckedObjectsTestServiceAsync service = getServiceAsync();
+    delayTestFinishForRpc();
+    service.echo(TypeCheckedObjectsTestSetFactory.createInvalidUncheckedGenericClass(),
+        new AsyncCallback<TypeUncheckedGenericClass<Integer, String>>() {
+          @Override
+          public void onFailure(Throwable caught) {
+            // Expected in this case
+            assertTrue(caught instanceof SerializationException);
+            finishTest();
+          }
+
+          @Override
+          public void onSuccess(TypeUncheckedGenericClass<Integer, String> result) {
+            fail("testInvalidUncheckedSerializer is expected to throw an assertion");
+          }
+        });
+  }
+
+  public void testTypeCheckedFieldSerializer() {
+    TypeCheckedObjectsTestServiceAsync service = getServiceAsync();
+    delayTestFinishForRpc();
+    service.echo(TypeCheckedObjectsTestSetFactory.createTypeCheckedFieldClass(),
+        new AsyncCallback<TypeCheckedFieldClass<Integer, String>>() {
+          @Override
+          public void onFailure(Throwable caught) {
+            AssertionFailedError er =
+                new AssertionFailedError("Could not serialize/deserialize TypeCheckedFieldClass");
+            er.initCause(caught);
+            throw er;
+          }
+
+          @Override
+          public void onSuccess(TypeCheckedFieldClass<Integer, String> result) {
+            assertNotNull(result);
+            assertTrue(TypeCheckedObjectsTestSetValidator.isValid(result));
+            finishTest();
+          }
+        });
+  }
+
+  public void testTypeCheckedSerializer() {
+    TypeCheckedObjectsTestServiceAsync service = getServiceAsync();
+    delayTestFinishForRpc();
+    service.echo(TypeCheckedObjectsTestSetFactory.createTypeCheckedGenericClass(),
+        new AsyncCallback<TypeCheckedGenericClass<Integer, String>>() {
+          @Override
+          public void onFailure(Throwable caught) {
+            AssertionFailedError er =
+                new AssertionFailedError("Could not serialize/deserialize TypeCheckedGenericClass");
+            er.initCause(caught);
+            throw er;
+          }
+
+          @Override
+          public void onSuccess(TypeCheckedGenericClass<Integer, String> result) {
+            assertNotNull(result);
+            assertTrue(TypeCheckedObjectsTestSetValidator.isValid(result));
+            finishTest();
+          }
+        });
+  }
+
+  public void testTypeCheckedSuperSerializer() {
+    TypeCheckedObjectsTestServiceAsync service = getServiceAsync();
+    delayTestFinishForRpc();
+    service.echo(TypeCheckedObjectsTestSetFactory.createTypeCheckedSuperClass(),
+        new AsyncCallback<TypeCheckedSuperClass<Integer, String>>() {
+          @Override
+          public void onFailure(Throwable caught) {
+            AssertionFailedError er =
+                new AssertionFailedError("Could not serialize/deserialize TypeCheckedGenericClass");
+            er.initCause(caught);
+            throw er;
+          }
+
+          @Override
+          public void onSuccess(TypeCheckedSuperClass<Integer, String> result) {
+            assertNotNull(result);
+            assertTrue(TypeCheckedObjectsTestSetValidator.isValid(result));
+            finishTest();
+          }
+        });
+  }
+
+  public void testTypeUncheckedSerializer() {
+    TypeCheckedObjectsTestServiceAsync service = getServiceAsync();
+    delayTestFinishForRpc();
+    service.echo(TypeCheckedObjectsTestSetFactory.createTypeUncheckedGenericClass(),
+        new AsyncCallback<TypeUncheckedGenericClass<Integer, String>>() {
+          @Override
+          public void onFailure(Throwable caught) {
+            AssertionFailedError er =
+                new AssertionFailedError("Could not serialize/deserialize TypeUncheckedGenericClass");
+            er.initCause(caught);
+            throw er;
+          }
+
+          @Override
+          public void onSuccess(TypeUncheckedGenericClass<Integer, String> result) {
+            assertNotNull(result);
+            assertTrue(TypeCheckedObjectsTestSetValidator.isValid(result));
+            finishTest();
+          }
+        });
+  }
+
+  private TypeCheckedObjectsTestServiceAsync getServiceAsync() {
+    if (typeCheckedObjectsTestService == null) {
+      typeCheckedObjectsTestService =
+          (TypeCheckedObjectsTestServiceAsync) GWT.create(TypeCheckedObjectsTestService.class);
+      ((ServiceDefTarget) typeCheckedObjectsTestService).setServiceEntryPoint(GWT
+          .getModuleBaseURL()
+          + "typecheckedobjects");
+    }
+    return typeCheckedObjectsTestService;
+  }
+}
diff --git a/user/test/com/google/gwt/user/client/rpc/TypeCheckedObjectsTestService.java b/user/test/com/google/gwt/user/client/rpc/TypeCheckedObjectsTestService.java
new file mode 100644
index 0000000..4a642e7
--- /dev/null
+++ b/user/test/com/google/gwt/user/client/rpc/TypeCheckedObjectsTestService.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2011 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.user.client.rpc;
+
+import com.google.gwt.user.client.rpc.TypeCheckedObjectsTestSetFactory.TypeCheckedFieldClass;
+import com.google.gwt.user.client.rpc.TypeCheckedObjectsTestSetFactory.TypeCheckedSuperClass;
+
+/**
+ * Service interface used by the
+ * {@link com.google.gwt.user.client.rpc.TypeCheckedObjectsTest
+ * TypeCheckedObjectsTest} unit test.
+ */
+public interface TypeCheckedObjectsTestService extends RemoteService {
+  TypeCheckedGenericClass<Integer, String> echo(TypeCheckedGenericClass<Integer, String> arg1);
+
+  TypeCheckedSuperClass<Integer, String> echo(TypeCheckedSuperClass<Integer, String> arg1);
+
+  TypeCheckedFieldClass<Integer, String> echo(TypeCheckedFieldClass<Integer, String> arg1);
+
+  TypeUncheckedGenericClass<Integer, String> echo(TypeUncheckedGenericClass<Integer, String> arg1);
+}
diff --git a/user/test/com/google/gwt/user/client/rpc/TypeCheckedObjectsTestServiceAsync.java b/user/test/com/google/gwt/user/client/rpc/TypeCheckedObjectsTestServiceAsync.java
new file mode 100644
index 0000000..5c63807
--- /dev/null
+++ b/user/test/com/google/gwt/user/client/rpc/TypeCheckedObjectsTestServiceAsync.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2011 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.user.client.rpc;
+
+import com.google.gwt.user.client.rpc.TypeCheckedObjectsTestSetFactory.TypeCheckedFieldClass;
+import com.google.gwt.user.client.rpc.TypeCheckedObjectsTestSetFactory.TypeCheckedSuperClass;
+
+/**
+ * Async service for testing type checking of RPC arguments.
+ * 
+ * Note that the first argument to each echo method is raw, so as to allow
+ * incorrect types to be sent and tested.
+ * 
+ */
+public interface TypeCheckedObjectsTestServiceAsync {
+
+  /**
+   * @see com.google.gwt.user.client.rpc.TypeCheckedObjectsTestService#echo(com.google.gwt.user.client.rpc.TypeCheckedGenericClass)
+   */
+  @SuppressWarnings("rawtypes")
+  void echo(TypeCheckedGenericClass arg1,
+      AsyncCallback<TypeCheckedGenericClass<Integer, String>> callback);
+
+  /**
+   * @see com.google.gwt.user.client.rpc.TypeCheckedObjectsTestService#echo(com.google.gwt.user.client.rpc.TypeCheckedObjectsTestSetFactory.TypeCheckedSuperClass)
+   */
+  @SuppressWarnings("rawtypes")
+  void echo(TypeCheckedSuperClass arg1,
+      AsyncCallback<TypeCheckedSuperClass<Integer, String>> callback);
+
+  /**
+   * @see com.google.gwt.user.client.rpc.TypeCheckedObjectsTestService#echo(com.google.gwt.user.client.rpc.TypeCheckedObjectsTestSetFactory.TypeCheckedFieldClass)
+   */
+  @SuppressWarnings("rawtypes")
+  void echo(TypeCheckedFieldClass arg1,
+      AsyncCallback<TypeCheckedFieldClass<Integer, String>> callback);
+
+  /**
+   * @see com.google.gwt.user.client.rpc.TypeCheckedObjectsTestService#echo(com.google.gwt.user.client.rpc.TypeUncheckedGenericClass)
+   */
+  @SuppressWarnings("rawtypes")
+  void echo(TypeUncheckedGenericClass arg1,
+      AsyncCallback<TypeUncheckedGenericClass<Integer, String>> callback);
+}
diff --git a/user/test/com/google/gwt/user/client/rpc/TypeCheckedObjectsTestSetFactory.java b/user/test/com/google/gwt/user/client/rpc/TypeCheckedObjectsTestSetFactory.java
new file mode 100644
index 0000000..faafc86
--- /dev/null
+++ b/user/test/com/google/gwt/user/client/rpc/TypeCheckedObjectsTestSetFactory.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2011 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.user.client.rpc;
+
+import java.io.Serializable;
+import java.util.HashSet;
+
+/**
+ * Generated test data for the
+ * {@link com.google.gwt.user.client.rpc.TypeCheckedObjectsTest} unit tests.
+ */
+public class TypeCheckedObjectsTestSetFactory {
+
+  /**
+   * Used to test that the type checked field for an unchecked class is actually
+   * type checked.
+   */
+  public static class TypeCheckedFieldClass<X, Y> implements Serializable {
+    private TypeCheckedGenericClass<X, Y> checkedField = null;
+
+    public TypeCheckedFieldClass() {
+    }
+
+    public TypeCheckedGenericClass<X, Y> getCheckedField() {
+      return checkedField;
+    }
+
+    public void setCheckedField(TypeCheckedGenericClass<X, Y> value) {
+      checkedField = value;
+    }
+  }
+
+  /**
+   * Used to test that the type checked base class for an unchecked class is
+   * actually type checked.
+   */
+  public static class TypeCheckedSuperClass<X, Y> extends TypeCheckedGenericClass<X, Y> {
+  }
+
+  public static TypeCheckedFieldClass<HashSet<Integer>, String> createInvalidCheckedFieldClass() {
+    TypeCheckedFieldClass<HashSet<Integer>, String> result =
+      new TypeCheckedFieldClass<HashSet<Integer>, String>();
+    TypeCheckedGenericClass<HashSet<Integer>, String> field =
+      new TypeCheckedGenericClass<HashSet<Integer>, String>();
+    field.hashField.put(TypeCheckedObjectsTestSetValidator.invalidMarkerKey,
+        TypeCheckedObjectsTestSetValidator.markerValue);
+    result.setCheckedField(field);
+    return result;
+  }
+
+  public static
+  TypeCheckedGenericClass<Integer, HashSet<String>> createInvalidCheckedGenericClass() {
+    TypeCheckedGenericClass<Integer, HashSet<String>> result =
+        new TypeCheckedGenericClass<Integer, HashSet<String>>();
+    result.hashField.put(TypeCheckedObjectsTestSetValidator.markerKey,
+        TypeCheckedObjectsTestSetValidator.invalidMarkerValue);
+    return result;
+  }
+
+  public static TypeCheckedSuperClass<HashSet<Integer>, String> createInvalidCheckedSuperClass() {
+    TypeCheckedSuperClass<HashSet<Integer>, String> result =
+      new TypeCheckedSuperClass<HashSet<Integer>, String>();
+    result.hashField.put(TypeCheckedObjectsTestSetValidator.invalidMarkerKey,
+        TypeCheckedObjectsTestSetValidator.markerValue);
+    return result;
+  }
+
+  public static TypeCheckedFieldClass<Integer, String> createTypeCheckedFieldClass() {
+    TypeCheckedFieldClass<Integer, String> result =
+      new TypeCheckedFieldClass<Integer, String>();
+    TypeCheckedGenericClass<Integer, String> field =
+      new TypeCheckedGenericClass<Integer, String>();
+    field.hashField.put(TypeCheckedObjectsTestSetValidator.markerKey,
+        TypeCheckedObjectsTestSetValidator.markerValue);
+    result.setCheckedField(field);
+    return result;
+  }
+
+  public static TypeCheckedGenericClass<Integer, String> createTypeCheckedGenericClass() {
+    TypeCheckedGenericClass<Integer, String> result =
+        new TypeCheckedGenericClass<Integer, String>();
+    result.hashField.put(TypeCheckedObjectsTestSetValidator.markerKey,
+        TypeCheckedObjectsTestSetValidator.markerValue);
+    return result;
+  }
+
+  public static TypeCheckedSuperClass<Integer, String> createTypeCheckedSuperClass() {
+    TypeCheckedSuperClass<Integer, String> result = new TypeCheckedSuperClass<Integer, String>();
+    result.hashField.put(TypeCheckedObjectsTestSetValidator.markerKey,
+        TypeCheckedObjectsTestSetValidator.markerValue);
+    return result;
+  }
+
+  public static
+  TypeUncheckedGenericClass<Integer, HashSet<String>> createInvalidUncheckedGenericClass() {
+    TypeUncheckedGenericClass<Integer, HashSet<String>> result =
+        new TypeUncheckedGenericClass<Integer, HashSet<String>>();
+    result.setMarker(TypeCheckedObjectsTestSetValidator.markerKey,
+        TypeCheckedObjectsTestSetValidator.invalidMarkerValue);
+    result.checkedField.hashField.put(TypeCheckedObjectsTestSetValidator.markerKey,
+        TypeCheckedObjectsTestSetValidator.invalidMarkerValue);
+    return result;
+  }
+
+  public static TypeUncheckedGenericClass<Integer, String> createTypeUncheckedGenericClass() {
+    TypeUncheckedGenericClass<Integer, String> result =
+        new TypeUncheckedGenericClass<Integer, String>();
+    result.setMarker(TypeCheckedObjectsTestSetValidator.markerKey,
+        TypeCheckedObjectsTestSetValidator.markerValue);
+    result.checkedField.hashField.put(TypeCheckedObjectsTestSetValidator.markerKey,
+        TypeCheckedObjectsTestSetValidator.markerValue);
+    return result;
+  }
+}
diff --git a/user/test/com/google/gwt/user/client/rpc/TypeCheckedObjectsTestSetValidator.java b/user/test/com/google/gwt/user/client/rpc/TypeCheckedObjectsTestSetValidator.java
new file mode 100644
index 0000000..4adfbf8
--- /dev/null
+++ b/user/test/com/google/gwt/user/client/rpc/TypeCheckedObjectsTestSetValidator.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2011 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.user.client.rpc;
+
+import com.google.gwt.user.client.rpc.TypeCheckedObjectsTestSetFactory.TypeCheckedFieldClass;
+
+import java.util.HashSet;
+
+/**
+ * Data validator used by the
+ * {@link com.google.gwt.user.client.rpc.TypeCheckedObjectsTest} unit tests.
+ */
+public class TypeCheckedObjectsTestSetValidator {
+  public static final Integer markerKey = 12345;
+  public static final String markerValue = "Marker";
+
+  public static final HashSet<Integer> invalidMarkerKey = new HashSet<Integer>() {
+    {
+      add(12345);
+    }
+  };
+  public static final HashSet<String> invalidMarkerValue = new HashSet<String>() {
+    {
+      add("Marker");
+    }
+  };
+  
+  public static final String expectedInvalidMessage = "Expected isValid exception";
+
+  public static boolean isValid(TypeCheckedGenericClass<Integer, String> arg1) {
+    if (arg1 == null) {
+      return false;
+    }
+
+    if (!markerKey.equals(arg1.getMarkerKey())) {
+      return false;
+    }
+
+    if (!markerValue.equals(arg1.getMarkerValue())) {
+      return false;
+    }
+
+    if (arg1.hashField.size() != 1) {
+      return false;
+    }
+
+    if (!arg1.hashField.containsKey(markerKey) || !arg1.hashField.containsValue(markerValue)) {
+      return false;
+    }
+
+    return true;
+  }
+
+  public static boolean isValid(TypeCheckedFieldClass<Integer, String> arg1) {
+    if (arg1 == null) {
+      return false;
+    }
+
+    TypeCheckedGenericClass<Integer, String> field = arg1.getCheckedField();
+
+    if (!markerKey.equals(field.getMarkerKey())) {
+      return false;
+    }
+
+    if (!markerValue.equals(field.getMarkerValue())) {
+      return false;
+    }
+
+    if (field.hashField.size() != 1) {
+      return false;
+    }
+
+    if (!field.hashField.containsKey(markerKey) || !field.hashField.containsValue(markerValue)) {
+      return false;
+    }
+
+    return true;
+  }
+
+  @SuppressWarnings({"rawtypes", "unchecked"})
+  public static boolean isValid(TypeUncheckedGenericClass arg1) {
+    if (arg1 == null) {
+      return false;
+    }
+
+    Object markerKeyObject = arg1.getMarkerKey();
+    Object markerValueObject = arg1.getMarkerValue();
+    
+    if (!(markerKeyObject instanceof Integer)) {
+      return false;
+    }
+    
+    if (!(markerValueObject instanceof String)) {
+      return false;
+    }
+    
+    return isValid(arg1.checkedField);
+  }
+}
diff --git a/user/test/com/google/gwt/user/client/rpc/TypeUncheckedGenericClass.java b/user/test/com/google/gwt/user/client/rpc/TypeUncheckedGenericClass.java
new file mode 100644
index 0000000..d0f7a06
--- /dev/null
+++ b/user/test/com/google/gwt/user/client/rpc/TypeUncheckedGenericClass.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2011 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.user.client.rpc;
+
+/**
+ * A class with no server custom field serializer with a member that does
+ * have a type checked server custom field serializer.
+ * 
+ * The class tests deserialization in which type information is lost when
+ * deserializing the class, but the system then tries to use a deserializaer
+ * that expects type information (when deserializing the type checked member).
+ * 
+ * This class has the ability to record a marker field. Its intended use is for
+ * a server side instantiate/deserialize to set the marker to verify that the
+ * instantiate/deserialize was invoked correctly.
+ * 
+ * @param <X> the key type for the hash map field of the checkedField member
+ * @param <Y> the value type for the hash map field of the checkedField member
+ */
+public class TypeUncheckedGenericClass<X, Y> implements IsSerializable {
+  /**
+   * Public hash map to insert test elements.
+   */
+  public TypeCheckedGenericClass<X, Y> checkedField = null;
+
+  public TypeUncheckedGenericClass() {
+    checkedField = new TypeCheckedGenericClass<X, Y>();
+  }
+
+  public void setMarker(X key, Y value) {
+    checkedField.setMarker(key, value);
+  }
+
+  public X getMarkerKey() {
+    return checkedField.getMarkerKey();
+  }
+
+  public Y getMarkerValue() {
+    return checkedField.getMarkerValue();
+  }
+}
diff --git a/user/test/com/google/gwt/user/client/rpc/TypeUncheckedGenericClass_CustomFieldSerializer.java b/user/test/com/google/gwt/user/client/rpc/TypeUncheckedGenericClass_CustomFieldSerializer.java
new file mode 100644
index 0000000..88a9ffd
--- /dev/null
+++ b/user/test/com/google/gwt/user/client/rpc/TypeUncheckedGenericClass_CustomFieldSerializer.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2011 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.user.client.rpc;
+
+/**
+ * This class is defined outside of the TypeUncheckedObjectTestSetFactory
+ * because of a bug where custom field serializers cannot be inner classes. Once
+ * we fix this bug we can move this class into the test set factory.
+ */
+@SuppressWarnings("rawtypes")
+public class TypeUncheckedGenericClass_CustomFieldSerializer {
+  @SuppressWarnings("unchecked")
+  public static void deserialize(SerializationStreamReader streamReader,
+      TypeUncheckedGenericClass instance) throws SerializationException {
+    instance.checkedField = (TypeCheckedGenericClass) streamReader.readObject();
+  }
+  
+  @SuppressWarnings("unused")
+  public static TypeUncheckedGenericClass instantiate(SerializationStreamReader streamReader) {
+    return new TypeUncheckedGenericClass();
+  }
+
+  public static void serialize(SerializationStreamWriter streamWriter,
+      TypeUncheckedGenericClass instance) throws SerializationException {
+    streamWriter.writeObject(instance.checkedField);
+  }
+}
\ No newline at end of file
diff --git a/user/test/com/google/gwt/user/server/rpc/RPCTypeCheckArraysTest.java b/user/test/com/google/gwt/user/server/rpc/RPCTypeCheckArraysTest.java
new file mode 100644
index 0000000..f9ff365
--- /dev/null
+++ b/user/test/com/google/gwt/user/server/rpc/RPCTypeCheckArraysTest.java
@@ -0,0 +1,575 @@
+/*
+ * Copyright 2011 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.user.server.rpc;
+
+import com.google.gwt.user.client.rpc.IncompatibleRemoteServiceException;
+import com.google.gwt.user.client.rpc.RemoteService;
+import com.google.gwt.user.client.rpc.SerializationException;
+import com.google.gwt.user.client.rpc.SerializedTypeViolationException;
+
+import junit.framework.TestCase;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * Array test cases for the type checking code found in server-side RPC
+ * deserialization.
+ */
+@SuppressWarnings("rawtypes")
+public class RPCTypeCheckArraysTest extends TestCase {
+
+  /*
+   * Test that primitive arrays cannot be replaced by non-array types
+   *   - int for int[]: testPrimitiveSpoofingArray
+   *   - String for String[]: testPrimitiveSpoofingArray
+   *   - HashSet for int[]: testHashSetSpoofingArray
+   *   - HashSet for String[]: testHashSetSpoofingArray
+   *   - HashSet for AClass[]: testHashSetSpoofingClassArray
+   * 
+   * Test that non-array-type cannot be replaced by array types
+   *   - int[] for int: testArraysSpoofingObjects
+   *   - String[] for String: testArraytestArraysSpoofingObjects
+   *   - String[] for AClass: testArraysSpoofingObjects
+   *
+   * Test generics parameterized by arrays:
+   *   - HashMap<String, int[]>: testArrayAsGeneric
+   *   - HashMap<String, Integer[]>: testArrayAsGeneric
+   *   - HashMap<String, AClass[]>: testArrayAsGeneric
+   *
+   * Test arrays of generics:
+   *   - List<Integer>[]: testArrayOfGeneric
+   *   - List[]: testArrayOfGeneric
+   *   - List<? extends HashSet<Integer>>[]: testArrayOfGeneric
+   */  
+  
+  
+  /**
+   * A class for testing spoofing of arrays.
+   */
+  public static class ArraysParamTestClass implements RemoteService {
+    @SuppressWarnings("unused")
+    public static void testArrays(int[] intArg, String[] stringArg) { }
+
+    @SuppressWarnings("unused")
+    public static void testAClassArray(RPCTypeCheckTest.AClass[] arg1) { }
+    
+    @SuppressWarnings("unused")
+    public static void testGenericsArrays(
+        HashMap<String, int[]> arg1,
+        HashMap<String, Integer[]> arg2,
+        HashMap<String, RPCTypeCheckTest.AClass[]> arg3) { }
+    
+    @SuppressWarnings("unused")
+    public static void testArrayOfGeneric(List<Integer>[] arg1) { }
+    
+    @SuppressWarnings("unused")
+    public static void testArrayOfGenericRaw(List[] arg1) { }
+    
+    @SuppressWarnings("unused")
+    public static void testArrayOfGenericWildcard(List<? extends HashSet<Integer>>[] arg1) { }
+  }
+
+  private static String generateArrayGenericsA() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ArraysParamTestClass.class, "testGenericsArrays");
+      
+      HashMap<String, HashSet> arg1 = new HashMap<String, HashSet>();
+      arg1.put("foo", RPCTypeCheckFactory.generateTestHashSet());
+      strFactory.write(arg1);
+      
+      HashMap<String, Integer[]> arg2 = new HashMap<String, Integer[]>();
+      Integer[] entry1 = new Integer[] {12345, 67890};
+      arg2.put("bar", entry1);
+      strFactory.write(arg2);
+
+      HashMap<String, RPCTypeCheckTest.AClass[]> arg3 =
+        new HashMap<String, RPCTypeCheckTest.AClass[]>();
+      RPCTypeCheckTest.AClass[] entry2 = new RPCTypeCheckTest.AClass[2];
+      entry2[0] = new RPCTypeCheckTest.AClass(234);
+      entry2[1] = new RPCTypeCheckTest.AClass(567);
+      arg3.put("baz", entry2);
+      strFactory.write(arg3);
+
+      return strFactory.toString();
+      
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  private static String generateArrayGenericsB() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ArraysParamTestClass.class, "testGenericsArrays");
+      
+      HashMap<String, int[]> arg1 = new HashMap<String, int[]>();
+      int[] entry1 = new int[] { 234, 567 };
+      arg1.put("foo", entry1);
+      strFactory.write(arg1);
+      
+      HashMap<String, HashSet[]> arg2 = new HashMap<String, HashSet[]>();
+      HashSet[] entry2 = new HashSet[] { RPCTypeCheckFactory.generateTestHashSet() };
+      arg2.put("bar", entry2);
+      strFactory.write(arg2);
+
+      HashMap<String, RPCTypeCheckTest.AClass[]> arg3 =
+        new HashMap<String, RPCTypeCheckTest.AClass[]>();
+      RPCTypeCheckTest.AClass[] entry3 = new RPCTypeCheckTest.AClass[2];
+      entry3[0] = new RPCTypeCheckTest.AClass(234);
+      entry3[1] = new RPCTypeCheckTest.AClass(567);
+      arg3.put("baz", entry3);
+      strFactory.write(arg3);
+
+      return strFactory.toString();
+      
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  private static String generateArrayGenericsC() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ArraysParamTestClass.class, "testGenericsArrays");
+      
+      HashMap<String, int[]> arg1 = new HashMap<String, int[]>();
+      int[] entry1 = new int[] { 234, 567 };
+      arg1.put("foo", entry1);
+      strFactory.write(arg1);
+      
+      HashMap<String, Integer[]> arg2 = new HashMap<String, Integer[]>();
+      Integer[] entry2 = new Integer[] {12345, 67890};
+      arg2.put("bar", entry2);
+      strFactory.write(arg2);
+
+      HashMap<String, HashSet[]> arg3 = new HashMap<String, HashSet[]>();
+      HashSet[] entry3 = new HashSet[] { RPCTypeCheckFactory.generateTestHashSet() };
+      arg3.put("baz", entry3);
+      strFactory.write(arg3);
+
+      return strFactory.toString();
+      
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  private static String generateArrayOfGenericsA(String methodName) {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ArraysParamTestClass.class, methodName);
+      
+      HashMap<String, HashSet> arg1 = new HashMap<String, HashSet>();
+      arg1.put("foo", RPCTypeCheckFactory.generateTestHashSet());
+      strFactory.write(arg1);
+      
+      return strFactory.toString();
+      
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  private static String generateArrayOfGenericsB(String methodName) {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ArraysParamTestClass.class, methodName);
+      
+      LinkedList[] arg1 = new LinkedList[1];
+      LinkedList<LinkedHashSet> list = new LinkedList<LinkedHashSet>();
+      list.add(RPCTypeCheckFactory.generateTestLinkedHashSet());
+      arg1[0] = list;
+      strFactory.write(arg1);
+      
+      return strFactory.toString();
+      
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  @SuppressWarnings("unchecked")
+  private static String generateArrayOfGenericsC(String methodName) {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ArraysParamTestClass.class, methodName);
+      
+      LinkedList<HashSet<Integer>>[] arg1 = new LinkedList[1];
+      LinkedList<HashSet<Integer>> list = new LinkedList<HashSet<Integer>>();
+      HashSet<Integer> listElmt = new HashSet<Integer>();
+      listElmt.add(12345);
+      listElmt.add(67890);
+      list.add(listElmt);
+      arg1[0] = list;
+      strFactory.write(arg1);
+      
+      return strFactory.toString();
+      
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  private static String generateHashSetSpoofingClassArray() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ArraysParamTestClass.class, "testAClassArray");
+
+      HashSet<Integer> hashSet = new HashSet<Integer>();
+      hashSet.add(12345);
+      hashSet.add(67890);
+      strFactory.write(hashSet);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  private static String generateHashSetSpoofingIntArray() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ArraysParamTestClass.class, "testArrays");
+
+      HashSet<Integer> hashSet = new HashSet<Integer>();
+      hashSet.add(12345);
+      hashSet.add(67890);
+      strFactory.write(hashSet);
+
+      String[] stringArray = new String[]{"foo", "bar"};
+      strFactory.write(stringArray);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  private static String generateHashSetSpoofingStringArray() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ArraysParamTestClass.class, "testArrays");
+
+      int[] intArray = new int[]{12345, 67890};
+      strFactory.write(intArray);
+
+      HashSet<Integer> hashSet = new HashSet<Integer>();
+      hashSet.add(12345);
+      hashSet.add(67890);
+      strFactory.write(hashSet);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  private static String generateIntArraySpoofingInt() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(RPCTypeCheckTest.PrimitiveParamTestClass.class, "testIntString");
+
+      int[] iArray = new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
+      strFactory.write(iArray);
+
+      strFactory.write("foo");
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  private static String generateIntSpoofingIntArray() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ArraysParamTestClass.class, "testArrays");
+
+      int i = 12345;
+      strFactory.write(i);
+
+      String[] stringArray = new String[]{"foo", "bar"};
+      strFactory.write(stringArray);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+  
+  private static String generateStringArraySpoofingClass() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(RPCTypeCheckTest.ClassesParamTestClass.class, "testAClass");
+
+      String a = "a";
+      String[] stringArray = new String[]{a, a, a, a, a, a, a, a, a, a, a, a, a, a};
+      strFactory.write(stringArray);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  private static String generateStringArraySpoofingString() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(RPCTypeCheckTest.PrimitiveParamTestClass.class, "testIntString");
+
+      int i = 12345;
+      strFactory.write(i);
+
+      String a = "a";
+      String[] stringArray = new String[]{a, a, a, a, a, a, a, a, a, a, a, a, a, a};
+      strFactory.write(stringArray);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  private static String generateStringSpoofingStringArray() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ArraysParamTestClass.class, "testArrays");
+
+      int[] arg1 = new int[]{12345, 67890};
+      strFactory.write(arg1);
+
+      String arg2 = "bar";
+      strFactory.write(arg2);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  /**
+   * This checks that arrays as generic types are checked correctly.
+   */
+  public void testArrayGenerics() {
+    try {
+      RPC.decodeRequest(generateArrayGenericsA());
+      fail("Expected IncompatibleRemoteServiceException from testArrayGenerics (1)");
+    } catch (IncompatibleRemoteServiceException e) {
+      // Expected to get here
+      assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+      assertTrue(e.getCause().getMessage().matches(".*HashSet.*int.*"));
+    }
+    try {
+      RPC.decodeRequest(generateArrayGenericsB());
+      fail("Expected IncompatibleRemoteServiceException from testArrayGenerics (2)");
+    } catch (IncompatibleRemoteServiceException e) {
+      // Expected to get here
+      assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+      assertTrue(e.getCause().getMessage().matches(".*HashSet.*Integer.*"));
+    }
+    try {
+      RPC.decodeRequest(generateArrayGenericsC());
+      fail("Expected IncompatibleRemoteServiceException from testArrayGenerics (3)");
+    } catch (IncompatibleRemoteServiceException e) {
+      // Expected to get here
+      assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+      assertTrue(e.getCause().getMessage().matches(".*HashSet.*AClass.*"));
+    }
+  }
+
+  /**
+   * This checks that arrays of generic types are checked correctly.
+   */
+  public void testArrayOfGenerics() {
+    try {
+      RPC.decodeRequest(generateArrayOfGenericsC("testArrayOfGenericRaw"));
+      // Expect to pass
+    } catch (Exception e) {
+      fail("Unexpected exception from testArrayOfGenerics (0): " + e.getMessage());
+    }
+    try {
+      RPC.decodeRequest(generateArrayOfGenericsA("testArrayOfGeneric"));
+      fail("Expected IncompatibleRemoteServiceException from testArrayOfGenerics (1)");
+    } catch (IncompatibleRemoteServiceException e) {
+      // Expected to get here
+      assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+      assertTrue(e.getCause().getMessage().matches(".*HashMap.*List.*"));
+    }
+    try {
+      RPC.decodeRequest(generateArrayOfGenericsB("testArrayOfGeneric"));
+      fail("Expected IncompatibleRemoteServiceException from testArrayOfGenerics (2)");
+    } catch (IncompatibleRemoteServiceException e) {
+      // Expected to get here
+      assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+      assertTrue(e.getCause().getMessage().matches(".*HashSet.*Integer.*"));
+    }
+    try {
+      RPC.decodeRequest(generateArrayOfGenericsA("testArrayOfGenericWildcard"));
+      fail("Expected IncompatibleRemoteServiceException from testArrayOfGenerics (3)");
+    } catch (IncompatibleRemoteServiceException e) {
+      // Expected to get here
+      assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+      assertTrue(e.getCause().getMessage().matches(".*HashMap.*List.*"));
+    }
+    try {
+      RPC.decodeRequest(generateArrayOfGenericsC("testArrayOfGenericWildcard"));
+    } catch (Exception e) {
+      fail("Unexpected Exception from testArrayOfGenerics (4): " + e.getMessage());
+    }
+    try {
+      RPC.decodeRequest(generateArrayOfGenericsB("testArrayOfGenericWildcard"));
+      fail("Expected IncompatibleRemoteServiceException from testArrayOfGenerics (5)");
+    } catch (IncompatibleRemoteServiceException e) {
+      // Expected to get here
+      assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+      assertTrue(e.getCause().getMessage().matches(".*HashSet.*Integer.*"));
+    }
+  }
+
+  /**
+   * This checks that arrays cannot be used in place of primitives.
+   */
+  public void testArraysSpoofingObjects() {
+    try {
+      RPC.decodeRequest(generateIntArraySpoofingInt());
+      fail("Expected ArrayIndexOutOfBoundsException from testArraysSpoofingPrimitives (1)");
+    } catch (Exception e) {
+      // Expected to get here
+      assertTrue(e instanceof ArrayIndexOutOfBoundsException);
+    }
+    try {
+      RPCRequest result = RPC.decodeRequest(generateStringArraySpoofingString());
+      assertTrue(result.getParameters()[1].toString().matches(".*\\[Ljava.lang.String.*"));
+    } catch (Exception e) {
+      fail("Unexpected Exception from testArraysSpoofingPrimitives (2)");
+    }
+    try {
+      RPC.decodeRequest(generateStringArraySpoofingClass());
+      fail("Expected IncompatibleRemoteServiceException from testArraysSpoofingPrimitives (3)");
+    } catch (IncompatibleRemoteServiceException e) {
+      // Expected to get here
+      assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+      assertTrue(e.getCause().getMessage().matches(".*\\[Ljava.lang.String.*AClass.*"));
+    }
+  }
+
+  /**
+   * This checks situations in which an RPC message is modified to replace
+   * arguments of a primitive type with another primitive type.
+   */
+  public void testHashSetSpoofingArray() {
+    try {
+      RPC.decodeRequest(generateHashSetSpoofingIntArray());
+      fail("Expected IncompatibleRemoteServiceException from testHashSetSpoofingArray (1)");
+    } catch (IncompatibleRemoteServiceException e) {
+      // Expected to get here. When the int array is processed, it reads the
+      // HashSet class and fails to know what it is, because it is not the
+      // way an int would be serialized in this context.
+      assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+      assertTrue(e.getCause().getMessage().matches(".*HashSet.*\\[I.*"));
+    }
+    try {
+      RPC.decodeRequest(generateHashSetSpoofingStringArray());
+      fail("Expected IncompatibleRemoteServiceException from testHashSetSpoofingArray (2)");
+    } catch (IncompatibleRemoteServiceException e) {
+      // Expected to get here
+      assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+      assertTrue(e.getCause().getMessage().matches(".*HashSet.*String.*"));
+    }
+  }
+
+  /**
+   * This checks situations in which an RPC message is modified to replace an
+   * array of objects with a HashSet (custom serialized container type).
+   */
+  public void testHashSetSpoofingClassArray() {
+    try {
+      RPC.decodeRequest(generateHashSetSpoofingClassArray());
+      fail("Expected IncompatibleRemoteServiceException from testHashSetSpoofingClassArray");
+    } catch (IncompatibleRemoteServiceException e) {
+      // Expected to get here.
+      assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+      assertTrue(e.getCause().getMessage().matches(".*HashSet.*AClass.*"));
+    }
+  }
+
+  /**
+   * This checks situations in which an RPC message is modified to replace
+   * arguments of a primitive type with another primitive type.
+   */
+  public void testPrimitiveSpoofingArray() {
+    try {
+      RPC.decodeRequest(generateIntSpoofingIntArray());
+      fail("Expected IncompatibleRemoteServiceException from testPrimitiveSpoofingArray (1)");
+    } catch (Exception e) {
+      // Expected to get here. When the int array value is processed, it reads
+      // the
+      // integer value and tries to look it up as a class string, only to run
+      // out
+      // of bounds in the string table.
+      assertTrue(e instanceof ArrayIndexOutOfBoundsException);
+    }
+    try {
+      RPC.decodeRequest(generateStringSpoofingStringArray());
+      fail("Expected IncompatibleRemoteServiceException from testPrimitiveSpoofingArray (2)");
+    } catch (IncompatibleRemoteServiceException e) {
+      // Expected to get here
+      assertEquals(SerializationException.class, e.getCause().getClass());
+    }
+  }
+}
diff --git a/user/test/com/google/gwt/user/server/rpc/RPCTypeCheckCollectionsTest.java b/user/test/com/google/gwt/user/server/rpc/RPCTypeCheckCollectionsTest.java
new file mode 100644
index 0000000..e021530
--- /dev/null
+++ b/user/test/com/google/gwt/user/server/rpc/RPCTypeCheckCollectionsTest.java
@@ -0,0 +1,1258 @@
+/*
+ * Copyright 2011 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.user.server.rpc;
+
+import com.google.gwt.user.client.rpc.IncompatibleRemoteServiceException;
+import com.google.gwt.user.client.rpc.IsSerializable;
+import com.google.gwt.user.client.rpc.RemoteService;
+import com.google.gwt.user.client.rpc.SerializedTypeViolationException;
+import com.google.gwt.user.client.rpc.TestSetFactory.ReverseSorter;
+
+import junit.framework.TestCase;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.IdentityHashMap;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import java.util.Vector;
+
+/**
+ * Collections test cases for the type checking code found in server-side RPC
+ * deserialization.
+ */
+@SuppressWarnings("rawtypes")
+public class RPCTypeCheckCollectionsTest extends TestCase {
+
+  /*
+   * Test top-level generic type mismatches for all custom serialized container
+   * classes. These are all tested using a class that involves a pathological
+   * hash-code object. Hence checking that improper objects are not deserialized
+   * at all (the test will time out if there is a problem).
+   *   - ArrayList<Integer>: testListHashSetSpoofingList
+   *   - ArraysAsList<Integer>: testListHashSetSpoofingList
+   *   - HashMap<String, Integer>: testMapHashSetSpoofingMap
+   *   - HashSet<Integer>: testSetHashSetSpoofingSet
+   *   - IdentityHashMap<AClass, Integer>: testMapHashSetSpoofingMap
+   *   - LinkedHashMap<String, Integer>: testMapHashSetSpoofingMap
+   *   - LinkedHashSet<Integer>: testSetSpoofingSet
+   *   - LinkedList<Integer>: testListHashSetSpoofingList
+   *   - SingletonList<Integer>: testListHashSetSpoofingList
+   *   - TreeMap<String, Integer>: testMapHashSetSpoofingMap
+   *   - TreeSet<Integer>: testSetHashSetSpoofingSet
+   *   - Vector<Integer>: testVectorHashSetSpoofingVector
+   *   
+   * Also tests raw version of all of these types, to verify no false positives
+   * for type violations.
+   *
+   * Test nested generics, at least 2 levels deep:
+   *   - HashMap<String, HashSet<Integer>>: testNestedGenerics
+   *   - HashMap<String, HashMap<Integer, AClass>>: testNestedGenerics
+   *   - HashMap<String, HashMap<Integer, List<Long>>>: testNestedGenerics
+   *   - HashMap<String, HashMap>: testNestedGenerics
+   */  
+  
+  /**
+   * A class containing a method used to check type spoofing attacks on RPC
+   * messages containing collection types.
+   */
+  public static class ContainerParamTestClass implements RemoteService {
+    @SuppressWarnings("unused")
+    public static void testList(List<Integer> arg1, List<Integer> arg2, List<Integer> arg3,
+        List<Integer> arg4) {
+    }
+
+    @SuppressWarnings("unused")
+    public static void testListRaw(List arg1, List arg2, List arg3, List arg4) {
+    }
+
+    @SuppressWarnings("unused")
+    public static void testMap(Map<String, Integer> arg1, Map<String, Integer> arg2,
+        Map<String, Integer> arg3, Map<String, Integer> arg4) { }
+
+    @SuppressWarnings("unused")
+    public static void testMapRaw(Map arg1, Map arg2, Map arg3, Map arg4) { }
+
+    @SuppressWarnings("unused")
+    public static void testSet(Set<Integer> arg1, Set<Integer> arg2, Set<Integer> arg3) { }
+
+    @SuppressWarnings("unused")
+    public static void testSetRaw(Set arg1, Set arg2, Set arg3) { }
+
+    @SuppressWarnings("unused")
+    public static void testVector(Vector<Integer> arg1) { }
+
+    @SuppressWarnings("unused")
+    public static void testVectorRaw(Vector arg1) { }
+
+    @SuppressWarnings("unused")
+    public static void testNestedGenerics(HashMap<String, HashSet<Integer>> arg1,
+        HashMap<String, HashMap<Integer, RPCTypeCheckTest.AClass>> arg2,
+        HashMap<String, HashMap<Integer, List<Long>>> arg3) { }
+
+    @SuppressWarnings("unused")
+    public static void testNestedGenericsRaw(HashMap<String, HashSet> arg1,
+        HashMap<String, HashMap> arg2,
+        HashMap<String, HashMap<Integer, List>> arg3) { }
+  }
+
+  /**
+   * Used to test a generic class that extends another generic class.
+   * 
+   * Also necessary because LinkedHashSet is not serializable alone.
+   */
+  public static class TestHashSet<T> extends LinkedHashSet<T> implements IsSerializable {
+    // Does nothing different.
+  }
+
+  private static String generateArrayListHashSetSpoofingList() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ContainerParamTestClass.class, "testList");
+      
+      ArrayList<HashSet<Integer>> arrayList = new ArrayList<HashSet<Integer>>();
+      HashSet<Integer> hashSet = new HashSet<Integer>();
+      hashSet.add(12345);
+      arrayList.add(hashSet);
+      strFactory.write(arrayList);
+      
+      Integer i = 54321;
+      Integer j = 9876;
+      List<Integer> arrayAsList = Arrays.asList(i, j);
+      strFactory.write(arrayAsList);
+
+      LinkedList<Integer> linkedList = new LinkedList<Integer>();
+      linkedList.add(12345);
+      linkedList.add(67890);
+      strFactory.write(linkedList);
+      
+      Integer k = new Integer(45678);
+      List<Integer> singletonList = Collections.singletonList(k);
+      strFactory.write(singletonList);
+
+      return strFactory.toString();
+      
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  private static String generateArraysAsListHashSetSpoofingList() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ContainerParamTestClass.class, "testList");
+      
+      Integer i = 54321;
+      Integer j = 9876;
+      ArrayList<Integer> arrayList = new ArrayList<Integer>();
+      arrayList.add(i);
+      arrayList.add(j);
+      strFactory.write(arrayList);
+      
+      List<HashSet> arrayAsList = Arrays.asList(RPCTypeCheckFactory.generateTestHashSet());
+      strFactory.write(arrayAsList);
+
+      LinkedList<Integer> linkedList = new LinkedList<Integer>();
+      linkedList.add(12345);
+      linkedList.add(67890);
+      strFactory.write(linkedList);
+      
+      Integer k = new Integer(45678);
+      List<Integer> singletonList = Collections.singletonList(k);
+      strFactory.write(singletonList);
+
+      return strFactory.toString();
+      
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  private static String generateArraysAsListHashSetSpoofingArraysAsList() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ContainerParamTestClass.class, "testList");
+      
+      List<HashSet> arrayAsList = Arrays.asList(RPCTypeCheckFactory.generateTestHashSet());
+      strFactory.write(arrayAsList);
+      
+      LinkedList<Integer> linkedList = new LinkedList<Integer>();
+      linkedList.add(12345);
+      linkedList.add(67890);
+      strFactory.write(linkedList);
+      
+      return strFactory.toString();
+      
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  private static String generateHashMapKeyHashSetSpoofingHashMap() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ContainerParamTestClass.class, "testMap");
+
+      HashMap<HashSet, Integer> hashMap = RPCTypeCheckFactory.generateTestHashMap();
+      strFactory.write(hashMap);
+
+      IdentityHashMap<String, Integer> identityHashMap = new IdentityHashMap<String, Integer>();
+      identityHashMap.put("foo", new Integer(12));
+      identityHashMap.put("bar", new Integer(34));
+      strFactory.write(identityHashMap);
+
+      LinkedHashMap<String, Integer> linkedHashMap = new LinkedHashMap<String, Integer>();
+      linkedHashMap.put("foo", new Integer(56));
+      linkedHashMap.put("bar", new Integer(78));
+      strFactory.write(linkedHashMap);
+
+      TreeMap<String, Integer> treeMap = new TreeMap<String, Integer>();
+      treeMap.put("foo", new Integer(90));
+      treeMap.put("bar", new Integer(12));
+      strFactory.write(treeMap);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  private static String generateHashMapValueHashSetSpoofingHashMap() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ContainerParamTestClass.class, "testMap");
+
+      HashMap<String, HashSet> hashMap = new HashMap<String, HashSet>();
+      hashMap.put("foo", RPCTypeCheckFactory.generateTestHashSet());
+      strFactory.write(hashMap);
+
+      IdentityHashMap<String, Integer> identityHashMap = new IdentityHashMap<String, Integer>();
+      identityHashMap.put("foo", new Integer(12));
+      identityHashMap.put("bar", new Integer(34));
+      strFactory.write(identityHashMap);
+
+      LinkedHashMap<String, Integer> linkedHashMap = new LinkedHashMap<String, Integer>();
+      linkedHashMap.put("foo", new Integer(56));
+      linkedHashMap.put("bar", new Integer(78));
+      strFactory.write(linkedHashMap);
+
+      TreeMap<String, Integer> treeMap = new TreeMap<String, Integer>();
+      treeMap.put("foo", new Integer(90));
+      treeMap.put("bar", new Integer(12));
+      strFactory.write(treeMap);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  private static String generateHashSetHashSetSpoofingSetInteger() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ContainerParamTestClass.class, "testSet");
+
+      HashSet hashSet = RPCTypeCheckFactory.generateTestHashSet();
+      strFactory.write(hashSet);
+
+      ReverseSorter<Integer> sorter = new ReverseSorter<Integer>();
+      TreeSet<Integer> treeSet = new TreeSet<Integer>(sorter);
+      treeSet.add(12345);
+      treeSet.add(67890);
+      strFactory.write(treeSet);
+
+      TestHashSet<Integer> treeHashSet = new TestHashSet<Integer>();
+      treeHashSet.add(12345);
+      treeHashSet.add(67890);
+      strFactory.write(treeHashSet);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  private static String generateIdentityHashMapKeyHashSetSpoofingIdentityHashMap() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ContainerParamTestClass.class, "testMap");
+
+      HashMap<String, Integer> hashMap = new HashMap<String, Integer>();
+      hashMap.put("foo", new Integer(12));
+      hashMap.put("bar", new Integer(34));
+      strFactory.write(hashMap);
+
+      IdentityHashMap<HashSet, Integer> identityHashMap =
+        RPCTypeCheckFactory.generateTestIdentityHashMap();
+      strFactory.write(identityHashMap);
+
+      LinkedHashMap<String, Integer> linkedHashMap = new LinkedHashMap<String, Integer>();
+      linkedHashMap.put("foo", new Integer(56));
+      linkedHashMap.put("bar", new Integer(78));
+      strFactory.write(linkedHashMap);
+
+      TreeMap<String, Integer> treeMap = new TreeMap<String, Integer>();
+      treeMap.put("foo", new Integer(90));
+      treeMap.put("bar", new Integer(12));
+      strFactory.write(treeMap);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  private static String generateIdentityHashMapValueHashSetSpoofingIdentityHashMap() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ContainerParamTestClass.class, "testMap");
+
+      HashMap<String, Integer> hashMap = new HashMap<String, Integer>();
+      hashMap.put("foo", new Integer(12));
+      hashMap.put("bar", new Integer(34));
+      strFactory.write(hashMap);
+
+      IdentityHashMap<String, HashSet> identityHashMap = new IdentityHashMap<String, HashSet>();
+      identityHashMap.put("foo", RPCTypeCheckFactory.generateTestHashSet());
+      strFactory.write(identityHashMap);
+
+      LinkedHashMap<String, Integer> linkedHashMap = new LinkedHashMap<String, Integer>();
+      linkedHashMap.put("foo", new Integer(56));
+      linkedHashMap.put("bar", new Integer(78));
+      strFactory.write(linkedHashMap);
+
+      TreeMap<String, Integer> treeMap = new TreeMap<String, Integer>();
+      treeMap.put("foo", new Integer(90));
+      treeMap.put("bar", new Integer(12));
+      strFactory.write(treeMap);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  private static String generateLinkedHashMapKeyHashSetSpoofingLinkedHashMap() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ContainerParamTestClass.class, "testMap");
+
+      HashMap<String, Integer> hashMap = new HashMap<String, Integer>();
+      hashMap.put("foo", new Integer(12));
+      hashMap.put("bar", new Integer(34));
+      strFactory.write(hashMap);
+
+      IdentityHashMap<String, Integer> identityHashMap = new IdentityHashMap<String, Integer>();
+      identityHashMap.put("foo", new Integer(56));
+      identityHashMap.put("bar", new Integer(78));
+      strFactory.write(identityHashMap);
+
+      LinkedHashMap<HashSet, Integer> linkedHashMap =
+        RPCTypeCheckFactory.generateTestLinkedHashMap();
+      strFactory.write(linkedHashMap);
+
+      TreeMap<String, Integer> treeMap = new TreeMap<String, Integer>();
+      treeMap.put("foo", new Integer(90));
+      treeMap.put("bar", new Integer(12));
+      strFactory.write(treeMap);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  private static String generateLinkedHashMapValueHashSetSpoofingLinkedHashMap() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ContainerParamTestClass.class, "testMap");
+
+      HashMap<String, Integer> hashMap = new HashMap<String, Integer>();
+      hashMap.put("foo", new Integer(12));
+      hashMap.put("bar", new Integer(34));
+      strFactory.write(hashMap);
+
+      IdentityHashMap<String, Integer> identityHashMap = new IdentityHashMap<String, Integer>();
+      identityHashMap.put("foo", new Integer(56));
+      identityHashMap.put("bar", new Integer(78));
+      strFactory.write(identityHashMap);
+
+      LinkedHashMap<String, HashSet> linkedHashMap = new LinkedHashMap<String, HashSet>();
+      linkedHashMap.put("foo", RPCTypeCheckFactory.generateTestHashSet());
+      strFactory.write(linkedHashMap);
+
+      TreeMap<String, Integer> treeMap = new TreeMap<String, Integer>();
+      treeMap.put("foo", new Integer(90));
+      treeMap.put("bar", new Integer(12));
+      strFactory.write(treeMap);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  private static String generateLinkedHashSetHashSetSpoofingSetInteger() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ContainerParamTestClass.class, "testSet");
+
+      HashSet<Integer> hashSet = new HashSet<Integer>();
+      hashSet.add(12345);
+      hashSet.add(67890);
+      strFactory.write(hashSet);
+
+      ReverseSorter<Integer> sorter = new ReverseSorter<Integer>();
+      TreeSet<Integer> treeSet = new TreeSet<Integer>(sorter);
+      treeSet.add(12345);
+      treeSet.add(67890);
+      strFactory.write(treeSet);
+
+      LinkedHashSet linkedHashSet = RPCTypeCheckFactory.generateTestLinkedHashSet();
+      strFactory.write(linkedHashSet);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  private static String generateLinkedListHashSetSpoofingList() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ContainerParamTestClass.class, "testList");
+
+      ArrayList<Integer> arrayList = new ArrayList<Integer>();
+      arrayList.add(12345);
+      arrayList.add(67890);
+      strFactory.write(arrayList);
+
+      Integer i = 54321;
+      Integer j = 9876;
+      List<Integer> arrayAsList = Arrays.asList(i, j);
+      strFactory.write(arrayAsList);
+
+      LinkedList<HashSet> linkedList = new LinkedList<HashSet>();
+      linkedList.add(RPCTypeCheckFactory.generateTestHashSet());
+      strFactory.write(linkedList);
+
+      Integer k = new Integer(45678);
+      List<Integer> singletonList = Collections.singletonList(k);
+      strFactory.write(singletonList);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  private static String generateListStringSpoofingListInteger() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ContainerParamTestClass.class, "testList");
+
+      ArrayList<String> arrayList = new ArrayList<String>();
+      arrayList.add("foo");
+      arrayList.add("bar");
+      strFactory.write(arrayList);
+
+      LinkedList<String> linkedList = new LinkedList<String>();
+      linkedList.add("foo");
+      linkedList.add("bar");
+      strFactory.write(linkedList);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  private static String generateListValid(String methodName) {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ContainerParamTestClass.class, methodName);
+
+      ArrayList<Integer> arrayList = new ArrayList<Integer>();
+      arrayList.add(12345);
+      arrayList.add(67890);
+      strFactory.write(arrayList);
+
+      Integer i = 54321;
+      Integer j = 9876;
+      List<Integer> arrayAsList = Arrays.asList(i, j);
+      strFactory.write(arrayAsList);
+
+      LinkedList<Integer> linkedList = new LinkedList<Integer>();
+      linkedList.add(12345);
+      linkedList.add(67890);
+      strFactory.write(linkedList);
+
+      Integer k = new Integer(45678);
+      List<Integer> singletonList = Collections.singletonList(k);
+      strFactory.write(singletonList);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  private static String generateMapRaw() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ContainerParamTestClass.class, "testMapRaw");
+
+      HashMap<String, Integer> hashMap = new HashMap<String, Integer>();
+      hashMap.put("foo", 1020);
+      strFactory.write(hashMap);
+
+      IdentityHashMap<String, Integer> identityHashMap = new IdentityHashMap<String, Integer>();
+      identityHashMap.put("foo", new Integer(12));
+      identityHashMap.put("bar", new Integer(34));
+      strFactory.write(identityHashMap);
+
+      LinkedHashMap<String, Integer> linkedHashMap = new LinkedHashMap<String, Integer>();
+      linkedHashMap.put("foo", new Integer(56));
+      linkedHashMap.put("bar", new Integer(78));
+      strFactory.write(linkedHashMap);
+
+      TreeMap<String, Integer> treeMap = new TreeMap<String, Integer>();
+      treeMap.put("foo", new Integer(90));
+      treeMap.put("bar", new Integer(12));
+      strFactory.write(treeMap);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  @SuppressWarnings("unchecked")
+  private static String generateNestedGenericsA() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ContainerParamTestClass.class, "testNestedGenerics");
+
+      HashMap<String, HashSet<HashSet>> arg1 = new HashMap<String, HashSet<HashSet>>();
+      HashSet<HashSet> entry1 = RPCTypeCheckFactory.generateTestHashSetHashSet();
+      arg1.put("foo", entry1);
+      strFactory.write(arg1);
+
+      HashMap<String, HashMap<Integer, RPCTypeCheckTest.AClass>> arg2 =
+          new HashMap<String, HashMap<Integer, RPCTypeCheckTest.AClass>>();
+      HashMap<Integer, RPCTypeCheckTest.AClass> entry2 =
+          new HashMap<Integer, RPCTypeCheckTest.AClass>();
+      entry2.put(12345, new RPCTypeCheckTest.AClass(1));
+      arg2.put("bar", entry2);
+      strFactory.write(arg2);
+
+      HashMap<String, HashMap<Integer, List<Long>>> arg3 =
+          new HashMap<String, HashMap<Integer, List<Long>>>();
+      HashMap<Integer, List<Long>> entry3 = new HashMap<Integer, List<Long>>();
+      List<Long> entry4 = new LinkedList<Long>();
+      entry4.add(new Long(987654321));
+      entry3.put(67890, entry4);
+      arg3.put("baz", entry3);
+      strFactory.write(arg3);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  private static String generateNestedGenericsB() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ContainerParamTestClass.class, "testNestedGenerics");
+
+      HashMap<String, HashSet<Integer>> arg1 = new HashMap<String, HashSet<Integer>>();
+      HashSet<Integer> entry1 = new HashSet<Integer>();
+      entry1.add(12345);
+      arg1.put("foo", entry1);
+      strFactory.write(arg1);
+
+      HashMap<String, HashMap<Integer, HashSet>> arg2 =
+          new HashMap<String, HashMap<Integer, HashSet>>();
+      HashMap<Integer, HashSet> entry2 = new HashMap<Integer, HashSet>();
+      entry2.put(12345, RPCTypeCheckFactory.generateTestHashSet());
+      arg2.put("bar", entry2);
+      strFactory.write(arg2);
+
+      HashMap<String, HashMap<Integer, List<Long>>> arg3 =
+          new HashMap<String, HashMap<Integer, List<Long>>>();
+      HashMap<Integer, List<Long>> entry3 = new HashMap<Integer, List<Long>>();
+      List<Long> entry4 = new LinkedList<Long>();
+      entry4.add(new Long(987654321));
+      entry3.put(67890, entry4);
+      arg3.put("baz", entry3);
+      strFactory.write(arg3);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  private static String generateNestedGenericsC() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ContainerParamTestClass.class, "testNestedGenerics");
+
+      HashMap<String, HashSet<Integer>> arg1 = new HashMap<String, HashSet<Integer>>();
+      HashSet<Integer> entry1 = new HashSet<Integer>();
+      entry1.add(12345);
+      arg1.put("foo", entry1);
+      strFactory.write(arg1);
+
+      HashMap<String, HashMap<Integer, RPCTypeCheckTest.AClass>> arg2 =
+          new HashMap<String, HashMap<Integer, RPCTypeCheckTest.AClass>>();
+      HashMap<Integer, RPCTypeCheckTest.AClass> entry2 =
+          new HashMap<Integer, RPCTypeCheckTest.AClass>();
+      entry2.put(12345, new RPCTypeCheckTest.AClass(1));
+      arg2.put("bar", entry2);
+      strFactory.write(arg2);
+
+      HashMap<String, HashMap<Integer, List<HashSet>>> arg3 =
+          new HashMap<String, HashMap<Integer, List<HashSet>>>();
+      HashMap<Integer, List<HashSet>> entry3 = new HashMap<Integer, List<HashSet>>();
+      List<HashSet> entry4 = new LinkedList<HashSet>();
+      entry4.add(RPCTypeCheckFactory.generateTestHashSet());
+      entry3.put(67890, entry4);
+      arg3.put("baz", entry3);
+      strFactory.write(arg3);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  private static String generateNestedGenericsRaw() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ContainerParamTestClass.class, "testNestedGenericsRaw");
+
+      HashMap<String, HashSet<Integer>> arg1 = new HashMap<String, HashSet<Integer>>();
+      HashSet<Integer> entry1 = new HashSet<Integer>();
+      entry1.add(12345);
+      arg1.put("foo", entry1);
+      strFactory.write(arg1);
+
+      HashMap<String, HashMap<Integer, RPCTypeCheckTest.AClass>> arg2 =
+          new HashMap<String, HashMap<Integer, RPCTypeCheckTest.AClass>>();
+      HashMap<Integer, RPCTypeCheckTest.AClass> entry2 =
+          new HashMap<Integer, RPCTypeCheckTest.AClass>();
+      entry2.put(12345, new RPCTypeCheckTest.AClass(1));
+      arg2.put("bar", entry2);
+      strFactory.write(arg2);
+
+      HashMap<String, HashMap<Integer, List<Long>>> arg3 =
+          new HashMap<String, HashMap<Integer, List<Long>>>();
+      HashMap<Integer, List<Long>> entry3 = new HashMap<Integer, List<Long>>();
+      List<Long> entry4 = new LinkedList<Long>();
+      entry4.add(new Long(987654321));
+      entry3.put(67890, entry4);
+      arg3.put("baz", entry3);
+      strFactory.write(arg3);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  private static String generateSetStringSpoofingSetInteger() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ContainerParamTestClass.class, "testSet");
+
+      HashSet<Integer> hashSet = new HashSet<Integer>();
+      hashSet.add(12345);
+      hashSet.add(67890);
+      strFactory.write(hashSet);
+
+      ReverseSorter<String> sorter = new ReverseSorter<String>();
+      TreeSet<String> treeSet = new TreeSet<String>(sorter);
+      treeSet.add("foo");
+      treeSet.add("bar");
+      strFactory.write(treeSet);
+
+      TestHashSet<Integer> testHashSet = new TestHashSet<Integer>();
+      testHashSet.add(12345);
+      testHashSet.add(67890);
+      strFactory.write(testHashSet);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  private static String generateSetValid(String methodName) {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ContainerParamTestClass.class, methodName);
+
+      HashSet<Integer> hashSet = new HashSet<Integer>();
+      hashSet.add(12345);
+      hashSet.add(67890);
+      strFactory.write(hashSet);
+
+      ReverseSorter<Integer> sorter = new ReverseSorter<Integer>();
+      TreeSet<Integer> treeSet = new TreeSet<Integer>(sorter);
+      treeSet.add(12345);
+      treeSet.add(67890);
+      strFactory.write(treeSet);
+
+      TestHashSet<Integer> treeHashSet = new TestHashSet<Integer>();
+      treeHashSet.add(12345);
+      treeHashSet.add(67890);
+      strFactory.write(treeHashSet);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  private static String generateSingletonListHashSetSpoofingList() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ContainerParamTestClass.class, "testList");
+
+      ArrayList<Integer> arrayList = new ArrayList<Integer>();
+      arrayList.add(12345);
+      arrayList.add(67890);
+      strFactory.write(arrayList);
+
+      Integer i = 54321;
+      Integer j = 9876;
+      List<Integer> arrayAsList = Arrays.asList(i, j);
+      strFactory.write(arrayAsList);
+
+      LinkedList<Integer> linkedList = new LinkedList<Integer>();
+      linkedList.add(12345);
+      linkedList.add(67890);
+      strFactory.write(linkedList);
+
+      List<HashSet> singletonList =
+          Collections.singletonList(RPCTypeCheckFactory.generateTestHashSet());
+      strFactory.write(singletonList);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  private static String generateTreeMapKeyHashSetSpoofingTreeMap() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ContainerParamTestClass.class, "testMap");
+
+      HashMap<String, Integer> hashMap = new HashMap<String, Integer>();
+      hashMap.put("foo", new Integer(12));
+      hashMap.put("bar", new Integer(34));
+      strFactory.write(hashMap);
+
+      IdentityHashMap<String, Integer> identityHashMap = new IdentityHashMap<String, Integer>();
+      identityHashMap.put("foo", new Integer(56));
+      identityHashMap.put("bar", new Integer(78));
+      strFactory.write(identityHashMap);
+
+      LinkedHashMap<String, Integer> linkedHashMap = new LinkedHashMap<String, Integer>();
+      linkedHashMap.put("foo", new Integer(90));
+      linkedHashMap.put("bar", new Integer(12));
+      strFactory.write(linkedHashMap);
+
+      TreeMap<HashSet, Integer> treeMap = new TreeMap<HashSet, Integer>();
+      treeMap.put(RPCTypeCheckFactory.generateTestHashSet(), 12345);
+      strFactory.write(treeMap);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  private static String generateTreeMapValueHashSetSpoofingTreeMap() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ContainerParamTestClass.class, "testMap");
+
+      HashMap<String, Integer> hashMap = new HashMap<String, Integer>();
+      hashMap.put("foo", new Integer(12));
+      hashMap.put("bar", new Integer(34));
+      strFactory.write(hashMap);
+
+      IdentityHashMap<String, Integer> identityHashMap = new IdentityHashMap<String, Integer>();
+      identityHashMap.put("foo", new Integer(56));
+      identityHashMap.put("bar", new Integer(78));
+      strFactory.write(identityHashMap);
+
+      LinkedHashMap<String, Integer> linkedHashMap = new LinkedHashMap<String, Integer>();
+      linkedHashMap.put("foo", new Integer(90));
+      linkedHashMap.put("bar", new Integer(12));
+      strFactory.write(linkedHashMap);
+
+      TreeMap<String, HashSet> treeMap = new TreeMap<String, HashSet>();
+      treeMap.put("foo", RPCTypeCheckFactory.generateTestHashSet());
+      strFactory.write(treeMap);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  @SuppressWarnings("unchecked")
+  private static String generateTreeSetHashSetSpoofingSetInteger() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ContainerParamTestClass.class, "testSet");
+
+      HashSet<Integer> hashSet = new HashSet<Integer>();
+      hashSet.add(12345);
+      hashSet.add(67890);
+      strFactory.write(hashSet);
+
+      ReverseSorter sorter = new ReverseSorter();
+      TreeSet treeSet = new TreeSet(sorter);
+      treeSet.add(RPCTypeCheckFactory.generateTestHashSet());
+      strFactory.write(treeSet);
+
+      TestHashSet<Integer> treeHashSet = new TestHashSet<Integer>();
+      treeHashSet.add(12345);
+      treeHashSet.add(67890);
+      strFactory.write(treeHashSet);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  private static String generateVectorHashSetSpoofingVector() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ContainerParamTestClass.class, "testVector");
+
+      Vector<HashSet> vector = new Vector<HashSet>();
+      vector.add(RPCTypeCheckFactory.generateTestHashSet());
+      strFactory.write(vector);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  private static String generateVectorRaw() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ContainerParamTestClass.class, "testVectorRaw");
+
+      Vector<Integer> vector = new Vector<Integer>();
+      vector.add(12345);
+      vector.add(67890);
+      strFactory.write(vector);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  /**
+   * This checks that a List generated by Arrays.asList correctly reports that
+   * it is an incorrect type.
+   */
+  public void testArraysAsListHashSetSpoofingArraysAsList() {
+    try {
+      RPC.decodeRequest(generateArraysAsListHashSetSpoofingArraysAsList());
+      fail("Expected IncompatibleRemoteServiceException from " +
+          "testArraysAsListHashSetSpoofingArraysAsList");
+    } catch (IncompatibleRemoteServiceException e) {
+      // Expected to get here
+      assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+      assertTrue(e.getCause().getMessage().matches(".*HashSet.*Integer.*"));
+    }
+  }
+
+  /**
+   * This checks that List correctly handles correct types, and reports when it
+   * gets and incorrectly parameterized generic type.
+   */
+  public void testListSpoofingList() {
+    try {
+      RPC.decodeRequest(generateListValid("testList"));
+    } catch (Exception e) {
+      fail("Received unexpected Exception from testListSpoofingList (a)" + e.getMessage());
+    }
+    try {
+      RPC.decodeRequest(generateListStringSpoofingListInteger());
+      fail("Expected IncompatibleRemoteServiceException from testListSpoofingList (1)");
+    } catch (IncompatibleRemoteServiceException e) {
+      // Expected to get here
+      assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+      assertTrue(e.getCause().getMessage().matches(".*String.*Integer.*"));
+    }
+    try {
+      RPC.decodeRequest(generateArrayListHashSetSpoofingList());
+      fail("Expected IncompatibleRemoteServiceException from testListSpoofingList (2)");
+    } catch (IncompatibleRemoteServiceException e) {
+      // Expected to get here
+      assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+      assertTrue(e.getCause().getMessage().matches(".*HashSet.*Integer.*"));
+    }
+    try {
+      RPC.decodeRequest(generateArraysAsListHashSetSpoofingList());
+      fail("Expected IncompatibleRemoteServiceException from testListSpoofingList (3)");
+    } catch (IncompatibleRemoteServiceException e) {
+      // Expected to get here
+      assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+      assertTrue(e.getCause().getMessage().matches(".*HashSet.*Integer.*"));
+    }
+    try {
+      RPC.decodeRequest(generateLinkedListHashSetSpoofingList());
+      fail("Expected IncompatibleRemoteServiceException from testListSpoofingList (4)");
+    } catch (IncompatibleRemoteServiceException e) {
+      // Expected to get here
+      assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+      assertTrue(e.getCause().getMessage().matches(".*HashSet.*Integer.*"));
+    }
+    try {
+      RPC.decodeRequest(generateSingletonListHashSetSpoofingList());
+      fail("Expected IncompatibleRemoteServiceException from testListSpoofingList (5)");
+    } catch (IncompatibleRemoteServiceException e) {
+      // Expected to get here
+      assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+      assertTrue(e.getCause().getMessage().matches(".*HashSet.*Integer.*"));
+    }
+  }
+
+  /**
+   * This checks that List correctly handles correct types, and reports when it
+   * gets and incorrectly parameterized generic type.
+   */
+  public void testListSpoofingListRaw() {
+    try {
+      RPC.decodeRequest(generateListValid("testListRaw"));
+    } catch (Exception e) {
+      fail("Received unexpected Exception from testListSpoofingListRaw" + e.getMessage());
+    }
+  }
+
+  /**
+   * This checks that Maps correctly reports when it's generic parameter types
+   * do not match the actual contents.
+   */
+  public void testMapHashSetSpoofingMap() {
+    try {
+      RPC.decodeRequest(generateHashMapKeyHashSetSpoofingHashMap());
+      fail("Expected IncompatibleRemoteServiceException from " +
+          "testHashMapHashSetSpoofingHashMap (1)");
+    } catch (IncompatibleRemoteServiceException e) {
+      // Expected to get here
+      assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+      assertTrue(e.getCause().getMessage().matches(".*HashSet.*String.*"));
+    }
+    try {
+      RPC.decodeRequest(generateHashMapValueHashSetSpoofingHashMap());
+      fail("Expected IncompatibleRemoteServiceException from " +
+          "testHashMapHashSetSpoofingHashMap (2)");
+    } catch (IncompatibleRemoteServiceException e) {
+      // Expected to get here
+      assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+      assertTrue(e.getCause().getMessage().matches(".*HashSet.*Integer.*"));
+    }
+    try {
+      RPC.decodeRequest(generateIdentityHashMapKeyHashSetSpoofingIdentityHashMap());
+      fail("Expected IncompatibleRemoteServiceException from " +
+          "testHashMapHashSetSpoofingHashMap (3)");
+    } catch (IncompatibleRemoteServiceException e) {
+      // Expected to get here
+      assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+      assertTrue(e.getCause().getMessage().matches(".*HashSet.*String.*"));
+    }
+    try {
+      RPC.decodeRequest(generateIdentityHashMapValueHashSetSpoofingIdentityHashMap());
+      fail("Expected IncompatibleRemoteServiceException from " +
+          "testHashMapHashSetSpoofingHashMap (4)");
+    } catch (IncompatibleRemoteServiceException e) {
+      // Expected to get here
+      assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+      assertTrue(e.getCause().getMessage().matches(".*HashSet.*Integer.*"));
+    }
+    try {
+      RPC.decodeRequest(generateLinkedHashMapKeyHashSetSpoofingLinkedHashMap());
+      fail("Expected IncompatibleRemoteServiceException from " +
+          "testHashMapHashSetSpoofingHashMap (5)");
+    } catch (IncompatibleRemoteServiceException e) {
+      // Expected to get here
+      assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+      assertTrue(e.getCause().getMessage().matches(".*HashSet.*String.*"));
+    }
+    try {
+      RPC.decodeRequest(generateLinkedHashMapValueHashSetSpoofingLinkedHashMap());
+      fail("Expected IncompatibleRemoteServiceException from " +
+          "testHashMapHashSetSpoofingHashMap (6)");
+    } catch (IncompatibleRemoteServiceException e) {
+      // Expected to get here
+      assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+      assertTrue(e.getCause().getMessage().matches(".*HashSet.*Integer.*"));
+    }
+    try {
+      RPC.decodeRequest(generateTreeMapKeyHashSetSpoofingTreeMap());
+      fail("Expected IncompatibleRemoteServiceException from " +
+          "testHashMapHashSetSpoofingHashMap (7)");
+    } catch (IncompatibleRemoteServiceException e) {
+      // Expected to get here
+      assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+      assertTrue(e.getCause().getMessage().matches(".*HashSet.*String.*"));
+    }
+    try {
+      RPC.decodeRequest(generateTreeMapValueHashSetSpoofingTreeMap());
+      fail("Expected IncompatibleRemoteServiceException from " +
+          "testHashMapHashSetSpoofingHashMap (8)");
+    } catch (IncompatibleRemoteServiceException e) {
+      // Expected to get here
+      assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+      assertTrue(e.getCause().getMessage().matches(".*HashSet.*Integer.*"));
+    }
+  }
+
+  /**
+   * This checks that Maps correctly processes raw maps without type information.
+   */
+  public void testMapRaw() {
+    try {
+      RPC.decodeRequest(generateMapRaw());
+    } catch (Exception e) {
+      fail("Unexpected Exception from testMapRaw: " + e.getMessage());
+    }
+  }
+
+  /**
+   * This checks that nested generics types are checked correctly.
+   */
+  public void testNestedGenerics() {
+    try {
+      RPC.decodeRequest(generateNestedGenericsA());
+      fail("Expected IncompatibleRemoteServiceException from testNestedGenerics (1)");
+    } catch (IncompatibleRemoteServiceException e) {
+      // Expected to get here
+      assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+      assertTrue(e.getCause().getMessage().matches(".*HashSet.*Integer.*"));
+    }
+    try {
+      RPC.decodeRequest(generateNestedGenericsB());
+      fail("Expected IncompatibleRemoteServiceException from testNestedGenerics (2)");
+    } catch (IncompatibleRemoteServiceException e) {
+      // Expected to get here
+      assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+      assertTrue(e.getCause().getMessage().matches(".*HashSet.*AClass.*"));
+    }
+    try {
+      RPC.decodeRequest(generateNestedGenericsC());
+      fail("Expected IncompatibleRemoteServiceException from testNestedGenerics (3)");
+    } catch (IncompatibleRemoteServiceException e) {
+      // Expected to get here
+      assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+      assertTrue(e.getCause().getMessage().matches(".*HashSet.*Long.*"));
+    }
+  }
+
+  /**
+   * This checks that raw nested generics types are checked correctly.
+   */
+  public void testNestedGenericsRaw() {
+    try {
+      RPC.decodeRequest(generateNestedGenericsRaw());
+    } catch (Exception e) {
+      fail("Unexpected Exception from testNestedGenericsRaw: " + e.getMessage());
+    }
+  }
+
+  /**
+   * This checks that Set correctly handles correct types, and reports when it
+   * gets and incorrectly parameterized generic type.
+   */
+  public void testSetHashSetSpoofingSet() {
+    try {
+      RPC.decodeRequest(generateSetValid("testSet"));
+    } catch (Exception e) {
+      fail("Received unexpected Exception from testSetSpoofingSet (1)" + e.getMessage());
+    }
+    try {
+      RPC.decodeRequest(generateSetStringSpoofingSetInteger());
+      fail("Expected IncompatibleRemoteServiceException from testSetSpoofingSet (2)");
+    } catch (IncompatibleRemoteServiceException e) {
+      // Expected to get here
+      assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+      assertTrue(e.getCause().getMessage().matches(".*String.*Integer.*"));
+    }
+    try {
+      RPC.decodeRequest(generateHashSetHashSetSpoofingSetInteger());
+      fail("Expected IncompatibleRemoteServiceException from testSetSpoofingSet (3)");
+    } catch (IncompatibleRemoteServiceException e) {
+      // Expected to get here
+      assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+      assertTrue(e.getCause().getMessage().matches(".*HashSet.*Integer.*"));
+    }
+    try {
+      RPC.decodeRequest(generateLinkedHashSetHashSetSpoofingSetInteger());
+      fail("Expected IncompatibleRemoteServiceException from testSetSpoofingSet (4)");
+    } catch (IncompatibleRemoteServiceException e) {
+      // Expected to get here
+      assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+      assertTrue(e.getCause().getMessage().matches(".*HashSet.*Integer.*"));
+    }
+    try {
+      RPC.decodeRequest(generateTreeSetHashSetSpoofingSetInteger());
+      fail("Expected IncompatibleRemoteServiceException from testSetSpoofingSet (5)");
+    } catch (IncompatibleRemoteServiceException e) {
+      // Expected to get here
+      assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+      assertTrue(e.getCause().getMessage().matches(".*HashSet.*Integer.*"));
+    }
+  }
+
+  /**
+   * This checks that a raw Set does not generate errors.
+   */
+  public void testSetRaw() {
+    try {
+      RPC.decodeRequest(generateSetValid("testSetRaw"));
+    } catch (Exception e) {
+      fail("Received unexpected Exception from testSetRaw:" + e.getMessage());
+    }
+  }
+
+  /**
+   * This checks that Vector correctly reports an incorrect generic type.
+   */
+  public void testVectorHashSetSpoofingVector() {
+    try {
+      RPC.decodeRequest(generateVectorHashSetSpoofingVector());
+      fail("Expected IncompatibleRemoteServiceException from testVectorHashSetSpoofingVector");
+    } catch (IncompatibleRemoteServiceException e) {
+      // Expected to get here
+      assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+      assertTrue(e.getCause().getMessage().matches(".*HashSet.*Integer.*"));
+    }
+  }
+
+  /**
+   * This checks that a raw Vector allows any type. 
+   */
+  public void testVectorRaw() {
+    try {
+      RPC.decodeRequest(generateVectorRaw());
+    } catch (Exception e) {
+      fail("Received unexpected Exception from testVectorRaw:" + e.getMessage());
+    }
+  }
+}
diff --git a/user/test/com/google/gwt/user/server/rpc/RPCTypeCheckFactory.java b/user/test/com/google/gwt/user/server/rpc/RPCTypeCheckFactory.java
new file mode 100644
index 0000000..9801f82
--- /dev/null
+++ b/user/test/com/google/gwt/user/server/rpc/RPCTypeCheckFactory.java
@@ -0,0 +1,558 @@
+/*
+ * Copyright 2011 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.user.server.rpc;
+
+import static com.google.gwt.user.client.rpc.impl.AbstractSerializationStream.RPC_SEPARATOR_CHAR;
+
+import com.google.gwt.user.client.rpc.SerializationException;
+import com.google.gwt.user.client.rpc.impl.AbstractSerializationStream;
+import com.google.gwt.user.server.rpc.RPCTypeCheckCollectionsTest.TestHashSet;
+import com.google.gwt.user.server.rpc.impl.SerializabilityUtil;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.IdentityHashMap;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import java.util.Vector;
+
+/**
+ * Generates RPC messages for use in testing server message handling.
+ * 
+ * This class is designed to make it easy to generate arbitrary messages, in the
+ * sense that the elements in the string do not need to match the method's
+ * expected elements in any way.
+ * 
+ */
+public class RPCTypeCheckFactory {
+  /**
+   * A Serialization policy that allows anything to be serialized.
+   */
+  public static class TestSerializationPolicy extends SerializationPolicy {
+    @Override
+    public boolean shouldDeserializeFields(Class<?> clazz) {
+      return true;
+    }
+
+    @Override
+    public boolean shouldSerializeFields(Class<?> clazz) {
+      return true;
+    }
+
+    @SuppressWarnings("unused")
+    @Override
+    public void validateDeserialize(Class<?> clazz) throws SerializationException {
+    }
+
+    @SuppressWarnings("unused")
+    @Override
+    public void validateSerialize(Class<?> clazz) throws SerializationException {
+    }
+  }
+
+  private static final TestSerializationPolicy testSerializationPolicy =
+      new TestSerializationPolicy();
+
+  /**
+   * Generates a HashMap containing HashSet for testing.
+   */
+  @SuppressWarnings({"rawtypes", "unchecked"})
+  public static HashMap<HashSet, Integer> generateTestHashMap() {
+    HashMap<HashSet, Integer> result = new HashMap<HashSet, Integer>();
+    HashSet entry1 = new HashSet();
+    entry1.add(0);
+    result.put(entry1, 12345);
+
+    return result;
+  }
+
+  /**
+   * Generates a HashSet of HashSet for testing.
+   */
+  @SuppressWarnings({"rawtypes", "unchecked"})
+  public static HashSet generateTestHashSet() {
+    HashSet result = new HashSet();
+    HashSet entry1 = new HashSet();
+    HashSet entry2 = new HashSet();
+    entry1.add(12345);
+    entry1.add(67890);
+    result.add(entry1);
+    result.add(entry2);
+
+    return result;
+  }
+  /**
+   * Generates a HashSet that contains a HashSet of HashSets for testing.
+   */
+  @SuppressWarnings({"rawtypes", "unchecked"})
+  public static HashSet generateTestHashSetHashSet() {
+    HashSet result = new HashSet();
+    HashSet entry0 = new HashSet();
+    HashSet entry1 = new HashSet();
+    HashSet entry2 = new HashSet();
+    entry1.add(12345);
+    entry2.add(67890);
+    entry0.add(entry1);
+    entry0.add(entry2);
+    result.add(entry0);
+
+    return result;
+  }
+
+  /**
+   * Generates a IdentityHashMap that contains a HashSet for testing.
+   */
+  @SuppressWarnings({"rawtypes", "unchecked"})
+  public static IdentityHashMap<HashSet, Integer> generateTestIdentityHashMap() {
+    IdentityHashMap<HashSet, Integer> result = new IdentityHashMap<HashSet, Integer>();
+    HashSet entry1 = new HashSet();
+    HashSet entry2 = new HashSet();
+    entry1.add(12345);
+    entry2.add(67890);
+    result.put(entry1, 12345);
+    result.put(entry2, 67890);
+
+    return result;
+  }
+  /**
+   * Generates a LinkedHashMap that contains a HashSet for testing.
+   */
+  @SuppressWarnings({"unchecked", "rawtypes"})
+  public static LinkedHashMap<HashSet, Integer> generateTestLinkedHashMap() {
+    LinkedHashMap<HashSet, Integer> result = new LinkedHashMap<HashSet, Integer>();
+    HashSet entry1 = new HashSet();
+    HashSet entry2 = new HashSet();
+    entry1.add(12345);
+    entry2.add(67890);
+    result.put(entry1, 12345);
+    result.put(entry2, 67890);
+
+    return result;
+  }
+
+  /**
+   * Generates a LinkedHashSet that takes a very long time to deserialize.
+   * 
+   * @return A LinkedHashSet that cannot be used but requires computation of an
+   *         exponential number of hash codes.
+   */
+  @SuppressWarnings({"rawtypes", "unchecked"})
+  public static TestHashSet generateTestLinkedHashSet() {
+    TestHashSet result = new TestHashSet();
+    HashSet entry1 = new HashSet();
+    HashSet entry2 = new HashSet();
+    entry1.add(12345);
+    entry2.add(67890);
+    result.add(entry1);
+    result.add(entry2);
+
+    return result;
+  }
+
+  private static String generateSerializedClassString(Class<?> instanceType) {
+    if (instanceType == int.class) {
+      return "I";
+    }
+
+    return instanceType.getName() + "/"
+        + SerializabilityUtil.getSerializationSignature(instanceType, testSerializationPolicy);
+  }
+
+  private String headerString = "";
+
+  private String bodyString = "";
+
+  private LinkedHashMap<String, Integer> stringTable;
+
+  private IdentityHashMap<Object, Integer> encodedObjectTable;
+
+  /**
+   * Create a new Factory that will generate an RPC string to invoke the given
+   * method found in the given class.
+   * 
+   * @throws NoSuchMethodException when the method requested does not exist on
+   *           the class provided.
+   */
+  public RPCTypeCheckFactory(Class<?> testMethodClass, String testMethodName)
+      throws NoSuchMethodException {
+    headerString +=
+        Integer.toString(AbstractSerializationStream.SERIALIZATION_STREAM_VERSION)
+            + RPC_SEPARATOR_CHAR + // version
+            "0" + RPC_SEPARATOR_CHAR; // flags
+
+    stringTable = new LinkedHashMap<String, Integer>(20);
+    writeStringFromTable("moduleBaseURL");
+    writeStringFromTable("whitelistHashcode");
+
+    encodedObjectTable = new IdentityHashMap<Object, Integer>(20);
+
+    writeMethodSignature(testMethodClass, testMethodName);
+  }
+
+  /**
+   * Convert the accumulated contents of this object to the final RPC message
+   * string.
+   */
+  @Override
+  public String toString() {
+    String result = headerString;
+
+    result += Integer.toString(stringTable.size()) + RPC_SEPARATOR_CHAR;
+    for (String strEntry : stringTable.keySet()) {
+      result += strEntry + RPC_SEPARATOR_CHAR;
+    }
+
+    result += bodyString;
+
+    return result;
+  }
+
+  /**
+   * Add data for an int object.
+   */
+  public void write(int integer) throws SerializationException {
+    try {
+      bodyString += Integer.toString(integer) + RPC_SEPARATOR_CHAR;
+    } catch (Exception e) {
+      throw new SerializationException(e);
+    }
+  }
+
+  /**
+   * Add data for an int array object.
+   */
+  public void write(int[] intArray) {
+    writeStringFromTable(generateSerializedClassString(intArray.getClass()));
+    bodyString += Integer.toString(intArray.length) + RPC_SEPARATOR_CHAR;
+    for (int i : intArray) {
+      bodyString += Integer.toString(i) + RPC_SEPARATOR_CHAR;
+    }
+  }
+
+  /**
+   * Add data for an {@link Integer} object.
+   */
+  public void write(Integer integer) throws SerializationException {
+    if (getEncodedIndex(integer)) {
+      return;
+    }
+    try {
+      writeStringFromTable(generateSerializedClassString(Integer.class));
+      bodyString += integer.toString() + RPC_SEPARATOR_CHAR;
+    } catch (Exception e) {
+      throw new SerializationException(e);
+    }
+  }
+
+  /**
+   * Add data for a {@link java.util.List} object.
+   */
+  public void write(List<?> list) throws SerializationException {
+    if (getEncodedIndex(list)) {
+      return;
+    }
+    writeStringFromTable(generateSerializedClassString(list.getClass()));
+    writeListBody(list);
+  }
+
+  /**
+   * Add data for a {@link java.util.Map} object.
+   */
+  public void write(Map<?, ?> map) throws SerializationException {
+    if (getEncodedIndex(map)) {
+      return;
+    }
+    writeStringFromTable(generateSerializedClassString(map.getClass()));
+    if (map instanceof LinkedHashMap) {
+      // Set the iteration order for the linked hash map, always insertion
+      // order.
+      bodyString += "0" + RPC_SEPARATOR_CHAR;
+    }
+    if (map instanceof TreeMap) {
+      // Set the iteration order for the linked hash map, always insertion
+      // order.
+      Comparator<?> comp = ((TreeMap<?, ?>) map).comparator();
+      if (comp == null) {
+        bodyString += "0" + RPC_SEPARATOR_CHAR;
+      } else {
+        writeStringFromTable(generateSerializedClassString(comp.getClass()));
+      }
+    }
+    bodyString += Integer.toString(map.size()) + RPC_SEPARATOR_CHAR;
+    for (Map.Entry<?, ?> entry : map.entrySet()) {
+      writeCollectionElement(entry.getKey());
+      writeCollectionElement(entry.getValue());
+    }
+  }
+
+  /**
+   * Add data for an {@link Object} where no more specific write method exists.
+   */
+  public void write(Object object) throws SerializationException {
+    if (getEncodedIndex(object)) {
+      return;
+    }
+
+    try {
+      Class<?> clazz = object.getClass();
+      writeStringFromTable(generateSerializedClassString(clazz));
+
+      writeClass(object, clazz);
+    } catch (Exception e) {
+      throw new SerializationException(e);
+    }
+  }
+
+  /**
+   * Add data for a {@link Object} array.
+   */
+  public void write(Object[] objectArray) throws SerializationException {
+    if (getEncodedIndex(objectArray)) {
+      return;
+    }
+    writeStringFromTable(generateSerializedClassString(objectArray.getClass()));
+    bodyString += Integer.toString(objectArray.length) + RPC_SEPARATOR_CHAR;
+    for (Object element : objectArray) {
+      writeCollectionElement(element);
+    }
+  }
+
+  /**
+   * Add data for a {@link java.util.Set} object.
+   */
+  public void write(Set<?> set) throws SerializationException {
+    if (getEncodedIndex(set)) {
+      return;
+    }
+    writeStringFromTable(generateSerializedClassString(set.getClass()));
+    if (set instanceof TreeSet) {
+      writeStringFromTable(generateSerializedClassString(((TreeSet<?>) set).comparator().getClass()));
+    }
+    bodyString += Integer.toString(set.size()) + RPC_SEPARATOR_CHAR;
+    for (Object element : set) {
+      writeCollectionElement(element);
+    }
+  }
+
+  /**
+   * Add data for a {@link String} object.
+   */
+  public void write(String string) {
+    writeStringFromTable(string);
+  }
+
+  /**
+   * Add data for a {@link java.util.Vector} object.
+   */
+  public void write(Vector<?> vector) throws SerializationException {
+    if (getEncodedIndex(vector)) {
+      return;
+    }
+    writeStringFromTable(generateSerializedClassString(vector.getClass()));
+    bodyString += Integer.toString(vector.size()) + RPC_SEPARATOR_CHAR;
+    for (Object element : vector) {
+      writeCollectionElement(element);
+    }
+  }
+
+  /**
+   * Add data for an empty list object (as returned by
+   * {@link java.util.Collections}).
+   */
+  public void writeEmptyList() {
+    List<?> emptyList = java.util.Collections.emptyList();
+    writeStringFromTable(generateSerializedClassString(emptyList.getClass()));
+  }
+
+  /**
+   * Add data for an empty map object (as returned by
+   * {@link java.util.Collections}).
+   */
+  public void writeEmptyMap() {
+    Map<?, ?> emptyMap = java.util.Collections.emptyMap();
+    writeStringFromTable(generateSerializedClassString(emptyMap.getClass()));
+  }
+
+  /**
+   * Add data for an empty set object (as returned by
+   * {@link java.util.Collections}).
+   */
+  public void writeEmptySet() {
+    Set<?> emptySet = java.util.Collections.emptySet();
+    writeStringFromTable(generateSerializedClassString(emptySet.getClass()));
+  }
+
+  private boolean getEncodedIndex(Object obj) {
+    Integer foundIndex = encodedObjectTable.get(obj);
+    if (foundIndex == null) {
+      foundIndex = encodedObjectTable.size();
+      encodedObjectTable.put(obj, foundIndex);
+      return false;
+    }
+
+    bodyString += Integer.toString(-(foundIndex.intValue() + 1)) + RPC_SEPARATOR_CHAR;
+
+    return true;
+  }
+
+  private void writeClass(Object object, Class<?> clazz) throws SerializationException {
+    Field[] fields = clazz.getDeclaredFields();
+    try {
+      for (Field field : fields) {
+        Type type = field.getGenericType();
+        if (type == int.class) {
+          write(field.getInt(object));
+        } else {
+          Object value = field.get(object);
+          writeCollectionElement(value);
+        }
+      }
+
+      Class<?> superClass = clazz.getSuperclass();
+      if (superClass == Object.class) {
+        return;
+      } else if (superClass == LinkedList.class) {
+        writeListBody((List<?>) object);
+        return;
+      }
+
+      writeClass(object, superClass);
+
+    } catch (Exception e) {
+      throw new SerializationException(e);
+    }
+  }
+
+  /**
+   * @param element
+   * @throws SerializationException
+   */
+  @SuppressWarnings("cast")
+  private void writeCollectionElement(Object element) throws SerializationException {
+    if (element == null) {
+      bodyString += "0" + RPC_SEPARATOR_CHAR;
+    } else if (element instanceof Map) {
+      write((Map<?, ?>) element);
+    } else if (element instanceof HashSet) {
+      write((HashSet<?>) element);
+    } else if (element instanceof int[]) {
+      write((int[]) element);
+    } else if (element instanceof Integer) {
+      write((Integer) element);
+    } else if (element instanceof Long) {
+      writeLong((Long) element);
+    } else if (element instanceof Float) {
+      writeFloat((Float) element);
+    } else if (element instanceof Short) {
+      writeShort((Short) element);
+    } else if (element instanceof Double) {
+      writeDouble((Double) element);
+    } else if (element instanceof RPCTypeCheckTest.DClass) {
+      write((Object) element);
+    } else if (element instanceof String) {
+      writeStringInCollection((String) element);
+    } else if (element instanceof List) {
+      write((List<?>) element);
+    } else if (element instanceof Object[]) {
+      write((Object[]) element);
+    } else {
+      write(element);
+    }
+  }
+
+  private void writeDouble(Double d) {
+    writeStringFromTable(generateSerializedClassString(Double.class));
+    writeStringFromTable(d.toString());
+  }
+
+  private void writeFloat(Float f) {
+    writeStringFromTable(generateSerializedClassString(Float.class));
+    writeStringFromTable(f.toString());
+  }
+
+  private void writeListBody(List<?> list) throws SerializationException {
+    if (list.getClass().toString().matches(".*Arrays\\$ArrayList.*")) {
+      write(list.toArray());
+      return;
+    }
+    if (!list.getClass().toString().matches(".*Collections\\$SingletonList.*")) {
+      bodyString += Integer.toString(list.size()) + RPC_SEPARATOR_CHAR;
+    }
+    for (Object element : list) {
+      writeCollectionElement(element);
+    }
+  }
+
+  private void writeLong(Long l) {
+    writeStringFromTable(generateSerializedClassString(Long.class));
+    writeStringFromTable(l.toString());
+  }
+
+  private void writeMethodSignature(Class<?> testMethodClass, String testMethodName)
+      throws NoSuchMethodException {
+    writeStringFromTable(testMethodClass.getName());
+    writeStringFromTable(testMethodName);
+
+    Method[] testMethods = testMethodClass.getDeclaredMethods();
+    Method targetMethod = null;
+    for (Method method : testMethods) {
+      if (method.getName().equals(testMethodName)) {
+        targetMethod = method;
+        break;
+      }
+    }
+    if (targetMethod == null) {
+      throw new NoSuchMethodException("Could not find " + testMethodName + " in "
+          + testMethodClass.getName());
+    }
+
+    Class<?>[] parameterClasses = targetMethod.getParameterTypes();
+    bodyString += Integer.toString(parameterClasses.length) + RPC_SEPARATOR_CHAR;
+    for (Class<?> parameter : parameterClasses) {
+      writeStringFromTable(generateSerializedClassString(parameter));
+    }
+  }
+
+  private void writeShort(Short s) {
+    writeStringFromTable(generateSerializedClassString(Short.class));
+    writeStringFromTable(s.toString());
+  }
+
+  private void writeStringFromTable(String string) {
+    Integer index = stringTable.get(string);
+    if (index == null) {
+      index = stringTable.size() + 1;
+      stringTable.put(string, index);
+    }
+    bodyString += index.toString() + RPC_SEPARATOR_CHAR;
+  }
+
+  private void writeStringInCollection(String string) {
+    writeStringFromTable(generateSerializedClassString(string.getClass()));
+    writeStringFromTable(string);
+  }
+
+}
diff --git a/user/test/com/google/gwt/user/server/rpc/RPCTypeCheckTest.java b/user/test/com/google/gwt/user/server/rpc/RPCTypeCheckTest.java
new file mode 100644
index 0000000..f45b1f8
--- /dev/null
+++ b/user/test/com/google/gwt/user/server/rpc/RPCTypeCheckTest.java
@@ -0,0 +1,2304 @@
+/*
+ * Copyright 2011 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.user.server.rpc;
+
+import com.google.gwt.user.client.rpc.IncompatibleRemoteServiceException;
+import com.google.gwt.user.client.rpc.IsSerializable;
+import com.google.gwt.user.client.rpc.RemoteService;
+import com.google.gwt.user.client.rpc.SerializationException;
+import com.google.gwt.user.client.rpc.SerializedTypeViolationException;
+import com.google.gwt.user.client.rpc.TestSetFactory.ReverseSorter;
+import com.google.gwt.user.server.rpc.RPCTypeCheckCollectionsTest.TestHashSet;
+
+import junit.framework.TestCase;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.IdentityHashMap;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import java.util.Vector;
+
+/**
+ * Test cases for the type checking code found in server-side RPC
+ * deserialization.
+ */
+public class RPCTypeCheckTest extends TestCase {
+
+  /*
+   * Test that collection classes correctly report when they are not the
+   * expected type, when the other type is a class.
+   *   - ArrayList<Integer>: testArrayListSpoofingClass
+   *   - ArraysAsList<Integer>: testArraysAsListSpoofingClass
+   *   - EmptyList<Integer>: testEmptyListSpoofingClass
+   *   - EmptyMap<String, Integer>: testEmptyMapSpoofingClass
+   *   - EmptySet<Integer>: testEmptySetSpoofingClass
+   *   - HashMap<String, Integer>: testHashMapSpoofingClass
+   *   - HashSet<Integer>: testHashSetSpoofingClass
+   *   - IdentityHashMap<AClass, Integer>: testIdentityHashMapSpoofingClass
+   *   - LinkedHashMap<String, Integer>: testLinkedHashMapSpoofingClass
+   *   - LinkedHashSet<Integer>: testHashSetSpoofingClass
+   *   - LinkedList<Integer>: testLinkedListSpoofingClass
+   *   - SingletonList<Integer>: testSingletonListSpoofingClass
+   *   - TreeMap<String, Integer>: testTreeMapSpoofingClass
+   *   - TreeSet<Integer>: testTreeSetSpoofingClass
+   *   - Vector<Integer>: testVectorSpoofingClass
+   * 
+   * Test that primitive classes cannot be interchanged, or can safely. In
+   * general, these do not pose much danger as the serialization for basic types
+   * all perform a strictly finite amount of work and catch invalid 
+   * representations.
+   *   - Integer for String, and vice versa: testPrimitiveSpoofing
+   *   - int for String, and String for int: testValueSpoofing
+   *   
+   * Test collections in fields of classes:
+   *   - BClass contains a List<Integer>: testClassField
+   *   - CClass contains a BClass: testClassField
+   *   
+   * Test generic classes that use their generic types in various ways
+   *   - DClass<X, Y> extends LinkedList<X>: testGenericClasses
+   *   - EClass<X, Y> { HashMap<X, Y> ... }: testGenericClasses
+   *   - FClass<X, Y> { List<X> ... List<Y> ... }: testGenericClasses
+   *   - GClass<X, Y> { DClass<X, Y>, ... }: testNestedGenericClasses
+   *   - JClass<X, Y> { fields of HashMap }: testGenericFields
+   *   - DClass<X, Y> where <X, Y> come from declaring class:
+   *      testMethodClassGenerics
+   * 
+   * Wildcard types
+   *   - (IClass<? extends T> arg1) with
+   *      <T extends Set<? super GEClass<Integer, String>>>: testComplexGenerics 
+   */  
+  
+  /**
+   * Test custom class.
+   */
+  public static class AClass extends Object implements IsSerializable {
+    int a = 9876;
+
+    AClass() {
+    }
+
+    AClass(int aVal) {
+      a = aVal;
+    }
+
+    int getA() {
+      return a;
+    }
+
+    void setA(int newA) {
+      a = newA;
+    }
+  }
+
+  /**
+   * Test custom class containing a parameterized collection.
+   */
+  public static class BClass extends Object implements IsSerializable {
+    List<Integer> aList = new LinkedList<Integer>();
+
+    BClass() {
+    }
+
+    BClass(int aVal) {
+      aList.add(aVal);
+    }
+
+    @SuppressWarnings({"rawtypes", "unchecked"})
+    public void setList(List newValue) {
+      aList = newValue;
+    }
+  }
+
+  /**
+   * Test custom class containing a raw collection.
+   */
+  @SuppressWarnings({"rawtypes", "unchecked"})
+  public static class BClassRaw extends Object implements IsSerializable {
+    List aList = new LinkedList();
+
+    BClassRaw() {
+    }
+
+    BClassRaw(int aVal) {
+      aList.add(aVal);
+    }
+
+    public void setList(List newValue) {
+      aList = newValue;
+    }
+  }
+
+  /**
+   * Test custom class containing a subclass containing a parameterized
+   * collection.
+   */
+  public static class CClass extends Object implements IsSerializable {
+    BClass b = new BClass(12345);
+
+    CClass() {
+    }
+
+    public void setBClass(BClass newValue) {
+      b = newValue;
+    }
+  }
+
+  /**
+   * Test custom class containing a subclass containing a raw
+   * collection.
+   */
+  public static class CClassRaw extends Object implements IsSerializable {
+    BClassRaw b = new BClassRaw(12345);
+
+    CClassRaw() {
+    }
+
+    public void setBClass(BClassRaw newValue) {
+      b = newValue;
+    }
+  }
+
+  /**
+   * A class containing a method used to check type spoofing attacks on RPC
+   * messages containing novel classes.
+   */
+  public static class ClassesParamTestClass<X, Y> implements RemoteService {
+    @SuppressWarnings("unused")
+    public static void testAClass(AClass arg1) {
+    }
+
+    @SuppressWarnings("unused")
+    public static void testAClassArray(AClass[] arg1) {
+    }
+
+    @SuppressWarnings("unused")
+    public static void testBClass(BClass arg1) {
+    }
+
+    @SuppressWarnings("unused")
+    public static void testBClassRaw(BClassRaw arg1) {
+    }
+
+    @SuppressWarnings("unused")
+    public static void testCClass(CClass arg1) {
+    }
+
+    @SuppressWarnings("unused")
+    public static void testCClassRaw(CClassRaw arg1) {
+    }
+
+    @SuppressWarnings("unused")
+    public static <T extends Set<? super GEClass<Integer, String>>> void testComplexGenerics(
+        IClass<? extends T> arg1) {
+    }
+
+    @SuppressWarnings({"unused", "rawtypes"})
+    public static <T extends Set<? super GEClass>> void testComplexGenericsRaw(
+        IClass<? extends T> arg1) {
+    }
+
+    @SuppressWarnings("unused")
+    public static void testDClass(DClass<Integer, String> arg1) {
+    }
+
+    @SuppressWarnings("unused")
+    public static void testDClassRaw(DClassRaw<String> arg1) {
+    }
+
+    @SuppressWarnings("unused")
+    public static void testEClass(EClass<String, Integer> arg1) {
+    }
+
+    @SuppressWarnings("unused")
+    public static void testFClass(FClass<Integer, String> arg1) {
+    }
+
+    @SuppressWarnings("unused")
+    public static void testFClassRaw(FClassRaw<Integer> arg1) {
+    }
+
+    @SuppressWarnings("unused")
+    public static void testGDClass(GDClass<Integer, String> arg1) {
+    }
+
+    @SuppressWarnings("unused")
+    public static void testGDClassRaw1(GDClassRaw arg1) {
+    }
+
+    @SuppressWarnings({"unused", "rawtypes"})
+    public static void testGDClassRaw2(GDClass arg1) {
+    }
+
+    @SuppressWarnings("unused")
+    public static void testGEClass(GEClass<Integer, String> arg1) {
+    }
+
+    @SuppressWarnings("unused")
+    public static void testHDClass(HDClass<Integer, String> arg1) {
+    }
+
+    @SuppressWarnings("unused")
+    public static void testHEClass(HEClass<Integer, String> arg1) {
+    }
+
+    @SuppressWarnings("unused")
+    public static void testJClass(JClass<Integer, String> arg1) {
+    }
+
+    @SuppressWarnings("unused")
+    public static void testJClassRaw1(JClassRaw<Integer, String> arg1) {
+    }
+
+    @SuppressWarnings({"unused", "rawtypes"})
+    public static void testJClassRaw2(JClass arg1) {
+    }
+
+    @SuppressWarnings("unused")
+    public void testDClassGeneric(DClass<X, Y> arg1) {
+    }
+  }
+
+  /**
+   * Test custom parameterized class that extends a parameterized class and
+   * includes a field of a parameterized type.
+   */
+  public static class DClass<X, Y> extends LinkedList<X> implements IsSerializable {
+    Y y;
+
+    DClass() {
+      y = null;
+    }
+
+    void setY(Y value) {
+      y = value;
+    }
+  }
+
+  /**
+   * Test custom parameterized class that extends a parameterized class and
+   * includes a field of a parameterized type, with no actual parameters (raw).
+   */
+  @SuppressWarnings("rawtypes")
+  public static class DClassRaw<Y> extends LinkedList implements IsSerializable {
+    Y y;
+
+    DClassRaw() {
+      y = null;
+    }
+
+    void setY(Y value) {
+      y = value;
+    }
+  }
+
+  /**
+   * Test custom parameterized class that includes a field of a parameterized
+   * type, with two types.
+   */
+  public static class EClass<X, Y> extends Object implements IsSerializable {
+    HashMap<X, Y> map;
+
+    EClass() {
+      map = null;
+    }
+
+    void setMap(HashMap<X, Y> value) {
+      map = value;
+    }
+  }
+
+  /**
+   * Test custom parameterized class that includes two fields, each using one of
+   * the parameterized types.
+   */
+  public static class FClass<X, Y> extends Object implements IsSerializable {
+    List<X> listX;
+    List<Y> listY;
+
+    FClass() {
+      listX = null;
+      listY = null;
+    }
+
+    void setX(List<X> value) {
+      listX = value;
+    }
+
+    void setY(List<Y> value) {
+      listY = value;
+    }
+  }
+
+  /**
+   * Test custom parameterized class that includes two fields, one using the
+   * parameter type and the other raw. We wish to ensure that the raw type
+   * does not feel the need to be the parameter type.
+   */
+  @SuppressWarnings("rawtypes")
+  public static class FClassRaw<X> extends Object implements IsSerializable {
+    List<X> listX;
+    List listY;
+
+    FClassRaw() {
+      listX = null;
+      listY = null;
+    }
+
+    void setX(List<X> value) {
+      listX = value;
+    }
+
+    void setY(List value) {
+      listY = value;
+    }
+  }
+
+  /**
+   * Test custom parameterized class that extends another custom parameterized
+   * class that extends another parameterized collection.
+   */
+  public static class GDClass<A, B> extends DClass<A, B> {
+  }
+
+  /**
+   * Test custom parameterized class that extends another custom parameterized
+   * class that extends another parameterized collection, raw version.
+   */
+  @SuppressWarnings("rawtypes")
+  public static class GDClassRaw extends DClass {
+  }
+
+  /**
+   * Test custom parameterized class that extends another custom parameterized
+   * class that has a parameterized collection for a field.
+   */
+  public static class GEClass<A, B> extends EClass<B, A> {
+  }
+
+  /**
+   * Test custom parameterized class that uses another custom parameterized
+   * class that extends another parameterized collection.
+   */
+  public static class HDClass<A, B> extends Object implements IsSerializable {
+    public DClass<A, B> dClass = null;
+  }
+
+  /**
+   * Test custom parameterized class that uses another custom parameterized
+   * class that has a parameterized collection for a field.
+   */
+  public static class HEClass<A, B> extends Object implements IsSerializable {
+    public EClass<B, A> eClass = null;
+  }
+
+  /**
+   * Test class that just holds a single field of a generic type.
+   */
+  public static class IClass<T> extends Object implements IsSerializable {
+    T a;
+
+    IClass() {
+      a = null;
+    }
+
+    IClass(T aVal) {
+      a = aVal;
+    }
+
+    T getA() {
+      return a;
+    }
+
+    void setA(T newA) {
+      a = newA;
+    }
+  }
+
+  /**
+   * Test class that has fields of the same underlying type but different actual
+   * parameters for each type.
+   */
+  public static class JClass<K, V> extends EClass<K, V> {
+    public HashMap<Long, Double> field1 = null;
+    public EClass<Short, Float> field2 = null;
+    public HashMap<K, V> field3 = null;
+    public HashMap<String, Integer> field4 = null;
+  }
+
+  /**
+   * Test class that has fields of the same underlying type but different actual
+   * parameters for each type, including raw types.
+   */
+  @SuppressWarnings("rawtypes")
+  public static class JClassRaw<K, V> extends EClass {
+    public HashMap<Long, Double> field1 = null;
+    public EClass<Short, Float> field2 = null;
+    public HashMap<K, V> field3 = null;
+    public HashMap field4 = null;
+  }
+
+  /**
+   * A class containing a method used to check type spoofing attacks on RPC
+   * messages containing primitive types.
+   */
+  public static class PrimitiveParamTestClass implements RemoteService {
+    @SuppressWarnings("unused")
+    public static void testIntegerString(Integer arg1, String arg2) {
+    }
+
+    @SuppressWarnings("unused")
+    public static void testIntString(int arg1, String arg2) {
+    }
+  }
+
+  private static String generateArrayListSpoofingClass() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ClassesParamTestClass.class, "testAClass");
+
+      ArrayList<Integer> arrayList = new ArrayList<Integer>();
+      arrayList.add(12345);
+      arrayList.add(67890);
+      strFactory.write(arrayList);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  private static String generateArraysAsListSpoofingClass() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ClassesParamTestClass.class, "testAClass");
+
+      List<Integer> arrayAsList = Arrays.asList(12345, 67890);
+      strFactory.write(arrayAsList);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  @SuppressWarnings({"rawtypes"})
+  private static String generateBClassSpoofing() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ClassesParamTestClass.class, "testBClass");
+
+      BClass arg1 = new BClass();
+      LinkedList<HashSet> list = new LinkedList<HashSet>();
+      list.add(RPCTypeCheckFactory.generateTestHashSet());
+      arg1.setList(list);
+      strFactory.write(arg1);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  private static String generateBClassRaw() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ClassesParamTestClass.class, "testBClassRaw");
+
+      BClassRaw arg1 = new BClassRaw();
+      LinkedList<String> list = new LinkedList<String>();
+      list.add("foo");
+      arg1.setList(list);
+      strFactory.write(arg1);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  @SuppressWarnings({"rawtypes"})
+  private static String generateCClassSpoofing() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ClassesParamTestClass.class, "testCClass");
+
+      CClass arg1 = new CClass();
+      BClass b = new BClass();
+      LinkedList<HashSet> list = new LinkedList<HashSet>();
+      list.add(RPCTypeCheckFactory.generateTestHashSet());
+      b.setList(list);
+      arg1.setBClass(b);
+      strFactory.write(arg1);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  private static String generateCClassRaw() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ClassesParamTestClass.class, "testCClassRaw");
+
+      CClassRaw arg1 = new CClassRaw();
+      BClassRaw b = new BClassRaw();
+      LinkedList<Integer> list = new LinkedList<Integer>();
+      list.add(9876);
+      b.setList(list);
+      arg1.setBClass(b);
+      strFactory.write(arg1);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  @SuppressWarnings("unchecked")
+  private static String generateDClassRaw() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ClassesParamTestClass.class, "testDClassRaw");
+
+      DClassRaw<String> arg1 = new DClassRaw<String>();
+      arg1.setY("foo");
+      arg1.add(12345);
+      strFactory.write((Object) arg1);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  private static String generateDClassValid1() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ClassesParamTestClass.class, "testDClassGeneric");
+
+      DClass<Integer, String> arg1 = new DClass<Integer, String>();
+      arg1.setY("foo");
+      arg1.add(12345);
+      strFactory.write((Object) arg1);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  private static String generateDClassValid2() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ClassesParamTestClass.class, "testDClassGeneric");
+
+      DClass<String, Integer> arg1 = new DClass<String, Integer>();
+      arg1.setY(12345);
+      arg1.add("foo");
+      strFactory.write((Object) arg1);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  @SuppressWarnings({"rawtypes"})
+  private static String generateDClassXSpoofing() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ClassesParamTestClass.class, "testDClass");
+
+      DClass<HashSet, String> arg1 = new DClass<HashSet, String>();
+      arg1.setY("foo");
+      arg1.add(RPCTypeCheckFactory.generateTestHashSet());
+      strFactory.write((Object) arg1);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  @SuppressWarnings({"rawtypes"})
+  private static String generateDClassYSpoofing() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ClassesParamTestClass.class, "testDClass");
+
+      DClass<Integer, HashSet> arg1 = new DClass<Integer, HashSet>();
+      arg1.setY(RPCTypeCheckFactory.generateTestHashSet());
+      arg1.add(12345);
+      strFactory.write((Object) arg1);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  @SuppressWarnings({"rawtypes"})
+  private static String generateEClassXSpoofing() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ClassesParamTestClass.class, "testEClass");
+
+      EClass<HashSet, Integer> arg1 = new EClass<HashSet, Integer>();
+      arg1.setMap(RPCTypeCheckFactory.generateTestHashMap());
+      strFactory.write(arg1);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  @SuppressWarnings({"rawtypes"})
+  private static String generateEClassYSpoofing() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ClassesParamTestClass.class, "testEClass");
+
+      EClass<String, HashSet> arg1 = new EClass<String, HashSet>();
+      HashMap<String, HashSet> map = new HashMap<String, HashSet>();
+      map.put("foo", RPCTypeCheckFactory.generateTestHashSet());
+      arg1.setMap(map);
+      strFactory.write(arg1);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  private static String generateEmptyListSpoofingClass() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ClassesParamTestClass.class, "testAClass");
+
+      strFactory.writeEmptyList();
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  private static String generateEmptyMapSpoofingClass() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ClassesParamTestClass.class, "testAClass");
+
+      strFactory.writeEmptyMap();
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  private static String generateEmptySetSpoofingClass() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ClassesParamTestClass.class, "testAClass");
+
+      strFactory.writeEmptySet();
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  private static String generateFClassRaw() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ClassesParamTestClass.class, "testFClassRaw");
+
+      FClassRaw<Integer> arg1 = new FClassRaw<Integer>();
+      LinkedList<Integer> fieldX = new LinkedList<Integer>();
+      fieldX.add(12345);
+      LinkedList<String> fieldY = new LinkedList<String>();
+      fieldY.add("foo");
+      arg1.setX(fieldX);
+      arg1.setY(fieldY);
+      strFactory.write(arg1);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  @SuppressWarnings({"rawtypes"})
+  private static String generateFClassXSpoofing() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ClassesParamTestClass.class, "testFClass");
+
+      FClass<HashSet, String> arg1 = new FClass<HashSet, String>();
+      LinkedList<HashSet> fieldX = new LinkedList<HashSet>();
+      fieldX.add(RPCTypeCheckFactory.generateTestHashSet());
+      LinkedList<String> fieldY = new LinkedList<String>();
+      fieldY.add("foo");
+      arg1.setX(fieldX);
+      arg1.setY(fieldY);
+      strFactory.write(arg1);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  @SuppressWarnings({"rawtypes"})
+  private static String generateFClassYSpoofing() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ClassesParamTestClass.class, "testFClass");
+
+      FClass<Integer, HashSet> arg1 = new FClass<Integer, HashSet>();
+      LinkedList<Integer> fieldX = new LinkedList<Integer>();
+      fieldX.add(12345);
+      LinkedList<HashSet> fieldY = new LinkedList<HashSet>();
+      fieldY.add(RPCTypeCheckFactory.generateTestHashSet());
+      arg1.setX(fieldX);
+      arg1.setY(fieldY);
+      strFactory.write(arg1);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  @SuppressWarnings("unchecked")
+  private static String generateGDClassRaw1() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ClassesParamTestClass.class, "testGDClassRaw1");
+
+      GDClassRaw gClass = new GDClassRaw();
+      gClass.setY("foo");
+      gClass.add(12345);
+      strFactory.write((Object) gClass);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  @SuppressWarnings({"unchecked", "rawtypes"})
+  private static String generateGDClassRaw2() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ClassesParamTestClass.class, "testGDClassRaw2");
+
+      GDClass gClass = new GDClass();
+      gClass.setY("foo");
+      gClass.add(12345);
+      strFactory.write((Object) gClass);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  @SuppressWarnings({"rawtypes"})
+  private static String generateGDClassXSpoofing() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ClassesParamTestClass.class, "testGDClass");
+
+      GDClass<HashSet, String> gClass = new GDClass<HashSet, String>();
+      gClass.setY("foo");
+      gClass.add(RPCTypeCheckFactory.generateTestHashSet());
+      strFactory.write((Object) gClass);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  @SuppressWarnings({"rawtypes"})
+  private static String generateGDClassYSpoofing() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ClassesParamTestClass.class, "testGDClass");
+
+      GDClass<Integer, HashSet> gClass = new GDClass<Integer, HashSet>();
+      gClass.setY(RPCTypeCheckFactory.generateTestHashSet());
+      gClass.add(12345);
+      strFactory.write((Object) gClass);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  @SuppressWarnings({"cast", "rawtypes"})
+  private static String generateGEClassXSpoofing() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ClassesParamTestClass.class, "testGEClass");
+
+      GEClass<HashSet, String> arg1 = new GEClass<HashSet, String>();
+      HashMap<String, HashSet> map = new HashMap<String, HashSet>();
+      map.put("foo", RPCTypeCheckFactory.generateTestHashSet());
+      arg1.setMap(map);
+      strFactory.write((Object) arg1);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  @SuppressWarnings({"rawtypes", "cast"})
+  private static String generateGEClassYSpoofing() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ClassesParamTestClass.class, "testGEClass");
+
+      GEClass<Integer, HashSet> arg1 = new GEClass<Integer, HashSet>();
+      arg1.setMap(RPCTypeCheckFactory.generateTestHashMap());
+      strFactory.write((Object) arg1);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  private static String generateHashMapSpoofingClass() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ClassesParamTestClass.class, "testAClass");
+
+      HashMap<String, Integer> hashMap = new HashMap<String, Integer>();
+      hashMap.put("foo", 12345);
+      strFactory.write(hashMap);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  private static String generateHashSetSpoofingClass() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ClassesParamTestClass.class, "testAClass");
+
+      HashSet<Integer> hashSet = new HashSet<Integer>();
+      hashSet.add(12345);
+      hashSet.add(67890);
+      strFactory.write(hashSet);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  @SuppressWarnings({"rawtypes"})
+  private static String generateHDClassXSpoofing() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ClassesParamTestClass.class, "testHDClass");
+
+      HDClass<HashSet, String> hClass = new HDClass<HashSet, String>();
+      hClass.dClass = new DClass<HashSet, String>();
+      hClass.dClass.setY("foo");
+      hClass.dClass.add(RPCTypeCheckFactory.generateTestHashSet());
+      strFactory.write(hClass);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  @SuppressWarnings({"rawtypes"})
+  private static String generateHDClassYSpoofing() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ClassesParamTestClass.class, "testHDClass");
+
+      HDClass<Integer, HashSet> hClass = new HDClass<Integer, HashSet>();
+      hClass.dClass = new DClass<Integer, HashSet>();
+      hClass.dClass.setY(RPCTypeCheckFactory.generateTestHashSet());
+      hClass.dClass.add(12345);
+      strFactory.write(hClass);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  @SuppressWarnings({"rawtypes"})
+  private static String generateHEClassXSpoofing() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ClassesParamTestClass.class, "testHEClass");
+
+      HEClass<HashSet, String> arg1 = new HEClass<HashSet, String>();
+      arg1.eClass = new EClass<String, HashSet>();
+      HashMap<String, HashSet> map = new HashMap<String, HashSet>();
+      map.put("foo", RPCTypeCheckFactory.generateTestHashSet());
+      arg1.eClass.setMap(map);
+      strFactory.write(arg1);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  @SuppressWarnings({"rawtypes"})
+  private static String generateHEClassYSpoofing() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ClassesParamTestClass.class, "testHEClass");
+
+      HEClass<Integer, HashSet> arg1 = new HEClass<Integer, HashSet>();
+      arg1.eClass = new EClass<HashSet, Integer>();
+      arg1.eClass.setMap(RPCTypeCheckFactory.generateTestHashMap());
+      strFactory.write(arg1);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  private static String generateIClassRaw() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ClassesParamTestClass.class, "testComplexGenericsRaw");
+
+      /*
+       * Generate an object that is valid for IClass<? extends T> where <T extends
+       * Set<? super GEClass>>.
+       * 
+       * EClass is a superclass of GEClass, so we can use that in the thing that
+       * extends Set.
+       */
+      HashMap<Integer, String> eClassField = new HashMap<Integer, String>();
+      eClassField.put(12345, "foo");
+      EClass<Integer, String> geClass = new EClass<Integer, String>();
+      geClass.setMap(eClassField);
+      TestHashSet<EClass<Integer, String>> set = new TestHashSet<EClass<Integer, String>>();
+      set.add(geClass);
+      IClass<TestHashSet<EClass<Integer, String>>> arg1 =
+          new IClass<TestHashSet<EClass<Integer, String>>>(set);
+
+      strFactory.write(arg1);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  private static String generateIClassSpoofingA() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ClassesParamTestClass.class, "testComplexGenerics");
+
+      /*
+       * Generate an object that spoofs IClass<? extends T> where <T extends
+       * Set<? super GEClass<Integer, String>>> at the highest level (i.e.
+       * something that does not extend Set).
+       * 
+       * EClass is a superclass of GEClass, so we can use that in the thing that
+       * extends Set. But it switched the parameter order, so we need
+       * EClass<String, Integer>.
+       * 
+       * LinkedList does not extend Set.
+       * 
+       * So we need to construct this: IClass<LinkedList<EClass<String,
+       * Integer>>> arg1
+       */
+      HashMap<String, Integer> eClassField = new HashMap<String, Integer>();
+      eClassField.put("foo", 12345);
+      eClassField.put("bar", 67890);
+      EClass<String, Integer> geClass = new EClass<String, Integer>();
+      geClass.setMap(eClassField);
+      LinkedList<EClass<String, Integer>> list = new LinkedList<EClass<String, Integer>>();
+      list.add(geClass);
+      IClass<LinkedList<EClass<String, Integer>>> arg1 =
+          new IClass<LinkedList<EClass<String, Integer>>>(list);
+
+      strFactory.write(arg1);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  @SuppressWarnings({"rawtypes"})
+  private static String generateIClassSpoofingB() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ClassesParamTestClass.class, "testComplexGenerics");
+
+      /*
+       * Generate an object that spoofs IClass<? extends T> where <T extends
+       * Set<? super GEClass<Integer, String>>> at the second highest level
+       * (i.e. something that is not a superclass of GEClass).
+       * 
+       * Pathological hash set is not a superclass of GEClass, so we can use
+       * that.
+       * 
+       * So we need to construct this: IClass<TestHashSet<HashSet<Integer,
+       * String>>> arg1
+       */
+      HashSet hashSet = RPCTypeCheckFactory.generateTestHashSetHashSet();
+      IClass<HashSet> arg1 = new IClass<HashSet>(hashSet);
+
+      strFactory.write(arg1);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  @SuppressWarnings({"rawtypes"})
+  private static String generateIClassSpoofingC() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ClassesParamTestClass.class, "testComplexGenerics");
+
+      /*
+       * Generate an object that spoofs IClass<? extends T> where <T extends
+       * Set<? super GEClass<Integer, String>>> at the deepest level (i.e.
+       * GEClass parameter types).
+       * 
+       * EClass is a superclass of GEClass, so we can use that in the thing that
+       * extends Set. But it switched the parameter order, so we need
+       * EClass<String, HashSet>.
+       */
+      HashMap<String, HashSet> eClassField = new HashMap<String, HashSet>();
+      eClassField.put("foo", RPCTypeCheckFactory.generateTestHashSet());
+      EClass<String, HashSet> geClass = new EClass<String, HashSet>();
+      geClass.setMap(eClassField);
+      TestHashSet<EClass<String, HashSet>> set = new TestHashSet<EClass<String, HashSet>>();
+      set.add(geClass);
+      IClass<TestHashSet<EClass<String, HashSet>>> arg1 =
+          new IClass<TestHashSet<EClass<String, HashSet>>>(set);
+
+      strFactory.write(arg1);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  private static String generateIClassValid() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ClassesParamTestClass.class, "testComplexGenerics");
+
+      /*
+       * Generate an object that works for IClass<? extends T> where <T extends
+       * Set<? super GEClass<Integer, String>>>.
+       * 
+       * EClass is a superclass of GEClass, so we can use that in the thing that
+       * extends Set. But it switched the parameter order, so we need
+       * EClass<String, Integer>.
+       * 
+       * HashSet extends Set.
+       * 
+       * RPCTypeCheckCollectionsTest.TestHashSet extends HashSet.
+       * 
+       * So we need to construct this: IClass<TestHashSet<EClass<String,
+       * Integer>>> arg1
+       */
+      HashMap<String, Integer> eClassField = new HashMap<String, Integer>();
+      eClassField.put("foo", 12345);
+      eClassField.put("bar", 67890);
+      EClass<String, Integer> geClass = new EClass<String, Integer>();
+      geClass.setMap(eClassField);
+      TestHashSet<EClass<String, Integer>> set = new TestHashSet<EClass<String, Integer>>();
+      set.add(geClass);
+      IClass<TestHashSet<EClass<String, Integer>>> arg1 =
+          new IClass<TestHashSet<EClass<String, Integer>>>(set);
+
+      strFactory.write(arg1);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  private static String generateIdentityHashMapSpoofingClass() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ClassesParamTestClass.class, "testAClass");
+
+      IdentityHashMap<String, Integer> hashMap = new IdentityHashMap<String, Integer>();
+      hashMap.put("foo", 12345);
+      strFactory.write(hashMap);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  private static String generateIntegerSpoofingString() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(PrimitiveParamTestClass.class, "testIntegerString");
+
+      Integer i = 12345;
+      strFactory.write(i);
+
+      Integer j = 67890;
+      strFactory.write(j);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  private static String generateIntSpoofingString() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(PrimitiveParamTestClass.class, "testIntString");
+
+      int i = 12345;
+      strFactory.write(i);
+
+      int j = 67890;
+      strFactory.write(j);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  @SuppressWarnings("unchecked")
+  private static String generateJClassRaw1() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ClassesParamTestClass.class, "testJClassRaw1");
+
+      JClassRaw<Integer, String> arg1 = new JClassRaw<Integer, String>();
+      arg1.field1 = new HashMap<Long, Double>();
+      arg1.field1.put(123L, 0.12345);
+      arg1.field2 = new EClass<Short, Float>();
+      HashMap<Short, Float> eClassMap = new HashMap<Short, Float>();
+      eClassMap.put((short) 567, 0.456f);
+      arg1.field2.setMap(eClassMap);
+      arg1.field3 = new HashMap<Integer, String>();
+      arg1.field3.put(9876, "foo");
+      arg1.field4 = new HashMap<String, Integer>();
+      arg1.field4.put("bar", 765);
+      HashMap<Integer, String> jClassMap = new HashMap<Integer, String>();
+      jClassMap.put(4321, "baz");
+      arg1.setMap(jClassMap);
+      strFactory.write(arg1);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  @SuppressWarnings({"unchecked", "rawtypes"})
+  private static String generateJClassRaw2() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ClassesParamTestClass.class, "testJClassRaw2");
+
+      JClass arg1 = new JClass();
+      arg1.field1 = new HashMap<Long, Double>();
+      arg1.field1.put(123L, 0.12345);
+      arg1.field2 = new EClass<Short, Float>();
+      HashMap<Short, Float> eClassMap = new HashMap<Short, Float>();
+      eClassMap.put((short) 567, 0.456f);
+      arg1.field2.setMap(eClassMap);
+      arg1.field3 = new HashMap<Integer, String>();
+      arg1.field3.put(9876, "foo");
+      arg1.field4 = new HashMap<String, Integer>();
+      arg1.field4.put("bar", 765);
+      HashMap<Integer, String> jClassMap = new HashMap<Integer, String>();
+      jClassMap.put(4321, "baz");
+      arg1.setMap(jClassMap);
+      strFactory.write(arg1);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  @SuppressWarnings({"unchecked", "rawtypes"})
+  private static String generateJClassSpoofing1() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ClassesParamTestClass.class, "testJClass");
+
+      JClass<Integer, String> arg1 = new JClass<Integer, String>();
+      arg1.field1 = (HashMap) RPCTypeCheckFactory.generateTestHashMap();
+      arg1.field2 = new EClass<Short, Float>();
+      HashMap<Short, Float> eClassMap = new HashMap<Short, Float>();
+      eClassMap.put((short) 567, 0.456f);
+      arg1.field2.setMap(eClassMap);
+      arg1.field3 = new HashMap<Integer, String>();
+      arg1.field3.put(9876, "foo");
+      arg1.field4 = new HashMap<String, Integer>();
+      arg1.field4.put("bar", 765);
+      HashMap<Integer, String> jClassMap = new HashMap<Integer, String>();
+      jClassMap.put(4321, "baz");
+      arg1.setMap(jClassMap);
+      strFactory.write(arg1);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  @SuppressWarnings({"unchecked", "rawtypes"})
+  private static String generateJClassSpoofing2() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ClassesParamTestClass.class, "testJClass");
+
+      JClass<Integer, String> arg1 = new JClass<Integer, String>();
+      arg1.field1 = new HashMap<Long, Double>();
+      arg1.field1.put(123L, 0.12345);
+      arg1.field2 = new EClass<Short, Float>();
+      arg1.field2.setMap((HashMap) RPCTypeCheckFactory.generateTestHashMap());
+      arg1.field3 = new HashMap<Integer, String>();
+      arg1.field3.put(9876, "foo");
+      arg1.field4 = new HashMap<String, Integer>();
+      arg1.field4.put("bar", 765);
+      HashMap<Integer, String> jClassMap = new HashMap<Integer, String>();
+      jClassMap.put(4321, "baz");
+      arg1.setMap(jClassMap);
+      strFactory.write(arg1);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  @SuppressWarnings({"unchecked", "rawtypes"})
+  private static String generateJClassSpoofing3() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ClassesParamTestClass.class, "testJClass");
+
+      JClass<Integer, String> arg1 = new JClass<Integer, String>();
+      arg1.field1 = new HashMap<Long, Double>();
+      arg1.field1.put(123L, 0.12345);
+      arg1.field2 = new EClass<Short, Float>();
+      HashMap<Short, Float> eClassMap = new HashMap<Short, Float>();
+      eClassMap.put((short) 567, 0.456f);
+      arg1.field2.setMap(eClassMap);
+      arg1.field3 = (HashMap) RPCTypeCheckFactory.generateTestHashMap();
+      arg1.field4 = new HashMap<String, Integer>();
+      arg1.field4.put("bar", 765);
+      HashMap<Integer, String> jClassMap = new HashMap<Integer, String>();
+      jClassMap.put(4321, "baz");
+      arg1.setMap(jClassMap);
+      strFactory.write(arg1);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  @SuppressWarnings({"unchecked", "rawtypes"})
+  private static String generateJClassSpoofing4() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ClassesParamTestClass.class, "testJClass");
+
+      JClass<Integer, String> arg1 = new JClass<Integer, String>();
+      arg1.field1 = new HashMap<Long, Double>();
+      arg1.field1.put(123L, 0.12345);
+      arg1.field2 = new EClass<Short, Float>();
+      HashMap<Short, Float> eClassMap = new HashMap<Short, Float>();
+      eClassMap.put((short) 567, 0.456f);
+      arg1.field2.setMap(eClassMap);
+      arg1.field3 = new HashMap<Integer, String>();
+      arg1.field3.put(9876, "foo");
+      arg1.field4 = (HashMap) RPCTypeCheckFactory.generateTestHashMap();
+      HashMap<Integer, String> jClassMap = new HashMap<Integer, String>();
+      jClassMap.put(4321, "baz");
+      arg1.setMap(jClassMap);
+      strFactory.write(arg1);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  @SuppressWarnings({"unchecked", "rawtypes"})
+  private static String generateJClassSpoofing5() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ClassesParamTestClass.class, "testJClass");
+
+      JClass<Integer, String> arg1 = new JClass<Integer, String>();
+      arg1.field1 = new HashMap<Long, Double>();
+      arg1.field1.put(123L, 0.12345);
+      arg1.field2 = new EClass<Short, Float>();
+      HashMap<Short, Float> eClassMap = new HashMap<Short, Float>();
+      eClassMap.put((short) 567, 0.456f);
+      arg1.field2.setMap(eClassMap);
+      arg1.field3 = new HashMap<Integer, String>();
+      arg1.field3.put(9876, "foo");
+      arg1.field4 = new HashMap<String, Integer>();
+      arg1.field4.put("bar", 765);
+      arg1.setMap((HashMap) RPCTypeCheckFactory.generateTestHashMap());
+      strFactory.write(arg1);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  private static String generateJClassValid() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ClassesParamTestClass.class, "testJClass");
+
+      JClass<Integer, String> arg1 = new JClass<Integer, String>();
+      arg1.field1 = new HashMap<Long, Double>();
+      arg1.field1.put(123L, 0.12345);
+      arg1.field2 = new EClass<Short, Float>();
+      HashMap<Short, Float> eClassMap = new HashMap<Short, Float>();
+      eClassMap.put((short) 567, 0.456f);
+      arg1.field2.setMap(eClassMap);
+      arg1.field3 = new HashMap<Integer, String>();
+      arg1.field3.put(9876, "foo");
+      arg1.field4 = new HashMap<String, Integer>();
+      arg1.field4.put("bar", 765);
+      HashMap<Integer, String> jClassMap = new HashMap<Integer, String>();
+      jClassMap.put(4321, "baz");
+      arg1.setMap(jClassMap);
+      strFactory.write(arg1);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  private static String generateLinkedHashMapSpoofingClass() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ClassesParamTestClass.class, "testAClass");
+
+      LinkedHashMap<String, Integer> hashMap = new LinkedHashMap<String, Integer>();
+      hashMap.put("foo", 12345);
+      strFactory.write(hashMap);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  private static String generateLinkedHashSetSpoofingClass() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ClassesParamTestClass.class, "testAClass");
+
+      LinkedHashSet<Integer> hashSet = new LinkedHashSet<Integer>();
+      hashSet.add(12345);
+      hashSet.add(67890);
+      strFactory.write(hashSet);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  private static String generateLinkedListSpoofingClass() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ClassesParamTestClass.class, "testAClass");
+
+      LinkedList<Integer> list = new LinkedList<Integer>();
+      list.add(12345);
+      list.add(67890);
+      strFactory.write(list);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  private static String generateSingletonListSpoofingClass() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ClassesParamTestClass.class, "testAClass");
+
+      Integer i = new Integer(67890);
+      List<Integer> singletonList = Collections.singletonList(i);
+      strFactory.write(singletonList);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  private static String generateStringSpoofingInt() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(PrimitiveParamTestClass.class, "testIntString");
+
+      String arg1 = "foo";
+      strFactory.write(arg1);
+
+      String arg2 = "bar";
+      strFactory.write(arg2);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  private static String generateStringSpoofingInteger() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(PrimitiveParamTestClass.class, "testIntegerString");
+
+      String arg1 = "foo";
+      strFactory.write(arg1);
+
+      String arg2 = "bar";
+      strFactory.write(arg2);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  private static String generateTreeMapSpoofingClass() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ClassesParamTestClass.class, "testAClass");
+
+      TreeMap<String, Integer> treeMap = new TreeMap<String, Integer>();
+      treeMap.put("foo", 12345);
+      strFactory.write(treeMap);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  private static String generateTreeSetSpoofingClass() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ClassesParamTestClass.class, "testAClass");
+
+      ReverseSorter<Integer> sorter = new ReverseSorter<Integer>();
+      TreeSet<Integer> treeSet = new TreeSet<Integer>(sorter);
+      treeSet.add(12345);
+      treeSet.add(67890);
+      strFactory.write(treeSet);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  private static String generateVectorSpoofingClass() {
+    try {
+      RPCTypeCheckFactory strFactory =
+          new RPCTypeCheckFactory(ClassesParamTestClass.class, "testAClass");
+
+      Vector<Integer> vector = new Vector<Integer>();
+      vector.add(12345);
+      vector.add(67890);
+      strFactory.write(vector);
+
+      return strFactory.toString();
+
+    } catch (Exception e) {
+      fail(e.getMessage());
+
+      return null;
+    }
+  }
+
+  /**
+   * This checks that ArrayList correctly reports that it is an incorrect type.
+   */
+  public void testArrayListSpoofingClass() {
+    try {
+      RPC.decodeRequest(generateArrayListSpoofingClass());
+      fail("Expected IncompatibleRemoteServiceException from testArrayListSpoofingClass");
+    } catch (IncompatibleRemoteServiceException e) {
+      // Expected to get here
+      assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+      assertTrue(e.getCause().getMessage().matches(".*ArrayList.*AClass.*"));
+    }
+  }
+
+  /**
+   * This checks that a List generated by Arrays.asList correctly reports that
+   * it is an incorrect type.
+   */
+  public void testArraysAsListSpoofingClass() {
+    try {
+      RPC.decodeRequest(generateArraysAsListSpoofingClass());
+      fail("Expected IncompatibleRemoteServiceException from testArraysAsListSpoofingClass");
+    } catch (IncompatibleRemoteServiceException e) {
+      // Expected to get here
+      assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+      assertTrue(e.getCause().getMessage().matches(".*Arrays\\$ArrayList.*AClass.*"));
+    }
+  }
+
+  /**
+   * This checks that generic fields of classes work correctly.
+   */
+  public void testClassFields() {
+    try {
+      RPC.decodeRequest(generateBClassSpoofing());
+      fail("Expected IncompatibleRemoteServiceException from testClassFields (1)");
+    } catch (IncompatibleRemoteServiceException e) {
+      // Expected to get here
+      assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+      assertTrue(e.getCause().getMessage().matches(".*HashSet.*Integer.*"));
+    }
+    try {
+      RPC.decodeRequest(generateCClassSpoofing());
+      fail("Expected IncompatibleRemoteServiceException from testClassFields (2)");
+    } catch (IncompatibleRemoteServiceException e) {
+      // Expected to get here
+      assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+      assertTrue(e.getCause().getMessage().matches(".*HashSet.*Integer.*"));
+    }
+  }
+
+  /**
+   * This checks that raw collections fields of classes work correctly.
+   */
+  public void testClassFieldsRaw() {
+    try {
+      RPC.decodeRequest(generateBClassRaw());
+    } catch (Exception e) {
+      fail("Unexpected Exception from testClassFieldsRaw (1): " + e.getMessage());
+    }
+    try {
+      RPC.decodeRequest(generateCClassRaw());
+    } catch (Exception e) {
+      fail("Unexpected Exception from testClassFieldsRaw (2): " + e.getMessage());
+    }
+  }
+
+  /**
+   * This checks that complex generic declarations are both correctly serialized
+   * (no false negatives) and correctly verified.
+   */
+  public void testComplexGenerics() {
+    try {
+      RPC.decodeRequest(generateIClassValid());
+    } catch (Exception e) {
+      fail("Unexpected assertion from testComplexGenerics (1a): " + e.getMessage());
+    }
+    try {
+      RPC.decodeRequest(generateIClassRaw());
+    } catch (Exception e) {
+      fail("Unexpected assertion from testComplexGenerics (1b): " + e.getMessage());
+    }
+    try {
+      RPC.decodeRequest(generateIClassSpoofingA());
+      fail("Expected IncompatibleRemoteServiceException from testComplexGenerics (2)");
+    } catch (IncompatibleRemoteServiceException e) {
+      // Expected to get here
+      assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+      assertTrue(e.getCause().getMessage().matches(".*LinkedList.*? extends T.*"));
+    }
+    try {
+      RPC.decodeRequest(generateIClassSpoofingB());
+      fail("Expected IncompatibleRemoteServiceException from testComplexGenerics (3)");
+    } catch (IncompatibleRemoteServiceException e) {
+      // Expected to get here
+      assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+      assertTrue(e.getCause().getMessage().matches(".*HashSet.*? super .*GEClass.*"));
+    }
+    try {
+      RPC.decodeRequest(generateIClassSpoofingC());
+      fail("Expected IncompatibleRemoteServiceException from testComplexGenerics (4)");
+    } catch (IncompatibleRemoteServiceException e) {
+      // Expected to get here
+      assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+      assertTrue(e.getCause().getMessage().matches(".*HashSet.*Integer.*"));
+    }
+  }
+
+  /**
+   * This checks that a List generated by Collections.emptyList correctly
+   * reports that it is an incorrect type.
+   */
+  public void testEmptyListSpoofingClass() {
+    try {
+      RPC.decodeRequest(generateEmptyListSpoofingClass());
+      fail("Expected IncompatibleRemoteServiceException from testEmptyListSpoofingClass");
+    } catch (IncompatibleRemoteServiceException e) {
+      // Expected to get here
+      assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+      assertTrue(e.getCause().getMessage().matches(".*List.*AClass.*"));
+    }
+  }
+
+  /**
+   * This checks that a Map generated by Collections.emptyMap correctly reports
+   * that it is an incorrect type.
+   */
+  public void testEmptyMapSpoofingClass() {
+    try {
+      RPC.decodeRequest(generateEmptyMapSpoofingClass());
+      fail("Expected IncompatibleRemoteServiceException from testEmptyMapSpoofingClass");
+    } catch (IncompatibleRemoteServiceException e) {
+      // Expected to get here
+      assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+      assertTrue(e.getCause().getMessage().matches(".*Map.*AClass.*"));
+    }
+  }
+
+  /**
+   * This checks that a Set generated by Collections.emptySet correctly reports
+   * that it is an incorrect type.
+   */
+  public void testEmptySetSpoofingClass() {
+    try {
+      RPC.decodeRequest(generateEmptySetSpoofingClass());
+      fail("Expected IncompatibleRemoteServiceException from testEmptySetSpoofingClass");
+    } catch (IncompatibleRemoteServiceException e) {
+      // Expected to get here
+      assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+      assertTrue(e.getCause().getMessage().matches(".*Set.*AClass.*"));
+    }
+  }
+
+  /**
+   * This checks that fields of generic classes work correctly.
+   */
+  public void testGenericClasses() {
+    try {
+      RPC.decodeRequest(generateDClassRaw());
+    } catch (Exception e) {
+      fail("Unexpected assertion from testGenericClasses (1-): " + e.getMessage());
+    }
+    try {
+      RPC.decodeRequest(generateDClassXSpoofing());
+      fail("Expected IncompatibleRemoteServiceException from testGenericClasses (1)");
+    } catch (IncompatibleRemoteServiceException e) {
+      // Expected to get here
+      assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+      assertTrue(e.getCause().getMessage().matches(".*HashSet.*Integer.*"));
+    }
+    try {
+      RPC.decodeRequest(generateDClassYSpoofing());
+      fail("Expected IncompatibleRemoteServiceException from testGenericClasses (2)");
+    } catch (IncompatibleRemoteServiceException e) {
+      // Expected to get here
+      assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+      assertTrue(e.getCause().getMessage().matches(".*HashSet.*String.*"));
+    }
+    try {
+      RPC.decodeRequest(generateEClassXSpoofing());
+      fail("Expected IncompatibleRemoteServiceException from testGenericClasses (3)");
+    } catch (IncompatibleRemoteServiceException e) {
+      // Expected to get here
+      assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+      assertTrue(e.getCause().getMessage().matches(".*HashSet.*String.*"));
+    }
+    try {
+      RPC.decodeRequest(generateEClassYSpoofing());
+      fail("Expected IncompatibleRemoteServiceException from testGenericClasses (4)");
+    } catch (IncompatibleRemoteServiceException e) {
+      // Expected to get here
+      assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+      assertTrue(e.getCause().getMessage().matches(".*HashSet.*Integer.*"));
+    }
+    try {
+      RPC.decodeRequest(generateFClassRaw());
+    } catch (Exception e) {
+      fail("Unexpected assertion from testGenericClasses (5-): " + e.getMessage());
+    }
+    try {
+      RPC.decodeRequest(generateFClassXSpoofing());
+      fail("Expected IncompatibleRemoteServiceException from testGenericClasses (5)");
+    } catch (IncompatibleRemoteServiceException e) {
+      // Expected to get here
+      assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+      assertTrue(e.getCause().getMessage().matches(".*HashSet.*Integer.*"));
+    }
+    try {
+      RPC.decodeRequest(generateFClassYSpoofing());
+      fail("Expected IncompatibleRemoteServiceException from testGenericClasses (6)");
+    } catch (IncompatibleRemoteServiceException e) {
+      // Expected to get here
+      assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+      assertTrue(e.getCause().getMessage().matches(".*HashSet.*String.*"));
+    }
+  }
+
+  /**
+   * This tests that generic class with fields that have the same types but
+   * different actual parameters are correctly handled.
+   */
+  public void testGenericFields() {
+    try {
+      RPC.decodeRequest(generateJClassValid());
+    } catch (Exception e) {
+      fail("Unexpected assertion from testGenericFields (1a): " + e.getMessage());
+    }
+    try {
+      RPC.decodeRequest(generateJClassRaw1());
+    } catch (Exception e) {
+      fail("Unexpected assertion from testGenericFields (1b): " + e.getMessage());
+    }
+    try {
+      RPC.decodeRequest(generateJClassRaw2());
+    } catch (Exception e) {
+      fail("Unexpected assertion from testGenericFields (1c): " + e.getMessage());
+    }
+    try {
+      RPC.decodeRequest(generateJClassSpoofing1());
+      fail("Expected IncompatibleRemoteServiceException from testGenericFields (2)");
+    } catch (IncompatibleRemoteServiceException e) {
+      // Expected to get here
+      assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+      assertTrue(e.getCause().getMessage().matches(".*HashSet.*Long.*"));
+    }
+    try {
+      RPC.decodeRequest(generateJClassSpoofing2());
+      fail("Expected IncompatibleRemoteServiceException from testGenericFields (3)");
+    } catch (IncompatibleRemoteServiceException e) {
+      // Expected to get here
+      assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+      assertTrue(e.getCause().getMessage().matches(".*HashSet.*Short.*"));
+    }
+    try {
+      RPC.decodeRequest(generateJClassSpoofing3());
+      fail("Expected IncompatibleRemoteServiceException from testGenericFields (4)");
+    } catch (IncompatibleRemoteServiceException e) {
+      // Expected to get here
+      assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+      assertTrue(e.getCause().getMessage().matches(".*HashSet.*Integer.*"));
+    }
+    try {
+      RPC.decodeRequest(generateJClassSpoofing4());
+      fail("Expected IncompatibleRemoteServiceException from testGenericFields (5)");
+    } catch (IncompatibleRemoteServiceException e) {
+      // Expected to get here
+      assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+      assertTrue(e.getCause().getMessage().matches(".*HashSet.*String.*"));
+    }
+    try {
+      RPC.decodeRequest(generateJClassSpoofing5());
+      fail("Expected IncompatibleRemoteServiceException from testGenericFields (6)");
+    } catch (IncompatibleRemoteServiceException e) {
+      // Expected to get here
+      assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+      assertTrue(e.getCause().getMessage().matches(".*HashSet.*Integer.*"));
+    }
+  }
+
+  /**
+   * This checks that HashMap correctly reports that it is an incorrect type.
+   */
+  public void testHashMapSpoofingClass() {
+    try {
+      RPC.decodeRequest(generateHashMapSpoofingClass());
+      fail("Expected IncompatibleRemoteServiceException from testHashMapSpoofingClass");
+    } catch (IncompatibleRemoteServiceException e) {
+      // Expected to get here
+      assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+      assertTrue(e.getCause().getMessage().matches(".*HashMap.*AClass.*"));
+    }
+  }
+
+  /**
+   * This checks that HashSet correctly reports that it is an incorrect type.
+   */
+  public void testHashSetSpoofingClass() {
+    try {
+      RPC.decodeRequest(generateHashSetSpoofingClass());
+      fail("Expected IncompatibleRemoteServiceException from testHashSetSpoofingClass (1)");
+    } catch (IncompatibleRemoteServiceException e) {
+      // Expected to get here
+      assertEquals(e.getCause().getClass(), SerializedTypeViolationException.class);
+      assertTrue(e.getCause().getMessage().matches(".*HashSet.*AClass.*"));
+    }
+    try {
+      RPC.decodeRequest(generateLinkedHashSetSpoofingClass());
+      fail("Expected IncompatibleRemoteServiceException from testHashSetSpoofingClass (2)");
+    } catch (IncompatibleRemoteServiceException e) {
+      // Expected to get here
+      assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+      assertTrue(e.getCause().getMessage().matches(".*LinkedHashSet.*AClass.*"));
+    }
+  }
+
+  /**
+   * This checks that IdentityHashMap correctly reports that it is an incorrect
+   * type.
+   */
+  public void testIdentityHashMapSpoofingClass() {
+    try {
+      RPC.decodeRequest(generateIdentityHashMapSpoofingClass());
+      fail("Expected IncompatibleRemoteServiceException from testIdentityHashMapSpoofingClass");
+    } catch (IncompatibleRemoteServiceException e) {
+      // Expected to get here
+      assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+      assertTrue(e.getCause().getMessage().matches(".*IdentityHashMap.*AClass.*"));
+    }
+  }
+
+  /**
+   * This checks that LinkedHashMap correctly reports that it is an incorrect
+   * type.
+   */
+  public void testLinkedHashMapSpoofingClass() {
+    try {
+      RPC.decodeRequest(generateLinkedHashMapSpoofingClass());
+      fail("Expected IncompatibleRemoteServiceException from testLinkedHashMapSpoofingClass");
+    } catch (IncompatibleRemoteServiceException e) {
+      // Expected to get here
+      assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+      assertTrue(e.getCause().getMessage().matches(".*LinkedHashMap.*AClass.*"));
+    }
+  }
+
+  /**
+   * This checks that LinkedList correctly reports that it is an incorrect type.
+   */
+  public void testLinkedListSpoofingClass() {
+    try {
+      RPC.decodeRequest(generateLinkedListSpoofingClass());
+      fail("Expected IncompatibleRemoteServiceException from testLinkedListSpoofingClass");
+    } catch (IncompatibleRemoteServiceException e) {
+      // Expected to get here
+      assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+      assertTrue(e.getCause().getMessage().matches(".*LinkedList.*AClass.*"));
+    }
+  }
+
+  /**
+   * This checks that we do not report an error when we cannot know the actual
+   * value of a generic because it comes from the method class (which has no
+   * known parameters when the method is invoked on the server).
+   */
+  public void testMethodClassGenerics() {
+    try {
+      RPC.decodeRequest(generateDClassValid1());
+    } catch (Exception e) {
+      fail("Unexpected Exception from testMethodClassGenerics (1): " + e.getMessage());
+    }
+    try {
+      RPC.decodeRequest(generateDClassValid2());
+    } catch (Exception e) {
+      fail("Unexpected Exception from testMethodClassGenerics (2): " + e.getMessage());
+    }
+  }
+
+  /**
+   * This checks that fields of generic classes work correctly.
+   */
+  public void testNestedGenericClasses() {
+    try {
+      RPC.decodeRequest(generateGDClassRaw1());
+    } catch (Exception e) {
+      fail("Unexpected Exception from testNestedGenericClasses (0a): " + e.getMessage());
+    }
+    try {
+      RPC.decodeRequest(generateGDClassRaw2());
+    } catch (Exception e) {
+      fail("Unexpected Exception from testNestedGenericClasses (0b): " + e.getMessage());
+    }
+    try {
+      RPC.decodeRequest(generateGDClassXSpoofing());
+      fail("Expected IncompatibleRemoteServiceException from testNestedGenericClasses (1)");
+    } catch (IncompatibleRemoteServiceException e) {
+      // Expected to get here
+      assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+      assertTrue(e.getCause().getMessage().matches(".*HashSet.*Integer.*"));
+    }
+    try {
+      RPC.decodeRequest(generateGDClassYSpoofing());
+      fail("Expected IncompatibleRemoteServiceException from testNestedGenericClasses (2)");
+    } catch (IncompatibleRemoteServiceException e) {
+      // Expected to get here
+      assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+      assertTrue(e.getCause().getMessage().matches(".*HashSet.*String.*"));
+    }
+    try {
+      RPC.decodeRequest(generateGEClassXSpoofing());
+      fail("Expected IncompatibleRemoteServiceException from testNestedGenericClasses (3)");
+    } catch (IncompatibleRemoteServiceException e) {
+      // Expected to get here
+      assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+      assertTrue(e.getCause().getMessage().matches(".*HashSet.*Integer.*"));
+    }
+    try {
+      RPC.decodeRequest(generateGEClassYSpoofing());
+      fail("Expected IncompatibleRemoteServiceException from testNestedGenericClasses (4)");
+    } catch (IncompatibleRemoteServiceException e) {
+      // Expected to get here
+      assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+      assertTrue(e.getCause().getMessage().matches(".*HashSet.*String.*"));
+    }
+    try {
+      RPC.decodeRequest(generateHDClassXSpoofing());
+      fail("Expected IncompatibleRemoteServiceException from testNestedGenericClasses (5)");
+    } catch (IncompatibleRemoteServiceException e) {
+      // Expected to get here
+      assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+      assertTrue(e.getCause().getMessage().matches(".*HashSet.*Integer.*"));
+    }
+    try {
+      RPC.decodeRequest(generateHDClassYSpoofing());
+      fail("Expected IncompatibleRemoteServiceException from testNestedGenericClasses (6)");
+    } catch (IncompatibleRemoteServiceException e) {
+      // Expected to get here
+      assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+      assertTrue(e.getCause().getMessage().matches(".*HashSet.*String.*"));
+    }
+    try {
+      RPC.decodeRequest(generateHEClassXSpoofing());
+      fail("Expected IncompatibleRemoteServiceException from testNestedGenericClasses (7)");
+    } catch (IncompatibleRemoteServiceException e) {
+      // Expected to get here
+      assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+      assertTrue(e.getCause().getMessage().matches(".*HashSet.*Integer.*"));
+    }
+    try {
+      RPC.decodeRequest(generateHEClassYSpoofing());
+      fail("Expected IncompatibleRemoteServiceException from testNestedGenericClasses (8)");
+    } catch (IncompatibleRemoteServiceException e) {
+      // Expected to get here
+      assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+      assertTrue(e.getCause().getMessage().matches(".*HashSet.*String.*"));
+    }
+  }
+
+  /**
+   * This checks situations in which an RPC message is modified to replace
+   * arguments of a primitive type with another primitive type.
+   */
+  public void testPrimitiveSpoofingPrimitive() {
+    try {
+      // An integer can pretend to be a string, and the result will be the
+      // Integer class serialization string appearing as the string value.
+      RPC.decodeRequest(generateIntegerSpoofingString());
+    } catch (IncompatibleRemoteServiceException e) {
+      fail("Unexpected IncompatibleRemoteServiceException from testPrimitiveSpoofingPrimitive (1)");
+    }
+    try {
+      RPC.decodeRequest(generateStringSpoofingInteger());
+      fail("Expected IncompatibleRemoteServiceException from testPrimitiveSpoofingPrimitive (2)");
+    } catch (IncompatibleRemoteServiceException e) {
+      // Expected to get here
+      assertEquals(SerializationException.class, e.getCause().getClass());
+    }
+  }
+
+  /**
+   * This checks that Collections.singletonList correctly reports that it is an
+   * incorrect type.
+   */
+  public void testSingletonListSpoofingClass() {
+    try {
+      RPC.decodeRequest(generateSingletonListSpoofingClass());
+      fail("Expected IncompatibleRemoteServiceException from testSingletonListSpoofingClass");
+    } catch (IncompatibleRemoteServiceException e) {
+      // Expected to get here
+      assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+      assertTrue(e.getCause().getMessage().matches(".*List.*AClass.*"));
+    }
+  }
+
+  /**
+   * This checks that TreeMap correctly reports that it is an incorrect type.
+   */
+  public void testTreeMapSpoofingClass() {
+    try {
+      RPC.decodeRequest(generateTreeMapSpoofingClass());
+      fail("Expected IncompatibleRemoteServiceException from testTreeMapSpoofingClass");
+    } catch (IncompatibleRemoteServiceException e) {
+      // Expected to get here
+      assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+      assertTrue(e.getCause().getMessage().matches(".*TreeMap.*AClass.*"));
+    }
+  }
+
+  /**
+   * This checks that TreeSet correctly reports that it is an incorrect type.
+   */
+  public void testTreeSetSpoofingClass() {
+    try {
+      RPC.decodeRequest(generateTreeSetSpoofingClass());
+      fail("Expected IncompatibleRemoteServiceException from testTreeSetSpoofingClass");
+    } catch (IncompatibleRemoteServiceException e) {
+      // Expected to get here
+      assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+      assertTrue(e.getCause().getMessage().matches(".*TreeSet.*AClass.*"));
+    }
+  }
+
+  /**
+   * This checks situations in which an RPC message is modified to replace
+   * arguments of a primitive value type with another primitive type.
+   */
+  public void testValueSpoofing() {
+    try {
+      // When an int appears in place of a string, the result will be the
+      // int value indexing the string table, which will result in
+      // an out of bounds exception if the index is out of bounds, or
+      // an incorrect string if the integer value is within range of the string
+      // table.
+      RPC.decodeRequest(generateIntSpoofingString());
+      fail("Expected ArrayIndexOutOfBoundsException from testValueSpoofing (1)");
+    } catch (ArrayIndexOutOfBoundsException e) {
+      // Expected
+    }
+    try {
+      // When a string pretends to be an int, it simply results in an incorrect
+      // integer value.
+      RPC.decodeRequest(generateStringSpoofingInt());
+    } catch (Exception e) {
+      fail("Unexpected exception: " + e.toString());
+    }
+  }
+
+  /**
+   * This checks that Vector correctly reports that it is an incorrect type.
+   */
+  public void testVectorSpoofingClass() {
+    try {
+      RPC.decodeRequest(generateVectorSpoofingClass());
+      fail("Expected IncompatibleRemoteServiceException from testVectorSpoofingClass");
+    } catch (IncompatibleRemoteServiceException e) {
+      // Expected to get here
+      assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+      assertTrue(e.getCause().getMessage().matches(".*Vector.*AClass.*"));
+    }
+  }
+}
diff --git a/user/test/com/google/gwt/user/server/rpc/TypeCheckedGenericClass_ServerCustomFieldSerializer.java b/user/test/com/google/gwt/user/server/rpc/TypeCheckedGenericClass_ServerCustomFieldSerializer.java
new file mode 100644
index 0000000..7fd6fba
--- /dev/null
+++ b/user/test/com/google/gwt/user/server/rpc/TypeCheckedGenericClass_ServerCustomFieldSerializer.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2011 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.user.server.rpc;
+
+import com.google.gwt.user.client.rpc.SerializationException;
+import com.google.gwt.user.client.rpc.TypeCheckedGenericClass;
+import com.google.gwt.user.client.rpc.TypeCheckedObjectsTestSetValidator;
+import com.google.gwt.user.server.rpc.impl.DequeMap;
+import com.google.gwt.user.server.rpc.impl.SerializabilityUtil;
+import com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Type;
+import java.util.HashMap;
+
+/**
+ * This class is defined outside of the TypeCheckedObjectTestSetFactory because
+ * of a bug where custom field serializers cannot be inner classes. Once we fix
+ * this bug we can move this class into the test set factory.
+ */
+@SuppressWarnings({"rawtypes", "unused"})
+public class TypeCheckedGenericClass_ServerCustomFieldSerializer {
+  @SuppressWarnings("unchecked")
+  public static void deserializeChecked(ServerSerializationStreamReader streamReader,
+      TypeCheckedGenericClass instance, Class<?> instanceClass,
+      DequeMap<Type, Type> resolvedTypes) throws SerializationException {
+    Object junkKey = streamReader.readObject();
+    Object junkValue = streamReader.readObject();
+
+    /*
+     * If deserializing a superclass we will not have been instantiated using
+     * the custom instantiator, so skip the checks for correct markers.
+     */
+    if (instance.getClass() != TypeCheckedGenericClass.class
+        || ((instance.getMarkerKey() instanceof Integer)
+            && ((Integer) instance.getMarkerKey()).intValue() == 54321
+            && (instance.getMarkerValue() instanceof String) && ((String) instance.getMarkerValue())
+            .equals("LocalMarker"))) {
+      instance.setMarker(TypeCheckedObjectsTestSetValidator.markerKey,
+          TypeCheckedObjectsTestSetValidator.markerValue);
+    } else {
+      throw new SerializationException(
+          "Incorrect markers in TypeCheckedGenericClass server deserialization. "
+              + "Custom instantiate probably not called.");
+    }
+
+    try {
+      Field declField = TypeCheckedGenericClass.class.getField("hashField");
+      Type declGenericType = declField.getGenericType();
+      SerializabilityUtil.resolveTypes(declGenericType, resolvedTypes);
+      instance.hashField = (HashMap) streamReader.readObject(declGenericType, resolvedTypes);
+      SerializabilityUtil.releaseTypes(declGenericType, resolvedTypes);
+    } catch (Exception e) {
+      throw new SerializationException(e);
+    }
+  }
+
+  public static TypeCheckedGenericClass instantiateChecked(
+      ServerSerializationStreamReader streamReader, Class<?> instanceClass,
+      DequeMap<Type, Type> resolvedTypes) {
+    TypeCheckedGenericClass<Integer, String> result =
+        new TypeCheckedGenericClass<Integer, String>();
+    result.setMarker(54321, "LocalMarker");
+    return result;
+  }
+}
\ No newline at end of file
diff --git a/user/test/com/google/gwt/user/server/rpc/TypeCheckedObjectsTestServiceImpl.java b/user/test/com/google/gwt/user/server/rpc/TypeCheckedObjectsTestServiceImpl.java
new file mode 100644
index 0000000..4d2a6b0
--- /dev/null
+++ b/user/test/com/google/gwt/user/server/rpc/TypeCheckedObjectsTestServiceImpl.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2011 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.user.server.rpc;
+
+import com.google.gwt.user.client.rpc.TypeCheckedGenericClass;
+import com.google.gwt.user.client.rpc.TypeCheckedObjectsTestService;
+import com.google.gwt.user.client.rpc.TypeCheckedObjectsTestSetFactory.TypeCheckedFieldClass;
+import com.google.gwt.user.client.rpc.TypeCheckedObjectsTestSetFactory.TypeCheckedSuperClass;
+import com.google.gwt.user.client.rpc.TypeCheckedObjectsTestSetValidator;
+import com.google.gwt.user.client.rpc.TypeUncheckedGenericClass;
+
+/**
+ * Servlet used by the
+ * {@link com.google.gwt.user.client.rpc.TypeCheckedObjectsTest} unit tests.
+ */
+public class TypeCheckedObjectsTestServiceImpl extends HybridServiceServlet implements
+    TypeCheckedObjectsTestService {
+
+  /*
+   * @see
+   * com.google.gwt.user.client.rpc.TypeCheckedObjectsTestService#echo(com.google
+   * .gwt.user.client.rpc.TypeCheckedGenericClass)
+   */
+  @Override
+  public TypeCheckedGenericClass<Integer, String> echo(
+      TypeCheckedGenericClass<Integer, String> arg1) {
+    if (!TypeCheckedObjectsTestSetValidator.isValid(arg1)) {
+      throw new RuntimeException();
+    }
+
+    return arg1;
+  }
+
+  /*
+   * @see
+   * com.google.gwt.user.client.rpc.TypeCheckedObjectsTestService#echo(com.google
+   * .
+   * gwt.user.client.rpc.TypeCheckedObjectsTestSetFactory.TypeCheckedSuperClass)
+   */
+  @Override
+  public TypeCheckedSuperClass<Integer, String> echo(TypeCheckedSuperClass<Integer, String> arg1) {
+    if (!TypeCheckedObjectsTestSetValidator.isValid(arg1)) {
+      throw new RuntimeException();
+    }
+
+    return arg1;
+  }
+
+  /*
+   * @see
+   * com.google.gwt.user.client.rpc.TypeCheckedObjectsTestService#echo(com.google
+   * .
+   * gwt.user.client.rpc.TypeCheckedObjectsTestSetFactory.TypeCheckedFieldClass)
+   */
+  @Override
+  public TypeCheckedFieldClass<Integer, String> echo(TypeCheckedFieldClass<Integer, String> arg1) {
+    if (!TypeCheckedObjectsTestSetValidator.isValid(arg1)) {
+      throw new RuntimeException();
+    }
+
+    return arg1;
+  }
+
+  /*
+   * @see
+   * com.google.gwt.user.client.rpc.TypeCheckedObjectsTestService#echo(com.google
+   * .gwt.user.client.rpc.TypeUncheckedGenericClass)
+   */
+  @Override
+  public TypeUncheckedGenericClass<Integer, String> echo(
+      TypeUncheckedGenericClass<Integer, String> arg1) {
+    if (!TypeCheckedObjectsTestSetValidator.isValid(arg1)) {
+      throw new RuntimeException();
+    }
+
+    return arg1;
+  }
+}