Export to GitHub

google-web-toolkit - issue #5811

RPC serialization policys omit serializable types referenced only in generics


Posted on Dec 23, 2010 by Happy Kangaroo

Found in GWT Release (e.g. 1.5.3, 1.6 RC):

GWT 1.7.1

Encountered on OS / Browser (e.g. WinXP, IE6-7, FF3):

Linux (Fedora Core 12), all browsers

Detailed description (please be as specific as possible):

  1. Create a class Foo which implements IsSerializable
  2. Reference Foo in a generic type accepted or returned by an RPC method
  3. Do not reference Foo directly as an argument or result of an RPC method
  4. When code attempts to call the RPC method, the following exception occurs:

com.google.gwt.user.client.rpc.SerializationException: Type 'Foo' was not included in the set of types which can be serialized by this SerializationPolicy or its Class object could not be loaded. For security purposes, this type will not be serialized.

Shortest code snippet which demonstrates issue (please indicate where actual result differs from expected result):

The RPC service method signature:

void getSomeFoos(AsyncCallback<List<Foo>> callback);

Call this from the client in the usual way:

GWT.create(FooService.class).getSomeFoos(callback)

Expected: The callback's onSuccess gets called with the List of Foos

Actual: The call fails with an HTTP 500 error, and the exception above appears in the server logs.

Workaround if you have one:

Reference Foo directly as an argument to / result of an RPC method (perhaps spuriously as per the Stack Overflow post below). Typically in our case this meant returning an array of Foo rather than a generic Collection<Foo>.

Links to relevant GWT Developer Forum posts:

http://stackoverflow.com/questions/4202964/serializationpolicy-error-when-performing-rpc-from-within-gwt-application

Comment #1

Posted on Jun 13, 2011 by Helpful Camel

Just wanted to comment that I think this probably has biten more people than have starred this issue. I only found this issue thanks to the link from StackOverflow and even then, that was only after a couple of hours of searching.

Comment #2

Posted on Jan 9, 2012 by Massive Rabbit

yep, that might be annoying, but I can imagine that it needs a different code-analyzing approach and therefore might be too much trouble for the problem issued.

I have a simple solution, works for me:

Make sure your generics at least implement some interface and you'll be good to go; the compiler then sees the classes that need to be on the whitelist, as they are referenced in the RPC method (references via this interface)

(e.g. instead of HashMap, use HashMap)

Comment #3

Posted on Feb 24, 2012 by Massive Monkey

Perhaps the good people at Google could add an annotation on the interface that defines the service, where we could manually add whitelisted classes.

Comment #4

Posted on Mar 15, 2012 by Quick Wombat

I get the same problem with compisite classes using CustomFieldSerializer classes.

I have the immutable class Wizard, with to fields: a String and a List where Step is another immutable class with it own CustomFieldSerializer class.

The CustomFieldSerializer class for wizard, serialize the List by iterating over the list, and serialize each Step instance at a time, so List is not serialized.

When trying to Serialize a wizard class in a RemoteServiceServlet, it gives this error.

The workarround is the same, add a method to the RemoteServiceServlet with Step as the return value, and as a Parameter.

Comment #5

Posted on Jan 18, 2013 by Happy Camel

This really bit me in the a** not because I am using custom types but because I have a DTO with a HashMap data member. The Object can be java.lang.Boolean, java.lang.Integer, java.util.Date.

Please add basic language types to the whitelist.

Comment #6

Posted on May 28, 2013 by Happy Lion

I hate this serialization problem with GWT!!!! Why don't you just make it easier and not harder for the developers!! I HATE THIS!!!

Comment #7

Posted on Jul 24, 2013 by Happy Cat

This is really lame. This shortcoming needs to be better documented. And there needs to be a better way to whitelist data types other than the way described here: http://stackoverflow.com/questions/4202964/serializationpolicy-error-when-performing-rpc-from-within-gwt-application How about a tag in the gwt.xml file?

Comment #8

Posted on Aug 4, 2013 by Grumpy Monkey

Just created a small project to see if this issue still exists in GWT 2.5.1 but it works as expected.

Relevant code used:

public class Foo implements Serializable { private String name; public Foo() { this("Unknown"); } public Foo(String name) { this.name = name } //getter, setter }

public class Bar implements IsSerializable { private String name; public Bar() { this("Unknown"); } public Bar(String name) { this.name = name } //getter, setter }

@RemoteServiceRelativePath("exampleService") public interface ExampleService extends RemoteService { //No direct reference to Foo, Bar. Only as type parameter. //Return type and parameter to check both directions: client -> server and server -> client List filterFoosByName(List foos, String name); List filterBarsByName(List bars, String name); }

@Override public void onModuleLoad() { testFoos(); testBars(); }

public void testFoos() { List foos = new ArrayList(); foos.add(new Foo("Joe")); foos.add(new Foo("John"));

ExampleServiceAsync exampleService = GWT.create(ExampleService.class); exampleService.filterFoosByName(foos, "John", new SysoutCallback>()); }

public void testBars() { List bars = new ArrayList(); bars.add(new Bar("Joe")); bars.add(new Bar("John"));

ExampleServiceAsync exampleService = GWT.create(ExampleService.class); exampleService.filterBarsByName(bars, "John", new SysoutCallback>()); }

GWT-RPC policy file generated:

biz.codr.gwt.contrib.playground.client.model.Bar, true, true, true, true, biz.codr.gwt.contrib.playground.client.model.Bar/2781003685, 2781003685 [Lbiz.codr.gwt.contrib.playground.client.model.Bar;, true, true, true, true, [Lbiz.codr.gwt.contrib.playground.client.model.Bar;/2912682399, 2912682399 biz.codr.gwt.contrib.playground.client.model.Foo, true, true, true, true, biz.codr.gwt.contrib.playground.client.model.Foo/941612411, 941612411 [Lbiz.codr.gwt.contrib.playground.client.model.Foo;, true, true, true, true, [Lbiz.codr.gwt.contrib.playground.client.model.Foo;/252201666, 252201666 biz.codr.gwt.contrib.playground.client.rpc.ExampleService, false, false, false, false, _, 237509316 com.google.gwt.user.client.rpc.IncompatibleRemoteServiceException, true, true, true, true, com.google.gwt.user.client.rpc.IncompatibleRemoteServiceException/3936916533, 3936916533 com.google.gwt.user.client.rpc.RpcTokenException, true, true, false, false, com.google.gwt.user.client.rpc.RpcTokenException/2345075298, 2345075298 com.google.gwt.user.client.rpc.XsrfToken, false, false, true, true, com.google.gwt.user.client.rpc.XsrfToken/4254043109, 4254043109 java.lang.Exception, true, false, true, false, java.lang.Exception/1920171873, 1920171873 java.lang.RuntimeException, true, false, true, false, java.lang.RuntimeException/515124647, 515124647 java.lang.String, true, true, true, true, java.lang.String/2004016611, 2004016611 java.lang.Throwable, true, false, true, false, java.lang.Throwable/2953622131, 2953622131 java.util.ArrayList, true, true, true, true, java.util.ArrayList/4159755760, 4159755760 java.util.Arrays$ArrayList, true, true, true, true, java.util.Arrays$ArrayList/2507071751, 2507071751 java.util.Collections$EmptyList, true, true, true, true, java.util.Collections$EmptyList/4157118744, 4157118744 java.util.Collections$SingletonList, true, true, true, true, java.util.Collections$SingletonList/1586180994, 1586180994 java.util.LinkedList, true, true, true, true, java.util.LinkedList/3953877921, 3953877921 java.util.Stack, true, true, true, true, java.util.Stack/1346942793, 1346942793 java.util.Vector, true, true, true, true, java.util.Vector/3057315478, 3057315478

The policy file looks like expected.

So this issue can be closed, unless someone provides a failing example that does not use List, Map or similar, because using Object with GWT-RPC is a totally different story.

I have created a new issue asking for an annotation that can be used to manually put types on the GWT-RPC whitelist:

https://code.google.com/p/google-web-toolkit/issues/detail?id=8298

Comment #9

Posted on Aug 6, 2013 by Swift Rhino

Thanks for the heads up Jens.

Comment #10

Posted on Aug 6, 2013 by Swift Monkey

It fails when using

public interface ExampleService extends RemoteService { Foo getFoo(String param); }

where Foo is an immutable class with no zero-argument constructor that implements IsSerializable and has

public final class Foo_CustomFieldSerializer { public boolean hasCustomInstantiateInstance() { return true; }

public static Foo instantiate(SerializationStreamReader streamReader) throws SerializationException { // implementation }

public static void deserialize(final SerializationStreamReader streamReader, final Foo instance) throws SerializationException { // nothing to do here }

public static void serialize(final SerializationStreamWriter streamWriter, final Foo instance) throws SerializationException { // implementation } }

java.lang.Double is not added to *.rpc

Comment #11

Posted on Aug 6, 2013 by Swift Monkey

let me be more precise, the exception is thrown from

com.google.gwt.user.server.rpc.impl.StandardSerializationPolicy:175

which can be traced back to the line marked with the arrow:

public static void serialize(final SerializationStreamWriter streamWriter, final Foo instance) throws SerializationException { -> streamWriter.writeObject(instance.getGenericField()); }

The execution runs fine when Double fakeMethod(Double aDouble) is added to ExampleService

I'm using Eclipse (for Java) 4.3 (Keppler) Google Plugin (3.3.0) For Eclipse 4.3 GWT 2.5.1

Comment #12

Posted on Aug 6, 2013 by Grumpy Monkey

Ok confirmed using the following code:

public final class Foo implements IsSerializable { //can also be Serializable, doesn't matter

private final Date CREATION_DATE = new Date(); private final T value;

public Foo(T value) { this.value = value; } public T getValue() { return value; }
public Date getCreationDate() { return CREATION_DATE; }

}

public interface ExampleService extends RemoteService { Foo getFoo(); }

public final class Foo_CustomFieldSerializer extends CustomFieldSerializer> {

@Override public boolean hasCustomInstantiateInstance() { return true; }

@Override public Foo instantiateInstance(SerializationStreamReader streamReader) throws SerializationException {
return new Foo(streamReader.readObject()); }

@Override public void deserializeInstance(SerializationStreamReader streamReader, Foo instance) throws SerializationException { //NOOP - see instantiateInstance() }

@Override public void serializeInstance(SerializationStreamWriter streamWriter, Foo instance) throws SerializationException { streamWriter.writeObject(instance.getValue()); }

public static void deserialize(SerializationStreamReader reader, Foo instance) { //NOOP - see instantiate() }

public static void serialize(SerializationStreamWriter writer, Foo instance) throws SerializationException { writer.writeObject(instance.getValue()); }

public static Foo instantiate(SerializationStreamReader reader) throws SerializationException { return new Foo(reader.readObject()); }

}

Serialization policy file only contains Foo but not Date and not Double.

Some things play together here:

1.) GWT-RPC does not support final fields and normally emits a warning if a serializable class contains final fields. All types of final fields will not be added to the whitelist. 2.) Because Foo has a CustomFieldSerializer no warning is emitted. 3.) The generator probably knows that the class's type parameter "T" is used in a final field and thus does not add possible types for T to the whitelist although a CustomFieldSerializer is available.

Using the Foo example above the correct behavior of the generator should be: 1.) always print a warning for CREATION_DATE and not add Date to the whitelist 2.) if a CustomFieldSerializer is available don't emit a warning for "final T value" and add possible types for T to the whitelist so that the CustomFieldSerializer can do its work.

Workaround is to remove the final keyword on these properties.

Comment #13

Posted on Aug 6, 2013 by Swift Monkey

this is a bit of topic, but what's the difference between static and instance methods in the above code?

The dev guide suggests to extend CustomFieldSerializer and override instance methods, however in my case I got an error that static serialize() is not defined.

So I looked up the java.util.Map_CustomFieldSerializer and implemented the static methods only instead.

Does the fact that Foo instances are created using a static factory method in my case have anything to do with it?

Should the dev guide be updated?

Comment #14

Posted on Aug 7, 2013 by Grumpy Monkey

I think before GWT 2.4 there was no CustomFieldSerializer class you could extend from. You just had to implement these static methods by convention and they were used on client and server side. Now if you extend CustomFieldSerializer the non-static methods are used on the server to reduce reflection (as the JavaDoc states) but you continue to need the static methods for use on the client side. Typically you would delegate from the non-static methods to the static ones.

The dev guide could be updated to give some information about the static methods.

But its not a problem with serializers. Its the way the generator handles final fields that causes this issue.

Comment #15

Posted on Aug 9, 2013 by Swift Rhino

Reopening, per new comments.

Comment #16

Posted on Jun 10, 2015 by Massive Cat

Issue tracked moved to github, see https://github.com/gwtproject/gwt/issues

Status: MovedToGithub

Labels:
Category-RPC