0

I have to put two tomcat servers (tcat01 and tcat02) in a loadbalancing architecture.

I'm using tomcat 6.x and I edited the conf/server.xml like this on tomcat tcat01 :

<Engine name="Catalina" defaultHost="localhost" jvmRoute="tcat01">
     <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster" channelSendOptions="6"/>
      ....

On tcat02 conf/server.xml is like this :

 <Engine name="Catalina" defaultHost="localhost" jvmRoute="tcat02">
     <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster" channelSendOptions="6"/>
      ....

I started tcat01, and then tcat02, il catalina.out it seems that tcat01 communicates well with tcat02.

Then I connected to the webapp with internet navigator, and then each time I do something in the webapp (I mean when I navigate) there is this exception :

  Nov 24, 2011 12:00:13 AM org.apache.catalina.core.ContainerBase backgroundProcess
WARNING: Exception processing manager org.apache.catalina.ha.session.DeltaManager@278c4835 background process
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.mycompany.myproject.model.authentification.Authority.groups, no session or session was closed
    at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:383)
    at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationExceptionIfNotConnected(AbstractPersistentCollection.java:375)
    at org.hibernate.collection.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:368)
    at org.hibernate.collection.AbstractPersistentCollection.read(AbstractPersistentCollection.java:111)
    at org.hibernate.collection.PersistentSet.toString(PersistentSet.java:332)
    at java.io.ObjectOutputStream.writeOrdinaryObject(Unknown Source)
    at java.io.ObjectOutputStream.writeObject0(Unknown Source)
    at java.io.ObjectOutputStream.defaultWriteFields(Unknown Source)
    at java.io.ObjectOutputStream.writeSerialData(Unknown Source)
    at java.io.ObjectOutputStream.writeOrdinaryObject(Unknown Source)

And here is the code of the class which cannot be sezialized (ie the java bean mentioned in the stack trace) :

import static javax.persistence.GenerationType.IDENTITY;

import java.util.HashSet;
import java.util.Set;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.PrePersist;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;

@Entity
@Table(name = "authority", uniqueConstraints = @UniqueConstraint(columnNames = "name"))
public class Authority implements java.io.Serializable {

/**
 * 
 */
private static final long serialVersionUID = -7436593455923433675L;

//SYSTEM ROLE CONSTANT
public static final String MANAGER_SYSTEM_ROLE = "ROLE_MANAGER";
public static final String CLIENT_SYSTEM_ROLE = "ROLE_CLIENT";
public static final String PEDAGO_ADMIN_SYSTEM_ROLE = "ROLE_PEDAGO_ADMIN";

private Integer id;
@Size(min=1)
@Pattern(regexp="^ROLE[_a-zA-Z]+$", message="{authority.name.pattern.error}")
private String name;
@Size(max=65535)
private String description;


private Boolean isSystem;

private Set<Group> groups = new HashSet<Group>(0);

public Authority() {
    this.isSystem = false;
}

public Authority(String name) {
    this.name = name;
    this.isSystem = false;
}

public Authority(String name, String description, Set<Group> groups) {
    this.name = name;
    this.description = description;
    this.groups = groups;
    this.isSystem = false;
}

@Id
@GeneratedValue(strategy = IDENTITY)
@Column(name = "id", unique = true, nullable = false)
public Integer getId() {
    return this.id;
}

public void setId(Integer id) {
    this.id = id;
}

@Column(name = "name", unique = true, nullable = false, length = 45)
public String getName() {
    return this.name;
}

public void setName(String name) {
    this.name = name;
}

@Column(name = "description")
public String getDescription() {
    return this.description;
}

public void setDescription(String description) {
    this.description = description;
}

public void setIsSystem(Boolean isSystem) {
    this.isSystem = isSystem;
}

@Column(name = "system")
public Boolean getIsSystem() {
    return isSystem;
}

@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(name = "group_authorities", joinColumns = { @JoinColumn(name = "authority_id", nullable = false, updatable = true) }, inverseJoinColumns = { @JoinColumn(name = "group_id", nullable = false, updatable = true) })
public Set<Group> getGroups() {
    return this.groups;
}

public void setGroups(Set<Group> groups) {
    this.groups = groups;
}

@PrePersist
protected void updateSystemField() {
    if(isSystem == null)
        isSystem = false;
}
}

And here is the code of the java bean Group (cause we have a lazily initialize exception on a collection of Groups) :

import static javax.persistence.GenerationType.IDENTITY;

import java.util.HashSet;
import java.util.Set;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.PrePersist;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;


@Entity
@Table(name = "groups", uniqueConstraints = @UniqueConstraint(columnNames = "name"))
public class Group implements java.io.Serializable {

/**
 * 
 */
private static final long serialVersionUID = 7380068327050752558L;

private Integer id;
@NotNull
@Size(min=1)
private String name;
private String description;

private Boolean isSystem;

private Set<Authority> authorities = new HashSet<Authority>(0);
private Set<User> users = new HashSet<User>(0);

public Group() {
    this.isSystem = false;
}

public Group(String name) {
    this.name = name;
    this.isSystem = false;
}

public Group(String name, String description, Set<Authority> authorities, Set<User> users) {
    this.name = name;
    this.description = description;
    this.isSystem = false;
    this.authorities = authorities;
    this.users = users;
}

@Id
@GeneratedValue(strategy = IDENTITY)
@Column(name = "id", unique = true, nullable = false)
public Integer getId() {
    return this.id;
}

public void setId(Integer id) {
    this.id = id;
}

@Column(name = "name", unique = true, nullable = false, length = 45)
public String getName() {
    return this.name;
}

public void setName(String name) {
    this.name = name;
}

@Column(name = "description")
public String getDescription() {
    return this.description;
}

public void setDescription(String description) {
    this.description = description;
}

public void setIsSystem(Boolean isSystem) {
    this.isSystem = isSystem;
}

@Column(name = "system")
public Boolean getIsSystem() {
    return isSystem;
}

@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(name = "group_authorities", joinColumns = { @JoinColumn(name = "group_id", nullable = false, updatable = true) }, inverseJoinColumns = { @JoinColumn(name = "authority_id", nullable = false, updatable = true) })
public Set<Authority> getAuthorities() {
    return this.authorities;
}

public void setAuthorities(Set<Authority> authorities) {
    this.authorities = authorities;
}

@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(name = "group_users", joinColumns = { @JoinColumn(name = "group_id", nullable = false, updatable = true) }, inverseJoinColumns = { @JoinColumn(name = "user_id", nullable = false, updatable = true) })
public Set<User> getUsers() {
    return this.users;
}

public void setUsers(Set<User> users) {
    this.users = users;
}

public void addAuthority(Authority authority) {
    if(authorities == null)
        authorities = new HashSet<Authority>(0);
    authorities.add(authority);
}

public boolean removeAuthority(Authority authority) {
    return authorities == null || authorities.remove(authority);
}

public void addUser(User user) {
    if(users == null)
        users = new HashSet<User>(0);
    users.add(user);
}

public boolean removeUser(User user) {
    return users == null || users.remove(user);
}

@PrePersist
protected void updateSystemField() {
    if(isSystem == null)
        isSystem = false;
}
}

Thanks for your help

BoltClock
  • 700,868
  • 160
  • 1,392
  • 1,356
Nico
  • 3,430
  • 4
  • 20
  • 27
  • looks like tomcat is trying to serialize a session and there are attributes in session that cannot be serialized, I would suggest you to look at the objects that are being added to session and look if they can be serialized. – Prasanna Talakanti Nov 10 '11 at 14:03
  • You're right, I found on google the option "-Dsun.io.serialization.extendedDebugInfo=true" for JVM. When I added this option I saw which objects couldn't be serialized. I don't know if I can change the detail of my question in stackoverflow, if I can't I will create another question. About which objets can't be serialized : java bean with lazy collection, in fact I have a lazy exception ! My lazy collections can't be serialized and I don't know how to solve this problem. – Nico Nov 28 '11 at 10:39
  • can you post the code for the object that you can't serialize, As long as the object implements the interface java.io.Serializable it will be serializable. – Prasanna Talakanti Nov 28 '11 at 14:33
  • ok I did it, and I edited my original post in order to put a better stack trace (cause I activated the JVM parameter sun.io.serialization.extendedDebugInfo=true) – Nico Nov 30 '11 at 17:13
  • I think @Chris is right, If you don't want do the mapping change then look at Hibernate.initialize after loading the Authority do the initialize. http://www.javalobby.org/java/forums/t62077.html – Prasanna Talakanti Nov 30 '11 at 19:25

1 Answers1

0

You're getting the lazy instantiation exception since you have authority groups mapped as lazy. By the time the session is replicated, you're outside of an active session so it can't be hydrated. To remedy this, you have a few options:

  1. Change your mapping so the association is fetched eagerly
  2. Change your DAO to step into that collection (or use hibernate.initialize) to force the lazy load to occur before the session is closed
  3. Detach the object from the session and then explicitly set the groups to null on the object before it is put into session to replace the hibernate proxy that is there.

For this particuar case, options 1 or 2 are probably the cleanest.

Chris
  • 22,923
  • 4
  • 56
  • 50