Hi!
Since a few days i have a really strange problem with our production system based on hibernate:
I sometime get a null value for one property (property "name") of an object (Class "Role") [other properties e.g. the "id" are not null when the name is null]. The property is defined as NOT NULL in the database. The fields in this table are also filled with values. Nowhere in the code a Role object is created with a null name.
My code always relied on the fact, that this property never can't be null, so
i built the hashCode Function on the String hashCode of Role.name
The role objects usually are in Sets associated with a User, a Workgroup or a RoleSet. Users/Workgroups/RoleSets are mapped to roles through many-to-many mappings.
Most times when the Sets with the roles are accessed this is done through the "Set. contains(Object o)". I create a Role with the same name then and ask the contains(..) if the e.g. User has this role.
(The roles are used for security in a tomcat based application; i use SecurityFilter [http://securityfilter.sourceforge.net/] for doing this. The only method that checks the roles in each request:
Code:
public boolean isUserInRole(String username, String rolename) {
if (username == null || rolename == null) {
return false;
}
// Vergleichsrolle
Role reqRole = new Role(rolename);
// User-Objekt aus DB holen
User u = UserDAO.getByUsername(username);
if (u == null) {
return false;
}
// Rollen des Benutzer
if (u.getRoles().contains(reqRole))
return true;
// Rolesets des Benutzers
Iterator roleSetIterator = u.getRoleSets().iterator();
while (roleSetIterator.hasNext()) {
RoleSet rs = (RoleSet) (roleSetIterator.next());
if (rs.getRoles().contains(reqRole))
return true;
}
// Rollen der Arbeitsgruppe
Workgroup w = u.getWorkgroup();
if (w.getRoles().contains(reqRole))
return true;
// Rolesets der Gruppe
roleSetIterator = w.getRoleSets().iterator();
while (roleSetIterator.hasNext()) {
RoleSet rs = (RoleSet) (roleSetIterator.next());
if (rs.getRoles().contains(reqRole))
return true;
}
return false;
}
)
First we got NullPointerException sometimes for the hashCode method, because of the actually impossible null values in the "name" (i for myself only saw them in the logs of the production system, and never got one, but many users said, that they got internal server errors [the log shows that this matches]), but this made the system unusable, so i tried to write a workaround for these NullPointerExceptions and now i check the "name" property if it's "null" (what should be impossible).
A big problem is, that i can't reproduce this problem. I see many debug messages (and my artificially created stack traces) in the logs, but i tried for hours to see the problem live. The users also say, that they don't get errors, since i catched the NullPointerException. Often for hours i don't get
any debug messages from the hashCode method.
Further i get this hashCode debug messages on a second production server, where the role objects aren't used directly (for the security check) and are only loaded while the User is loaded. Much more seldom, but i also the the debug messages from the Role.hashCode().
The Classes weren't changed since 4 month and worked without problems till monday. We changed from Hibernate 3.1.2 to 3.1.3 one and a half month ago, but after this version change it also worked until this unlucky monday. The runtime or database on the servers also wasn't changed for a few weeks.
I searched the web for a couple of hours, but didn't find anything useful.
What could I do or try? Any ideas? Could this be a Hibernate bug?
Best regards,
Stefan Kiendl
Hibernate version:3.1.3
Using EHCache
Running on BEA JRockit 1.5.0_04 on Debian Sarge
I updated the following packages of the Hibernate required libs cause of dependencies of other packages:
- commons-collections to v3.1
- c3p0 to v0.9.0.4
-
Mapping documents: Role:
Code:
package de.kiendl_solutions.laborax.model;
import de.kiendl_solutions.util.error.StackTraceUtil;
/**
* @hibernate.class table="laborax_role" dynamic-update="true"
* mutable="false" lazy="false"
* @hibernate.cache usage="read-only"
*/
public class Role implements Comparable, java.io.Serializable {
private static final long serialVersionUID = 1L;
private String description;
private Long id;
/**
* @directed true
*/
private Module module;
private String name;
private int version;
/**
* Hibernate Standardkonstruktor
*/
Role() {
}
/**
* Konstruktor, um Vergleichsobjekt zu erzeugen
*
* @param rolename
* Rolename
*/
public Role(String rolename) {
this.name = rolename;
}
/*
* (non-Javadoc)
*
* @see java.lang.Comparable#compareTo(java.lang.Object)
*/
public int compareTo(Object obj) {
if (obj != null && obj instanceof Role) {
if (this.getName() != null && ((Role) obj).getName() != null)
return this.getName().compareTo(((Role) obj).getName());
}
return 0;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#equals(java.lang.Object)
*/
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!(obj instanceof Role))
return false;
if (obj == null)
return false;
final Role castedObj = (Role) obj;
if (castedObj.getId() != null && this.getId() != null && castedObj.getId().equals(this.getId()))
return true;
if (this.getName() != null && castedObj.getName() != null && this.getName().equals(castedObj.getName()))
return true;
return false;
}
/**
* @return Returns the description.
*
* @hibernate.property length="10000" access="field"
*/
public String getDescription() {
return description;
}
/**
* @return Returns the id.
*
* @hibernate.id generator-class="native" access="field"
*/
public Long getId() {
return id;
}
/**
* @return Returns the module.
*
* @hibernate.many-to-one class="de.kiendl_solutions.laborax.model.Module"
* not-null="true" column="module_id" access="field"
* foreign-key="FK_role_module"
*/
public Module getModule() {
return module;
}
/**
* @return Returns the name.
*
* @hibernate.property length="100" not-null="true" unique="true" access="field"
*/
public String getName() {
return name;
}
/**
* Version dieses Objekts
*
* @hibernate.version access="field"
*/
public int getVersion() {
return version;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#hashCode()
*/
/**
* Wird für eindeutigen Schlüssel in den Sets verwendet. Set.contains(Object
* o) setzt ebenfalls darauf auf
*/
public int hashCode() {
if (name != null) {
return name.hashCode();
} else {
StackTraceUtil.displayStackTraceInformation(new Throwable());
System.out.println(new java.util.Date() + " -- INFO: Name of Role [ID: " + this.getId() + "] is empty");
return super.hashCode();
}
}
}
Through XDoclet generated mapping of "Role":
Code:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class dynamic-update="true" mutable="false" table="laborax_role" lazy="false" name="de.kiendl_solutions.laborax.model.Role">
<cache usage="read-only"/>
<id access="field" name="id">
<generator class="native"/>
</id>
<version unsaved-value="undefined" access="field" name="version"/>
<property name="description" length="10000" access="field"/>
<many-to-one not-null="true" column="module_id" foreign-key="FK_role_module" access="field" name="module" class="de.kiendl_solutions.laborax.model.Module"/>
<property name="name" not-null="true" length="100" access="field" unique="true"/>
</class>
</hibernate-mapping>
The following classes directly use on or more Roles:
User:
Code:
/**
* @hibernate.class table="laborax_user" dynamic-update="true" lazy="false"
* @hibernate.cache usage="read-write"
*/
public class User implements Comparable, java.io.Serializable, IVariableTransformer {
private static final long serialVersionUID = 1L;
private boolean active;
private String email;
private String firstname;
private Long id;
/**
* @associates <{de.kiendl_solutions.laborax.model.Note}>
* @directed directed
* @supplierCardinality 0..*
*/
private java.util.Set notices = null;
private String password;
private String position;
/**
* @link aggregation <{de.kiendl_solutions.laborax.model.Role}>
* @directed directed
* @supplierCardinality 0..*
*/
private java.util.Set roles = new java.util.HashSet();
/**
* @directed directed
* @link aggregation <{de.kiendl_solutions.laborax.model.RoleSet}>
* @supplierCardinality 0..*
*/
private java.util.Set roleSets = new java.util.HashSet();
private String shortCode;
private String surname;
private String title;
private String username;
private int version;
/**
* @clientCardinality 0..*
* @directed true
* @supplierCardinality 1
*/
private Workgroup workgroup;
/**
* Hibernate Standardkonstruktor
*
*/
User() {
}
public User(String username, String password, Workgroup workgroup, String firstname, String surname,
String shortCode, String email, boolean active) {
this.username = username;
this.password = password;
this.workgroup = workgroup;
this.firstname = firstname;
this.surname = surname;
this.shortCode = shortCode;
this.email = email;
this.active = active;
}
public void addRole(Role role) {
if (role == null)
throw new NullPointerException("Can't add a null Role to an User");
roles.add(role);
}
public void addRoleSet(RoleSet rs) {
if (rs == null)
throw new NullPointerException("Can't add a null RoleSet to an User");
roleSets.add(rs);
}
/*
* (non-Javadoc)
*
* @see java.lang.Comparable#compareTo(java.lang.Object)
*/
public int compareTo(Object obj) {
if (obj instanceof User) {
User u = (User) obj;
String s1 = this.getSurname() + this.getFirstname();
String s2 = u.getSurname() + u.getFirstname();
return s1.compareTo(s2);
}
return 0;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#equals(java.lang.Object)
*/
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!(obj instanceof User))
return false;
if (obj == null)
return false;
final User castedObj = (User) obj;
if (castedObj.getId() != null && castedObj.getId().equals(this.getId()))
return true;
return false;
}
/**
*
* @return Vollen Namen mit Titel in Form "Titel Vorname Nachname"
*/
public String getDisplayName() {
StringBuffer sb = new StringBuffer();
if (this.getTitle() != null && !this.getTitle().equals("")) {
sb.append(this.getTitle());
sb.append(" ");
}
sb.append(this.getFirstname());
sb.append(" ");
sb.append(this.getSurname());
return sb.toString();
}
/**
* @return Returns the email.
*
* @hibernate.property length="100" not-null="true"
*/
public String getEmail() {
return email;
}
/**
* @return Returns the firstname.
*
* @hibernate.property length="50" not-null="true"
*/
public String getFirstname() {
return firstname;
}
/**
* @return Returns the id.
*
* @hibernate.id generator-class="native"
*/
public Long getId() {
return id;
}
/**
* @return Returns the notices.
*
* @hibernate.set cascade="none" lazy="true" inverse="true"
* @hibernate.key column="userEdit_id"
* @hibernate.one-to-many class="de.kiendl_solutions.laborax.model.Note"
*/
public java.util.Set getNotices() {
return notices;
}
/**
* @return Returns the password.
*
* @hibernate.property length="30" not-null="true"
*/
public String getPassword() {
return password;
}
/**
* @return Returns the position.
*
* @hibernate.property length="30"
*/
public String getPosition() {
return position;
}
/**
* @return Returns the roles.
*
* @hibernate.set table="laborax_user_role" cascade="none" fetch="join"
* @hibernate.key column="user_id"
* @hibernate.many-to-many column="role_id"
* class="de.kiendl_solutions.laborax.model.Role"
*/
public java.util.Set getRoles() {
return roles;
}
/**
* @return Returns the roleSets.
*
* @hibernate.set table="laborax_user_roleset" cascade="none" fetch="join"
* @hibernate.key column="user_id"
* @hibernate.many-to-many column="roleset_id"
* class="de.kiendl_solutions.laborax.model.RoleSet"
*/
public java.util.Set getRoleSets() {
return roleSets;
}
/**
* @return Returns the shortCode.
*
* @hibernate.property length="5" not-null="true"
*/
public String getShortCode() {
return shortCode;
}
/**
* @return Returns the surname.
*
* @hibernate.property length="50" not-null="true"
*/
public String getSurname() {
return surname;
}
/**
* @return Returns the title.
*
* @hibernate.property length="20"
*/
public String getTitle() {
return title;
}
/**
* @return Returns the username.
*
* @hibernate.property length="30" not-null="true" unique="true"
* column="username"
*/
public String getUsername() {
return username;
}
/**
* Version dieses Objekts
*
* @hibernate.version access="field"
*/
public int getVersion() {
return version;
}
/**
* @return Returns the workgroup.
*
* @hibernate.many-to-one class="de.kiendl_solutions.laborax.model.Workgroup"
* not-null="true" column="workgroup_id"
* foreign-key="FK_user_workgroup" fetch="join"
*/
public Workgroup getWorkgroup() {
return workgroup;
}
/**
* @return Returns the active.
*
* @hibernate.property not-null="true"
*/
public boolean isActive() {
return active;
}
public void removeRole(Role role) {
if (role == null)
throw new NullPointerException("Can't remove a null Role from an User");
roles.remove(role);
}
public void removeRoleSet(RoleSet rs) {
if (rs == null)
throw new NullPointerException("Can't remove a null RoleSet from an User");
roleSets.remove(rs);
}
/**
* @param active
* The active to set.
*/
public void setActive(boolean active) {
this.active = active;
}
/**
* @param email
* The email to set.
*/
public void setEmail(String property1) {
this.email = property1;
}
/**
* @param firstname
* The firstname to set.
*/
public void setFirstname(String property1) {
this.firstname = property1;
}
/**
* @param id
* The id to set.
*/
public void setId(Long id) {
this.id = id;
}
/**
* @param notices
* The notices to set.
*/
public void setNotices(java.util.Set messages) {
this.notices = messages;
}
/**
* @param password
* The password to set.
*/
public void setPassword(String property1) {
this.password = property1;
}
/**
* @param position
* The position to set.
*/
public void setPosition(String property1) {
this.position = property1;
}
/**
* @param roles
* The roles to set.
*/
public void setRoles(java.util.Set roles) {
this.roles = roles;
}
/**
* @param roleSets
* The roleSets to set.
*/
public void setRoleSets(java.util.Set rolesets) {
this.roleSets = rolesets;
}
/**
* @param shortCode
* The shortCode to set.
*/
public void setShortCode(String property1) {
this.shortCode = property1;
}
/**
* @param surname
* The surname to set.
*/
public void setSurname(String property1) {
this.surname = property1;
}
/**
* @param title
* The title to set.
*/
public void setTitle(String property1) {
this.title = property1;
}
/**
* @param username
* The username to set.
*/
public void setUsername(String property1) {
this.username = property1;
}
/**
* @param workgroup
* The workgroup to set.
*/
public void setWorkgroup(Workgroup workgroup) {
this.workgroup = workgroup;
}
}
Through XDoclet generated mapping of "User":
Code:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class dynamic-update="true" table="laborax_user" lazy="false" name="de.kiendl_solutions.laborax.model.User">
<cache usage="read-write"/>
<id name="id">
<generator class="native"/>
</id>
<version unsaved-value="undefined" access="field" name="version"/>
<property name="email" not-null="true" length="100"/>
<property name="firstname" not-null="true" length="50"/>
<set lazy="true" inverse="true" cascade="none" name="notices">
<key column="userEdit_id"/>
<one-to-many class="de.kiendl_solutions.laborax.model.Note"/>
</set>
<property name="password" not-null="true" length="30"/>
<property name="position" length="30"/>
<set fetch="join" table="laborax_user_role" cascade="none" name="roles">
<key column="user_id"/>
<many-to-many column="role_id" class="de.kiendl_solutions.laborax.model.Role"/>
</set>
<set fetch="join" table="laborax_user_roleset" cascade="none" name="roleSets">
<key column="user_id"/>
<many-to-many column="roleset_id" class="de.kiendl_solutions.laborax.model.RoleSet"/>
</set>
<property name="shortCode" not-null="true" length="5"/>
<property name="surname" not-null="true" length="50"/>
<property name="title" length="20"/>
<property name="username" not-null="true" length="30" column="username" unique="true"/>
<many-to-one fetch="join" not-null="true" column="workgroup_id" foreign-key="FK_user_workgroup" name="workgroup" class="de.kiendl_solutions.laborax.model.Workgroup"/>
<property name="active" not-null="true"/>
</class>
</hibernate-mapping>
Workgroup:
Code:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class dynamic-update="true" table="laborax_workgroup" lazy="false" name="de.kiendl_solutions.laborax.model.Workgroup">
<cache usage="read-write"/>
<id name="id">
<generator class="native"/>
</id>
<version unsaved-value="undefined" access="field" name="version"/>
<property name="email" length="100"/>
<property name="name" not-null="true" length="50" unique="true"/>
<set lazy="true" inverse="true" cascade="none" name="notices">
<key column="workgroup_id"/>
<one-to-many class="de.kiendl_solutions.laborax.model.Note"/>
</set>
<set fetch="join" table="laborax_workgroup_role" name="roles">
<key column="workgroup_id"/>
<many-to-many column="role_id" class="de.kiendl_solutions.laborax.model.Role"/>
</set>
<set fetch="join" table="laborax_workgroup_roleset" name="roleSets">
<key column="workgroup_id"/>
<many-to-many column="roleset_id" class="de.kiendl_solutions.laborax.model.RoleSet"/>
</set>
<property name="active" not-null="true"/>
</class>
</hibernate-mapping>
RoleSet:
Code:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class dynamic-update="true" table="laborax_roleset" lazy="false" name="de.kiendl_solutions.laborax.model.RoleSet">
<cache usage="read-write"/>
<id name="id">
<generator class="native"/>
</id>
<version unsaved-value="undefined" access="field" name="version"/>
<property name="name" not-null="true" length="50" unique="true"/>
<set fetch="join" table="laborax_roleset_role" cascade="save-update" name="roles">
<key column="roleset_id"/>
<many-to-many column="role_id" class="de.kiendl_solutions.laborax.model.Role"/>
</set>
</class>
</hibernate-mapping>
Code between sessionFactory.openSession() and session.close():Impossible to say, because I don't know where to look for.
Full stack trace of any exception that occurs:I catch the fault and print the StackTrace with a custom method to see, how the code came there.
Method:
Code:
public static boolean displayStackTraceInformation(Throwable ex) {
if (ex == null) {
System.out.println("No valid StackTrace");
return false;
}
// StackTrace ausgeben
System.out.println("Output StackTrace");
ex.printStackTrace();
return true;
}
StackTrace:
Code:
java.lang.Throwable
at de.kiendl_solutions.laborax.model.Role.hashCode()I(Unknown Source)
at java.util.HashMap.hash(Ljava.lang.Object;)I(Unknown Source)
at java.util.HashMap.put(Ljava.lang.Object;Ljava.lang.Object;)Ljava.lang.Object;(Unknown Source)
at java.util.HashSet.add(HashSet.java:194)
at java.util.AbstractCollection.addAll(AbstractCollection.java:318)
at org.hibernate.collection.PersistentSet.endRead(PersistentSet.java:273)
at org.hibernate.engine.CollectionLoadContext.endLoadingCollection(CollectionLoadContext.java:183)
at org.hibernate.engine.CollectionLoadContext.endLoadingCollections(CollectionLoadContext.java:268)
at org.hibernate.engine.CollectionLoadContext.endLoadingCollections(CollectionLoadContext.java:249)
at org.hibernate.loader.Loader.endCollectionLoad(Loader.java:866)
at org.hibernate.loader.Loader.initializeEntitiesAndCollections(Loader.java:853)
at org.hibernate.loader.Loader.doQuery(Loader.java:717)
at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:230)
at org.hibernate.loader.Loader.loadEntity(Loader.java:1785)
at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:47)
at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:41)
at org.hibernate.loader.entity.BatchingEntityLoader.load(BatchingEntityLoader.java:82)
at org.hibernate.persister.entity.AbstractEntityPersister.load(AbstractEntityPersister.java:2730)
at org.hibernate.event.def.DefaultLoadEventListener.loadFromDatasource(DefaultLoadEventListener.java:365)
at org.hibernate.event.def.DefaultLoadEventListener.doLoad(DefaultLoadEventListener.java:304)
at org.hibernate.event.def.DefaultLoadEventListener.load(DefaultLoadEventListener.java:123)
at org.hibernate.event.def.DefaultLoadEventListener.proxyOrLoad(DefaultLoadEventListener.java:177)
at org.hibernate.event.def.DefaultLoadEventListener.onLoad(DefaultLoadEventListener.java:76)
at org.hibernate.impl.SessionImpl.fireLoad(SessionImpl.java:862)
at org.hibernate.impl.SessionImpl.internalLoad(SessionImpl.java:829)
at org.hibernate.type.EntityType.resolveIdentifier(EntityType.java:266)
at org.hibernate.type.EntityType.resolve(EntityType.java:306)
at org.hibernate.engine.TwoPhaseLoad.initializeEntity(TwoPhaseLoad.java:116)
at org.hibernate.loader.Loader.initializeEntitiesAndCollections(Loader.java:842)
at org.hibernate.loader.Loader.doQuery(Loader.java:717)
at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:230)
at org.hibernate.loader.Loader.loadEntity(Loader.java:1785)
at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:47)
at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:41)
at org.hibernate.loader.entity.BatchingEntityLoader.load(BatchingEntityLoader.java:82)
at org.hibernate.persister.entity.AbstractEntityPersister.load(AbstractEntityPersister.java:2730)
at org.hibernate.event.def.DefaultLoadEventListener.loadFromDatasource(DefaultLoadEventListener.java:365)
at org.hibernate.event.def.DefaultLoadEventListener.doLoad(DefaultLoadEventListener.java:304)
at org.hibernate.event.def.DefaultLoadEventListener.load(DefaultLoadEventListener.java:123)
at org.hibernate.event.def.DefaultLoadEventListener.proxyOrLoad(DefaultLoadEventListener.java:177)
at org.hibernate.event.def.DefaultLoadEventListener.onLoad(DefaultLoadEventListener.java:76)
at org.hibernate.impl.SessionImpl.fireLoad(SessionImpl.java:862)
Name and version of the database you are using: 5.0.19-Debian_1.dotdeb.1-log
With JDBC Driver: mysql-connector-java-3.1.12
The generated SQL (show_sql=true):The production system would generate to much sql, so i can't turn this on. On my both testing systems i can't reproduce the failure.
Debug level Hibernate log excerpt:The production system would generate to much output, so i can't turn this on. On my both testing systems i can't reproduce the failure.
Code: