Finishes issue 1139.  Adds java.io.Serializable to our JRE's version of Throwable.  The Throwable.getMessage() method for checked exceptions thrown by RPC methods no longer returns null if a user forgets to extend SerializableException.

The basic strategy is:

   1. Have the client-side code use Throwable's generated field serializer while the server special cases Throwable in its serialization/deserialization logic. 
   2. Only serialize the detailMessage field on Throwable.  The cause field was excluded because it could cause significant bloating of the client side proxy given the current setup of STOB.  The stack trace was not serialized because that does not seem like the type of information that you would want your clients to see.

SerializableException is now deprecated as since the detailMessage is now serialized by default.

Review by: Bobv

git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@2047 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/user/client/rpc/SerializableException.java b/user/src/com/google/gwt/user/client/rpc/SerializableException.java
index 2e713f2..eec580d 100644
--- a/user/src/com/google/gwt/user/client/rpc/SerializableException.java
+++ b/user/src/com/google/gwt/user/client/rpc/SerializableException.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 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
@@ -18,11 +18,14 @@
 /**
  * Superclass for exceptions thrown from RPC methods (those appearing in
  * interfaces derived from {@link RemoteService}).
+ * 
+ * @deprecated As of GWT 1.5, {@link Exception} implements
+ *             {@link java.io.Serializable Serializable} and can be used in place
+ *             of this class.
  */
+@Deprecated
 public class SerializableException extends Exception implements IsSerializable {
 
-  private String msg;
-
   /**
    * The default constructor. This constructor is used implicitly during
    * serialization or when constructing subclasses.
@@ -35,7 +38,7 @@
    * constructor is most often called by subclass constructors.
    */
   public SerializableException(String msg) {
-    this.msg = msg;
+    super(msg);
   }
 
   /**
@@ -48,11 +51,6 @@
     return null;
   }
 
-  @Override
-  public String getMessage() {
-    return msg;
-  }
-
   /**
    * No effect; exception chaining is not currently supported for serialized
    * exceptions.
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 4596cd5..0eed41c 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
@@ -103,18 +103,10 @@
       ArrayList<Field> fieldList = new ArrayList<Field>();
       Field[] fields = clazz.getDeclaredFields();
       for (Field field : fields) {
-        assert (field != null);
-
-        int fieldModifiers = field.getModifiers();
-        if (Modifier.isStatic(fieldModifiers)
-            || Modifier.isTransient(fieldModifiers)
-            || Modifier.isFinal(fieldModifiers)) {
-          continue;
+        if (fieldQualifiesForSerialization(field)) {
+          fieldList.add(field);
         }
-
-        fieldList.add(field);
       }
-
       serializableFields = fieldList.toArray(new Field[fieldList.size()]);
 
       // sort the fields by name
@@ -234,6 +226,25 @@
     return false;
   }
 
+  private static boolean fieldQualifiesForSerialization(Field field) {
+    if (Throwable.class == field.getDeclaringClass()) {
+      /**
+       * Only serialize Throwable's detailMessage field; all others are ignored.
+       * 
+       * NOTE: Changing the set of fields that we serialize for Throwable will
+       * necessitate a change to our JRE emulation's version of Throwable.
+       */
+      if ("detailMessage".equals(field.getName())) {
+        assert (isNotStaticTransientOrFinal(field));
+        return true;
+      } else {
+        return false;
+      }
+    } else {
+      return isNotStaticTransientOrFinal(field);
+    }
+  }
+
   private static void generateSerializationSignature(Class<?> instanceType,
       CRC32 crc) throws UnsupportedEncodingException {
     crc.update(getSerializedTypeName(instanceType).getBytes(DEFAULT_ENCODING));
@@ -293,6 +304,16 @@
     }
   }
 
+  private static boolean isNotStaticTransientOrFinal(Field field) {
+    /*
+     * Only serialize fields that are not static, transient and final.
+     */
+    int fieldModifiers = field.getModifiers();
+    return !Modifier.isStatic(fieldModifiers)
+        && !Modifier.isTransient(fieldModifiers)
+        && !Modifier.isFinal(fieldModifiers);
+  }
+
   private static void putCachedCRCForClass(Class<?> instanceType, String crc32) {
     synchronized (classCRC32Cache) {
       classCRC32Cache.put(instanceType, crc32);
diff --git a/user/super/com/google/gwt/emul/java/lang/Throwable.java b/user/super/com/google/gwt/emul/java/lang/Throwable.java
index d593a6a..f212156 100644
--- a/user/super/com/google/gwt/emul/java/lang/Throwable.java
+++ b/user/super/com/google/gwt/emul/java/lang/Throwable.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 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
@@ -16,32 +16,44 @@
 package java.lang;
 
 import java.io.PrintStream;
+import java.io.Serializable;
 
 /**
  * See <a
  * href="http://java.sun.com/j2se/1.5.0/docs/api/java/lang/Throwable.html">the
  * official Java API doc</a> for details.
  */
-public class Throwable {
-
-  private Throwable cause;
-  private String message;
-  private StackTraceElement[] stackTrace;
+public class Throwable implements Serializable {
+  /*
+   * NOTE: We cannot use custom field serializers because we need the client and
+   * server to use different serialization strategies to deal with this type.
+   * The client uses the generated field serializers which can use JSNI. That
+   * leaves the server free to special case Throwable so that only the
+   * detailMessage field is serialized.
+   * 
+   * Throwable is given special treatment by server's SerializabilityUtil class
+   * to ensure that only the detailMessage field is serialized. Changing the
+   * field modifiers below may necessitate a change to the server's
+   * SerializabilityUtil.fieldQualifiesForSerialization(Field) method.
+   */
+  private transient Throwable cause;
+  private String detailMessage;
+  private transient StackTraceElement[] stackTrace;
 
   public Throwable() {
   }
 
   public Throwable(String message) {
-    this.message = message;
+    this.detailMessage = message;
   }
 
   public Throwable(String message, Throwable cause) {
     this.cause = cause;
-    this.message = message;
+    this.detailMessage = message;
   }
 
   public Throwable(Throwable cause) {
-    this.message = (cause == null) ? null : cause.toString();
+    this.detailMessage = (cause == null) ? null : cause.toString();
     this.cause = cause;
   }
 
@@ -63,7 +75,7 @@
   }
 
   public String getMessage() {
-    return message;
+    return detailMessage;
   }
 
   /**
diff --git a/user/test/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilderTest.java b/user/test/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilderTest.java
index 00afa64..d41b6b8 100644
--- a/user/test/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilderTest.java
+++ b/user/test/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilderTest.java
@@ -194,7 +194,10 @@
         new TypeInfo(CovariantArrays.C.class.getName() + "[]", true),

         new TypeInfo(CovariantArrays.D.class.getName() + "[]", true),

         new TypeInfo(CovariantArrays.D.class.getName(), true),

-        new TypeInfo(String.class.getName(), true)};

+        new TypeInfo(Exception.class.getName(), false),

+        new TypeInfo(RuntimeException.class.getName(), false),

+        new TypeInfo(String.class.getName(), true),

+        new TypeInfo(Throwable.class.getName(), false)};

     validateSTO(sto, expected);

   }

 

@@ -259,7 +262,10 @@
             true),

         new TypeInfo(

             makeSourceName(NotAllSubtypesAreSerializable.D.class.getName()),

-            true), new TypeInfo(String.class.getName(), true)};

+            true), new TypeInfo(Exception.class.getName(), false),

+        new TypeInfo(RuntimeException.class.getName(), false),

+        new TypeInfo(String.class.getName(), true),

+        new TypeInfo(Throwable.class.getName(), false)};

     validateSTO(sto, expected);

   }