Another problem with serialization, ManyToMany and StackOverflow
misqu23 Sep 10, 2010 9:51 AMHi
During serialization I get StackOverflowError. My user entity holds references to the set of groups it belongs, and on the other hand group entity holds the set of the users which are belongs to it. During serialization of the user entity errai wants to serialize also groups, but when it serialize the group it starts to serialize the user and so on. In the result we get StackOverflowError. Below there is a simple test case with the corresponding User.class and Group.class implementation.
First the exception :
at org.jboss.errai.bus.server.io.JSONEncoder._encode(JSONEnc
java.lang.StackOverflowError at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39) at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27) at java.lang.reflect.Constructor.newInstance(Constructor.java:513) at java.lang.Class.newInstance0(Class.java:355) at java.lang.Class.newInstance(Class.java:308) at org.mvel2.optimizers.OptimizerFactory.getAccessorCompiler(OptimizerFactory.java:78) at org.mvel2.optimizers.dynamic.DynamicOptimizer.<init>(DynamicOptimizer.java:33) at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39) at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27) at java.lang.reflect.Constructor.newInstance(Constructor.java:513) at java.lang.Class.newInstance0(Class.java:355) at java.lang.Class.newInstance(Class.java:308) at org.mvel2.optimizers.OptimizerFactory.getDefaultAccessorCompiler(OptimizerFactory.java:69) at org.mvel2.ast.ASTNode.getReducedValueAccelerated(ASTNode.java:126) at org.mvel2.compiler.ExecutableAccessor.getValue(ExecutableAccessor.java:41) at org.mvel2.MVEL.executeExpression(MVEL.java:1039) at org.jboss.errai.bus.server.io.JSONEncoder.encodeObject(JSONEncoder.java:118) at org.jboss.errai.bus.server.io.JSONEncoder._encode(JSONEncoder.java:73) at org.jboss.errai.bus.server.io.JSONEncoder.encodeCollection(JSONEncoder.java:153) at org.jboss.errai.bus.server.io.JSONEncoder._encode(JSONEncoder.java:54) at org.jboss.errai.bus.server.io.JSONEncoder.encodeObject(JSONEncoder.java:119) at org.jboss.errai.bus.server.io.JSONEncoder._encode(JSONEncoder.java:73) at org.jboss.errai.bus.server.io.JSONEncoder.encodeCollection(JSONEncoder.java:153) at org.jboss.errai.bus.server.io.JSONEncoder._encode(JSONEncoder.java:54) at org.jboss.errai.bus.server.io.JSONEncoder.encodeObject(JSONEncoder.java:119) at org.jboss.errai.bus.server.io.JSONEncoder._encode(JSONEncoder.java:73) at org.jboss.errai.bus.server.io.JSONEncoder.encodeCollection(JSONEncoder.java:153) at org.jboss.errai.bus.server.io.JSONEncoder._encode(JSONEncoder.java:54) at org.jboss.errai.bus.server.io.JSONEncoder.encodeObject(JSONEncoder.java:119) at org.jboss.errai.bus.server.io.JSONEncoder._encode(JSONEncoder.java:73) at org.jboss.errai.bus.server.io.JSONEncoder.encodeCollection(JSONEncoder.java:153) at org.jboss.errai.bus.server.io.JSONEncoder._encode(JSONEncoder.java:54) at org.jboss.errai.bus.server.io.JSONEncoder.encodeObject(JSONEncoder.java:119) at org.jboss.errai.bus.server.io.JSONEncoder._encode(JSONEncoder.java:73) at org.jboss.errai.bus.server.io.JSONEncoder.encodeCollection(JSONEncoder.java:153) at org.jboss.errai.bus.server.io.JSONEncoder._encode(JSONEncoder.java:54) at org.jboss.errai.bus.server.io.JSONEncoder.encodeObject(JSONEncoder.java:119) at org.jboss.errai.bus.server.io.JSONEncoder._encode(JSONEncoder.java:73) at org.jboss.errai.bus.server.io.JSONEncoder.encodeCollection(JSONEncoder.java:153) at org.jboss.errai.bus.server.io.JSONEncoder._encode(JSONEncoder.java:54) at org.jboss.errai.bus.server.io.JSONEncoder.encodeObject(JSONEncoder.java:119) at org.jboss.errai.bus.server.io.JSONEncoder._encode(JSONEncoder.java:73) at org.jboss.errai.bus.server.io.JSONEncoder.encodeCollection(JSONEncoder.java:153) at org.jboss.errai.bus.server.io.JSONEncoder._encode(JSONEncoder.java:54) at org.jboss.errai.bus.server.io.JSONEncoder.encodeObject(JSONEncoder.java:119) at org.jboss.errai.bus.server.io.JSONEncoder._encode(JSONEncoder.java:73) at org.jboss.errai.bus.server.io.JSONEncoder.encodeCollection(JSONEncoder.java:153) at org.jboss.errai.bus.server.io.JSONEncoder._encode(JSONEncoder.java:54) at org.jboss.errai.bus.server.io.JSONEncoder.encodeObject(JSONEncoder.java:119) at org.jboss.errai.bus.server.io.JSONEncoder._encode(JSONEncoder.java:73)
Test case :
@Test public void testEncodeDecode() { User user = new User(); user.setLogin("admin"); user.setPassword("admin"); Group group = new Group(); group.setParent(null); group.setName("Test group"); group.setDescription("Test group description"); Set<User> users = new HashSet<User>(); users.add(user); group.setUsers(users); group.setParent(null); group.setSuperGroup(true); Set<Group> groups = new HashSet<Group>(); groups.add(group); user.setGroups(groups); JSONEncoder.encode(user); System.out.println(JSONEncoder.encode(user)); }
User.class and Group.class :
import java.io.Serializable; import java.util.Set; import javax.persistence.CascadeType; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.JoinTable; import javax.persistence.ManyToMany; import javax.validation.Valid; import javax.validation.constraints.NotNull; import org.jboss.errai.bus.server.annotations.ExposeEntity; @Entity @ExposeEntity public class User implements Serializable { private static final long serialVersionUID = 5861196132584179497L; @Id private Long id; private boolean active = true; private String login; private String password; @ManyToMany(cascade={CascadeType.REFRESH}) @JoinTable(name="USERS_GROUPS", joinColumns=@JoinColumn(name="USER_ID"), inverseJoinColumns=@JoinColumn(name="GROUP_ID")) @NotNull @Valid private Set<Group> groups; public User() { super(); } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public boolean isActive() { return active; } public void setActive(boolean active) { this.active = active; } public String getLogin() { return login; } public void setLogin(String login) { this.login = login; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public Set<Group> getGroups() { return groups; } public void setGroups(Set<Group> groups) { this.groups = groups; } } import java.io.Serializable; import java.util.Set; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToMany; import javax.persistence.ManyToOne; import javax.persistence.OneToMany; import org.jboss.errai.bus.server.annotations.ExposeEntity; @Entity @ExposeEntity public class Group implements Serializable { private static final long serialVersionUID = 1723678191345674847L; @Id private Long id; private String name; private String description; @OneToMany(mappedBy="parent", fetch = FetchType.LAZY, cascade={CascadeType.REFRESH,CascadeType.REMOVE}) private Set<Group> groups; @ManyToOne @JoinColumn(name="PARENT_ID", nullable=true) private Group parent; @ManyToMany(mappedBy="groups",fetch=FetchType.LAZY, cascade={CascadeType.REFRESH}) private Set<User> users; /** * Is super group? */ @Column(name="SUPER_GROUP", nullable=false) private boolean superGroup = false; public Group() { super(); } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public Set<Group> getGroups() { return groups; } public void setGroups(Set<Group> groups) { this.groups = groups; } public Group getParent() { return parent; } public void setParent(Group parent) { this.parent = parent; } public Set<User> getUsers() { return users; } public void setUsers(Set<User> users) { this.users = users; } public boolean isSuperGroup() { return superGroup; } public void setSuperGroup(boolean superGroup) { this.superGroup = superGroup; } }
Of course standard GWT RPC serialization was working fine with those classes. Is it possible to serialize those classes ?
Thanks in advance.
Marcin