GWT Field Field Serialization Error

Consider the immutable class Foo (a POJO consisting of an identifier and a name) that must be serialized so that data can be sent from the server to the client.

public final class Foo { private final int m_id; private final String m_displayName; private Foo(final int id, final String displayName) { m_id = id; m_displayName = displayName; } public static Foo create(final int id, final String displayName) { // Some error checking occurs here. . . . m_id = id; m_displayName = displayName; } // Getters etc. . . . } 

Activation of the Foo object occurs through the static factory function, and since the immutable object does not have a constructor with a null argument.

Consider also the immutable Bar class, which contains the Foo data element and implements the Builder template to create it (omitted from the fragment, since it is not related to the problem).

 public final class Bar { private final Foo m_foo; . . . private Bar(final Builder builder) { . . . } public static Builder createBuilder() { return new Builder(); } } 

After evaluating my options regarding how I should serialize this object without removing its immutability or adding zero-args constructors for serialization, I came to the conclusion that I need to implement CustomFieldSerializer (both for the client and the server).

I followed the recommendations written in the article Server Communication in GWT, and implemented my own CustomFieldSerializer, as shown below.

 // Contains the serialization logic of the class Bar. public final class Bar_CustomFieldSerializerBase { public static Bar instantiate(final SerializationStreamReader streamReader) throws SerializationException { return Bar.createBuilder().forFoo((Foo) streamReader.readObject()).build(); } public static void serialize(final SerializationStreamWriter streamWriter, final Bar instance) throws SerializationException { // . . . streamWriter.writeObject(instance.getFoo()); } public static void deserialize(final SerializationStreamReader streamReader, final Bar instance) { /* * Empty as everything is handled on instantiateInstance(). */ } } // The CustomFieldSerializer for class Bar. public class Bar_CustomFieldSerializer extends CustomFieldSerializer<Bar> { public static void deserialize(final SerializationStreamReader streamReader, final Bar instance) throws SerializationException { Bar_CustomFieldSerializerBase.deserialize(streamReader, instance); } public static void serialize(final SerializationStreamWriter streamWriter, final Bar instance) throws SerializationException { Bar_CustomFieldSerializerBase.serialize(streamWriter, instance); } public static Bar instantiate(final SerializationStreamReader streamReader) throws SerializationException { return Bar_CustomFieldSerializerBase.instantiate(streamReader); } @Override public boolean hasCustomInstantiateInstance() { return true; } @Override public Bar instantiateInstance(final SerializationStreamReader streamReader) throws SerializationException { return instantiate(streamReader); } @Override public void deserializeInstance(final SerializationStreamReader streamReader, final Bar instance) throws SerializationException { deserialize(streamReader, instance); } @Override public void serializeInstance(final SerializationStreamWriter streamWriter, final Bar instance) throws SerializationException { serialize(streamWriter, instance); } // Server side CustomFieldSerializer for class Bar. public class Bar_ServerCustomFieldSerializer extends ServerCustomFieldSerializer<Bar> { public static void deserialize(ServerSerializationStreamReader streamReader, Bar instance, Type[] expectedParameterTypes, DequeMap<TypeVariable<?>, Type> resolvedTypes) throws SerializationException { /* * Empty as everything is handled on instantiateInstance(). */ } @Override public Bar instantiateInstance(ServerSerializationStreamReader streamReader) throws SerializationException { return Bar_CustomFieldSerializerBase.instantiate(streamReader); } @Override public void deserializeInstance(ServerSerializationStreamReader streamReader, Bar instance, Type[] expectedParameterTypes, DequeMap<TypeVariable<?>, Type> resolvedTypes) throws SerializationException { deserialize(streamReader, instance, expectedParameterTypes, resolvedTypes); } @Override public void deserializeInstance(SerializationStreamReader streamReader, Bar instance) throws SerializationException { Bar_CustomFieldSerializerBase.deserialize(streamReader, instance); } @Override public void serializeInstance(SerializationStreamWriter streamWriter, Bar instance) throws SerializationException { Bar_CustomFieldSerializerBase.serialize(streamWriter, instance); } } 

Since the class panel contains a Foo object that needs to be serialized, I went on and implemented another set of CustomFieldSerializers, this time for the Foo class following the same template for both the client and server.

The problem occurs when serialization occurs for the Bar class, and specifically at this point:

 public static void serialize(final SerializationStreamWriter streamWriter, final Bar instance) throws SerializationException { // . . . streamWriter.writeObject(instance.getFoo()); } 

The error message I get is the following:

 [WARN] Exception while dispatching incoming RPC call com.google.gwt.user.client.rpc.SerializationException: Type 'ui.shared.models.fooItems.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. 

It seems that writeObject () cannot serialize an object of type Foo because the class Foo does not belong to whitelist elements, even if custom serializers were provided for both the client and the server.

I could always skip the call to writeObject () and call writeInt () and writeString () for each Foo data item (which works well), but I'd rather write writeObject (). The solution I propose is highly susceptible to maintenance errors, since any changes to the Foo class in the future should affect both the Foo serializers (obvious) and the string serializers (not so obvious).

I tried almost everything I could find on the network, from the implementation of the isSerializable interface to Foo and Bar (it doesn’t make any difference, and it should not make any difference since AFAIK classes that supply their own serializers do not need to adhere to this rule ) and even supply private zero-args constructors (which should also not make any difference, since the functions of creating an instance of a custom field polarizer should take care of this through static factories).

Why is the Foo class not whitelisted? Am I missing something obvious or misinterpreting something?

Thanks for your time in advance.

+6
source share
1 answer

Well, the problem is that, most likely, you never mention anywhere in your code that the Foo class is ever sent to the server. For instance. you only have utility methods like this:

 interface MyService { Foo getFoo(Bar bar); } 

To use writeObject , you need to explicitly specify a hint for GWT, include the Foo class in the deserialization list, adding a new service method that takes Foo as a parameter:

 interface MyService { Foo getFoo(Bar bar); void setFoo(Foo foo);// letting GWT know that we might send Foo object over the wire, you don't have to ever call this method in your app, or implement it in some meaningful way, just let it be there } 

You do not need to ever call this method in your application or provide it with any implementation. But otherwise it will not work. There are several other ways to create this tooltip for GWT, but the idea is the same, you will explicitly expand the Foo class for GWT-RPC. Also remember that if you use the Bar class in multiple services, you will need to add a method for each service that uses the Bar class

Now, in more detail, why this is happening. On the server side, GWT-RPC keeps track of two lists: objects that it can serialize, and objects that it can deserialize. This information is taken from the RPC policy manifest. In my first example, I only mentioned the Bar object as something that can be sent to the server. But since you defined your own field serializer in the Bar class, GWT does not do any analysis on Bar , so it does not know that the Foo instance can be sent to the server, so it decides that there is no deserializer needed for Foo on the server side. Therefore, when you try to send a Bar instance through the wire, the server will try to deserialize it, but since < readObject used inside the custom serializer, it also tries to find the deserializer for Foo, but it is not allowed, and therefore the whole process of degreasing fails. If you add an additional service method that sends only the Foo object over the wire, GWT will know that such an object can also be sent to the server, so it also marks Foo as deserializable.

This is very confusing, and I personally think that classes with custom serialization should automatically be added to all whitelists, but that’s what it is.

EDIT

Another (without a hacker solution without stupid empty methods) will use a specialized DTO layer for communication (for example, only POJOs with public default constructors), but this can be difficult in cases when you need to send complex graphic objects using a lot of cross-references.

+6
source

All Articles