Client-server communication peculiarities with GWT and App Engine DataNucleus
3 min read
3 min read
com.google.gwt.user.client.rpc.SerializationException: Type 'org.datanucleus.sco.backed.List' was not assignable to 'com.google.gwt.user.client.rpc.IsSerializable' and did not have a custom field serializer.For security purposes, this type will not be serialized.: instance = []
at com.google.gwt.user.server.rpc.impl.ServerSerializationStreamWriter.serialize(ServerSerializationStreamWriter.java:610)
at com.google.gwt.user.client.rpc.impl.AbstractSerializationStreamWriter.writeObject(AbstractSerializationStreamWriter.java:129)
at com.google.gwt.user.server.rpc.impl.ServerSerializationStreamWriter$ValueWriter$8.write(ServerSerializationStreamWriter.java:152)
at com.google.gwt.user.server.rpc.impl.ServerSerializationStreamWriter.serializeValue(ServerSerializationStreamWriter.java:534)
at com.google.gwt.user.server.rpc.impl.ServerSerializationStreamWriter.serializeClass(ServerSerializationStreamWriter.java:700)
at com.google.gwt.user.server.rpc.impl.ServerSerializationStreamWriter.serializeImpl(ServerSerializationStreamWriter.java:730)
at com.google.gwt.user.server.rpc.impl.ServerSerializationStreamWriter.serialize(ServerSerializationStreamWriter.java:612)
at com.google.gwt.user.client.rpc.impl.AbstractSerializationStreamWriter.writeObject(AbstractSerializationStreamWriter.java:129)
at com.google.gwt.user.server.rpc.impl.ServerSerializationStreamWriter$ValueWriter$8.write(ServerSerializationStreamWriter.java:152)
at com.google.gwt.user.server.rpc.impl.ServerSerializationStreamWriter.serializeValue(ServerSerializationStreamWriter.java:534)
at com.google.gwt.user.server.rpc.impl.ServerSerializationStreamWriter.serializeClass(ServerSerializationStreamWriter.java:700)
at com.google.gwt.user.server.rpc.impl.ServerSerializationStreamWriter.serializeImpl(ServerSerializationStreamWriter.java:730)
at com.google.gwt.user.server.rpc.impl.ServerSerializationStreamWriter.serialize(ServerSerializationStreamWriter.java:612)
at com.google.gwt.user.client.rpc.impl.AbstractSerializationStreamWriter.writeObject(AbstractSerializationStreamWriter.java:129)
at com.google.gwt.user.server.rpc.impl.ServerSerializationStreamWriter$ValueWriter$8.write(ServerSerializationStreamWriter.java:152)
at com.google.gwt.user.server.rpc.impl.ServerSerializationStreamWriter.serializeValue(ServerSerializationStreamWriter.java:534)
at com.google.gwt.user.server.rpc.RPC.encodeResponse(RPC.java:609)
at com.google.gwt.user.server.rpc.RPC.encodeResponseForSuccess(RPC.java:467)
at com.google.gwt.user.server.rpc.RPC.invokeAndEncodeResponse(RPC.java:564)
at com.google.gwt.user.server.rpc.RPC.invokeAndEncodeResponse(RPC.java:544)
at com.google.gwt.user.server.rpc.RPC.invokeAndEncodeResponse(RPC.java:504)
org.datanucleus.sco.backed.List
type in my entities which I transfer over the network. The reason however is the persist operation on App Engine. The code is like@OverrideIt takes an object of type
public void saveOrUpdate(SourceCodeItem item) {
PersistenceManager pm = getPersistenceManager();
try {
pm.currentTransaction().begin();
pm.makePersistent(item);
pm.currentTransaction().commit();
} catch (Exception ex) {
if (pm.currentTransaction().isActive())
pm.currentTransaction().rollback();
} finally {
pm.close();
}
}
SourceCodeItem
and makes it persistent by calling makePersistent(...)
. This call to makePersistent changes the state of the passed object. The SourceCodeItem object has the following structure (cut out details):@PersistenceCapable(identityType = IdentityType.APPLICATION)The highlighted member variable declaration is a list which is not persistent and it gets initialized in the constructor. However after a call to the JDO's
public class SourceCodeItem implements IsSerializable {
@PrimaryKey
@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
@Extension(vendorName = "datanucleus", key = "gae.encoded-pk", value = "true")
private String id;
@Persistent
private String title;
...
private List<Label> labels;
public SourceCodeItem() {
...
labels = new ArrayList<Label>();
}
...
}
makePersistent(..)
this list will be instantiated with the above mentioned org.datanucleus.sco.backed.List
type. If you don't "detach" the object from JDOs manager it will be transferred with that instance type of the list which cannot be serialized and you'll get the exception I described in the beginning.saveOrUpdate(SourceCodeItem)
and explicitly detach your object. This would result in the following code:@OverrideAlternatively, which is my favorite option is to add the following to your jdoconfig.xml:
public SourceCodeItem saveOrUpdate(SourceCodeItem item) {
SourceCodeItem result = null;
PersistenceManager pm = getPersistenceManager();
try {
pm.currentTransaction().begin();
pm.makePersistent(item);
pm.currentTransaction().commit();
result = pm.detachCopy(item);
} catch (Exception ex) {
if (pm.currentTransaction().isActive())
pm.currentTransaction().rollback();
} finally {
pm.close();
}
return result;
}
<property name="datanucleus.DetachAllOnCommit" value="true"/>In this way the detaching will be done automatically after a commit and you can leave the code as it was initially. In such a way you'd get the following lifecycle: