I use this code:
JSF:
<p:treeTable id="treeSkill" value="#{skillManager.rootSkill}"
var="skill" selectionMode="single" widgetVar="skillsTreeTable"
style="border: 0;">
<p:ajax event="expand"
listener="#{skillManager.expandNodeListener}" />
<p:column> ..... </p:column>
<p/treeTable>
SkillManager:
@Named
@SessionScoped
public class SkillManager implements Serializable {
private static final long serialVersionUID = 1L;
private TreeNode rootSkill;
public SkillManager() {
initSkillTree();
}
public void expandNodeListener(NodeExpandEvent nee) {
TreeNode treeNode = nee.getTreeNode();
if (treeNode instanceof FetchChildren)
((FetchChildren) treeNode).fetchChildren();
if (treeNode instanceof LazySkillTreeNode)
((LazySkillTreeNode) treeNode).fetchSubchildren();
}
private void initSkillTree() {
rootSkill = new DefaultTreeNode("Root", null);
Skill realRootSkill = HrDaoFactory.getInstance().getSkillDAO().getRootSkill();
TreeNode realRootNode = new LazySkillTreeNode(realRootSkill, rootSkill);
for (Skill skill : realRootSkill.getChildrensSkills()) {
LazySkillTreeNode node = new LazySkillTreeNode(skill, realRootNode);
node.fetchChildren();
}
RequestContext.getCurrentInstance().update("woCatalogTabView:skillTreeForm");
}
}
LazySkillTreeNode:
public class LazySkillTreeNode extends LazyTreeNode implements FetchChildren {
private static final long serialVersionUID = 8856168173751148652L;
private boolean childrenFetched;
public LazySkillTreeNode(Object data, TreeNode parent) {
super(data, parent);
}
@Override
public void fetchChildren() {
if (childrenFetched)
return;
for (Skill skill : ((Skill) super.getData()).getChildrensSkills())
new LazySkillTreeNode(skill, this);
childrenFetched = true;
}
}
LazyTreeNode:
public abstract class LazyTreeNode extends DefaultTreeNode {
private static final long serialVersionUID = 8839307424434170537L;
private boolean subChildrenFetched;
public LazyTreeNode(Object data, TreeNode parent) {
super(data, parent);
}
public void fetchSubchildren() {
if (subChildrenFetched || isLeaf())
return;
List<TreeNode> treeNodeList = getChildren();
for (TreeNode node : treeNodeList) {
if (node instanceof FetchChildren)
((FetchChildren) node).fetchChildren();
}
subChildrenFetched = true;
}
}
Everything works fine, but if add/delete elements (after all this operations we call method initSkillTree() for rebuild tree) a lot of times, or if 2 or more users start to do it, we beginning to recieve in response from server this string:
<?xml version='1.0' encoding='UTF-8'?>
<partial-response><error><error-name>class java.lang.StackOverflowError</error-name><error-message><![CDATA[]]></error-message></error></partial-response>
Other problem that i don't have any information about error. No information in log files. In server.log nothing to.
We use: JSF (Mojarra 2.14), Primefaces 3.41, JBOSS 7.
And in the end error was in Controller class where method:
public void addOrUpdateSkill(Skill skill) {
Session session = null;
try {
session = HibernateUtil.getCurrentSession();
session.beginTransaction();
session.saveOrUpdate(skill);
session.getTransaction().commit();
evictAllSkillsFromSession();
} catch (Throwable e) {
logger.fatal(skill, e);
if (session.getTransaction() != null && session.getTransaction().isActive())
session.getTransaction().rollback();
throw new RuntimeException(e);
}
}
and stack trace was appeared in the row "logger.fatal(skill, e);" you must pass the error message by first argument instead of Entity object. Error appear because of it's toString() method implementation of Skill class:
@Entity
@Table(name = "SKILLS", schema = AppData.HR_SCHEMA)
public class Skill implements Serializable {
private static final long serialVersionUID = -2728239519286686549L;
@Id
@SequenceGenerator(name = "SKILLS_ID_GENERATOR", sequenceName = AppData.HR_SCHEMA + ".SKILLS_ID_SEQ")
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SKILLS_ID_GENERATOR")
private BigDecimal id;
@Column(name = "NAME_ENG")
private String nameEng;
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "UPDATED_AT")
private Date updatedAt;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "UPDATED_BY", referencedColumnName = "USER_ID")
private User updatedBy;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "PARENT_ID")
private Skill parentSkill;
@OneToMany(mappedBy = "parentSkill", fetch = FetchType.LAZY, orphanRemoval = true)
private List<Skill> childrensSkills;
@Column(name = "DESCRIPTION")
private String description;
@OneToMany(orphanRemoval = true, mappedBy = "skill")
private List<SkillJoinedAction> skillJoinedActions;
@OneToMany(orphanRemoval = true, mappedBy = "skill")
private List<SkillJoinedEmployee> skillJoinedEmployees;
public Skill() {
}
public Skill(String nameEng, User updateBy, String description) {
this.nameEng = nameEng;
this.updatedBy = updateBy;
this.updatedAt = new Date();
this.setDescription(description);
}
public BigDecimal getId() {
return id;
}
public void setId(BigDecimal id) {
this.id = id;
}
public String getNameEng() {
return this.nameEng;
}
public void setNameEng(String nameEng) {
this.nameEng = nameEng;
}
public Date getUpdatedAt() {
return this.updatedAt;
}
public void setUpdatedAt(Date updatedAt) {
this.updatedAt = updatedAt;
}
public User getUpdatedBy() {
return updatedBy;
}
public void setUpdatedBy(User updatedBy) {
this.updatedBy = updatedBy;
}
public List<Skill> getChildrensSkills() {
return childrensSkills;
}
public void setChildrensSkills(List<Skill> childrensSkills) {
this.childrensSkills = childrensSkills;
}
public Skill getParentSkill() {
return parentSkill;
}
public void setParentSkill(Skill parentSkill) {
this.parentSkill = parentSkill;
}
@Override
public String toString() {
return "Skill [id=" + id + ", nameEng=" + nameEng + ", updatedAt=" + updatedAt + ", updatedBy=" + updatedBy + ", parentSkill="
+ parentSkill + ", childrensSkills=" + childrensSkills + "]";
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public List<SkillJoinedAction> getSkillJoinedActions() {
return skillJoinedActions;
}
public void setSkillJoinedActions(List<SkillJoinedAction> skillJoinedActions) {
this.skillJoinedActions = skillJoinedActions;
}
public List<SkillJoinedEmployee> getSkillJoinedEmployees() {
return skillJoinedEmployees;
}
public void setSkillJoinedEmployees(List<SkillJoinedEmployee> skillJoinedEmployees) {
this.skillJoinedEmployees = skillJoinedEmployees;
}
}
as you can see in method:
@Override
public String toString() {
return "Skill [id=" + id + ", nameEng=" + nameEng + ", updatedAt=" + updatedAt + ", updatedBy=" + updatedBy + ", parentSkill="
+ parentSkill + ", childrensSkills=" + childrensSkills + "]";
}
was called method toString() on parentSkill who in his turn call toString() on childrensSkills... infinite recursion.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With