I have a problem of loading large data set to a primefaces tree.
Background
I have a large data set, one big list of hashmaps to be specific. I need to loop over every element of the list to build the tree in the PostConstruct method for primefaces to display. Just looping over the list takes around 4 minutes.
How I am dealing with this
So, I thought of dividing the loop among a number of threads (yeah, the process in this context is divisible) to enhance performance. I create threads where every thread loops over a portion of the list building part of the tree (a tree node per loop per thread). This makes the processing goes down to roughly a minute. I want to further enhance the solution by displaying part of the tree (like 100 nodes) on startup and then let the threads do their work keeping the page responsive to the end user. Each thread, after every loop (i.e. after creating a tree node), should fire some kind of event (trigger a method) for primefaces to update the tree with the newly created node. And this is where I need help.
The Problem
I cannot find a way to programmatically tell primefaces to update the tree. I learned that RequestContext is used for such a purpose, but the problem is it fails when it's called from another thread. So I was wondering if you have better approach to solve this problem or maybe tell me what I am missing.
Simple Demo Code
xhtml file looks like this
<h:form id="form">
<h3 style="margin-top:0">Client</h3>
<p:tree value="#{treeBasicView.root}" var="node" id="clientTree">
<p:treeNode>
<h:outputText value="#{node}" />
</p:treeNode>
</p:tree>
</h:form>
Managed Bean looks like this
@ManagedBean(name = "treeBasicView")
@ViewScoped
public class BasicView implements Serializable {
private static TreeNode root;
@PostConstruct
public void init() {
root = new DefaultTreeNode("Root", null);
TreeNode node0 = new DefaultTreeNode("Node 0", root);
TreeNode node1 = new DefaultTreeNode("Node 1", root);
TreeNode node00 = new DefaultTreeNode("Node 0.0", node0);
TreeNode node01 = new DefaultTreeNode("Node 0.1", node0);
TreeNode node10 = new DefaultTreeNode("Node 1.0", node1);
node1.getChildren().add(new DefaultTreeNode("Node 1.1"));
node00.getChildren().add(new DefaultTreeNode("Node 0.0.0"));
node00.getChildren().add(new DefaultTreeNode("Node 0.0.1"));
node01.getChildren().add(new DefaultTreeNode("Node 0.1.0"));
node10.getChildren().add(new DefaultTreeNode("Node 1.0.0"));
root.getChildren().add(new DefaultTreeNode("Node 2"));
BackgroundThread t1 = new BackgroundThread();
t1.index = 5;
t1.view = this;
t1.setName("BGT 1");
BackgroundThread t2 = new BackgroundThread();
t2.index = 15;
t2.view = this;
t2.setName("BGT 2");
t1.start();
t2.start();
}
public TreeNode getRoot() {
return root;
}
public synchronized void updateTree(TreeNode node) {
root.getChildren().add(node);
}
}
The background thread looks like this:
public class BackgroundThread extends Thread {
public int index;
public BasicView view;
@Override
public void run() {
int end = index + 10;
while (index < end) {
System.out.println(getName() + ": Node BG " + index);
view.updateTree(new DefaultTreeNode("Node BG: " + index));
index += 1;
try {
Thread.sleep(500);
} catch (InterruptedException ex) {
Logger.getLogger(BackgroundThread.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}
This really works in terms of adding new items to the tree. But I cannot invoke anything to programmatically update the tree in primefaces. I mean, the object root (of type TreeNode in the managed bean) gets updated, but this is not reflected in the xhtml page.
Specs
I'm using JSF 2.2 with primefaces 5.3.