Reputation: 4445
I am developing a Tree component, which might be prunned according to the user input. I have all nodes cached, in order to provide primefaces component model with always the same instances of the tree nodes.
But the problem is, that when I change the parent-child relationships (remove some of them), I always get a concurrent modification exception... Is there any way to workaround this issue or am I missing something.
Thanks in advance.
/**
* Recursively copy the hierarchy and prune not visible branches
* @param originalNode node of the original hierarchy to be copied
* @param parent parent node in the new hierarchy
* @param visibleNodes set of nodes, which should be included in the new hierarchy
* @param model model of the TreeAutompleteComponent
* @return copy of the oroginal node in the new hierarchy
*/
private TreeNode copyHierarchy(TreeNode originalNode, TreeNode parent, Set<TreeNode> visibleNodes, TreeAutocompleteModel model) {
if (!visibleNodes.contains(originalNode)) {
return null;
}
TreeNode newNode = model.getCompleteModelNodes().get(((NodeWrapper)originalNode.getData()).getIri());//new DefaultTreeNode(originalNode.getData(), parent);
for (int i = 0; i < originalNode.getChildCount(); i++) {
copyHierarchy((TreeNode) originalNode.getChildren().get(i), newNode, visibleNodes, model);
}
newNode.setParent(parent); //when removed, no exception occurs
return newNode;
}
The exception
ava.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:819)
at java.util.ArrayList$Itr.next(ArrayList.java:791)
at org.primefaces.component.tree.TreeRenderer.encodeTreeNodeChildren(TreeRenderer.java:284)
at org.primefaces.component.tree.TreeRenderer.encodeTreeNode(TreeRenderer.java:274)
at org.primefaces.component.tree.TreeRenderer.encodeMarkup(TreeRenderer.java:165)
at org.primefaces.component.tree.TreeRenderer.encodeEnd(TreeRenderer.java:96)
at javax.faces.component.UIComponentBase.encodeEnd(UIComponentBase.java:875)
at com.sun.faces.renderkit.html_basic.HtmlBasicRenderer.encodeRecursive(HtmlBasicRenderer.java:312)
at com.sun.faces.renderkit.html_basic.GroupRenderer.encodeChildren(GroupRenderer.java:105)
at javax.faces.component.UIComponentBase.encodeChildren(UIComponentBase.java:845)
at com.sun.faces.renderkit.html_basic.HtmlBasicRenderer.encodeRecursive(HtmlBasicRenderer.java:304)
at com.sun.faces.renderkit.html_basic.GridRenderer.renderRow(GridRenderer.java:185)
at com.sun.faces.renderkit.html_basic.GridRenderer.encodeChildren(GridRenderer.java:129)
at javax.faces.component.UIComponentBase.encodeChildren(UIComponentBase.java:845)
at com.sun.faces.renderkit.html_basic.HtmlBasicRenderer.encodeRecursive(HtmlBasicRenderer.java:304)
at com.sun.faces.renderkit.html_basic.GridRenderer.renderRow(GridRenderer.java:185)
at com.sun.faces.renderkit.html_basic.GridRenderer.encodeChildren(GridRenderer.java:129)
at javax.faces.component.UIComponentBase.encodeChildren(UIComponentBase.java:845)
at com.sun.faces.renderkit.html_basic.HtmlBasicRenderer.encodeRecursive(HtmlBasicRenderer.java:304)
at com.sun.faces.renderkit.html_basic.GroupRenderer.encodeChildren(GroupRenderer.java:105)
at javax.faces.component.UIComponentBase.encodeChildren(UIComponentBase.java:845)
at com.sun.faces.renderkit.html_basic.HtmlBasicRenderer.encodeRecursive(HtmlBasicRenderer.java:304)
at com.sun.faces.renderkit.html_basic.GroupRenderer.encodeChildren(GroupRenderer.java:105)
at javax.faces.component.UIComponentBase.encodeChildren(UIComponentBase.java:845)
at com.sun.faces.renderkit.html_basic.HtmlBasicRenderer.encodeRecursive(HtmlBasicRenderer.java:304)
at com.sun.faces.renderkit.html_basic.GroupRenderer.encodeChildren(GroupRenderer.java:105)
at javax.faces.component.UIComponentBase.encodeChildren(UIComponentBase.java:845)
at javax.faces.component.UIComponent.encodeAll(UIComponent.java:1779)
at com.sun.faces.renderkit.html_basic.CompositeRenderer.encodeChildren(CompositeRenderer.java:78)
at javax.faces.component.UIComponentBase.encodeChildren(UIComponentBase.java:845)
at javax.faces.component.UIComponent.encodeAll(UIComponent.java:1779)
at javax.faces.render.Renderer.encodeChildren(Renderer.java:168)
at javax.faces.component.UIComponentBase.encodeChildren(UIComponentBase.java:845)
at javax.faces.component.UIComponent.encodeAll(UIComponent.java:1779)
at javax.faces.component.UIComponent.encodeAll(UIComponent.java:1782)
at javax.faces.component.UIComponent.encodeAll(UIComponent.java:1782)
at com.sun.faces.application.view.FaceletViewHandlingStrategy.renderView(FaceletViewHandlingStrategy.java:424)
at com.sun.faces.application.view.MultiViewHandler.renderView(MultiViewHandler.java:125)
at com.sun.faces.lifecycle.RenderResponsePhase.execute(RenderResponsePhase.java:121)
at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
at com.sun.faces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:139)
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:594)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:304)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at org.netbeans.modules.web.monitor.server.MonitorFilter.doFilter(MonitorFilter.java:393)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:240)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:164)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:462)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:164)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:100)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:563)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:399)
at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:317)
at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:204)
at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:182)
at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:311)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
at java.lang.Thread.run(Thread.java:722)
Upvotes: 2
Views: 4470
Reputation: 4445
This is solution, that works. I havent localized the actual issue, but I have sucessfully workaround all it...
public TreeNode constructActualModel(TreeAutocompleteMatcherIface matcher) {
if (prunedModel == null) {
eraseHierarchyStructure();
prunedModel = copyHierarchy((TreeNode) completeModel, getVisibleNodes(matcher));
}
return prunedModel;
}
/**
* Recursively copy the hierarchy and prune not visible branches
* @param originalNode node of the original hierarchy to be copied
* @param visibleNodes set of nodes, which should be included in the new hierarchy
* @param model model of the TreeAutompleteComponent
* @return copy of the oroginal node in the new hierarchy
*/
private TreeNode copyHierarchy(TreeNode originalNode, Set<TreeNode> visibleNodes) {
if (!visibleNodes.contains(originalNode)) {
return null;
}
TreeNode newNode = this.getCompleteModelNodes().get(((NodeWrapper) originalNode.getData()).getIri());//new DefaultTreeNode(originalNode.getData(), parent);
List<TreeNode> children = new ArrayList<TreeNode>();
for (int i = 0; i < originalNode.getChildCount(); i++) {
TreeNode child = copyHierarchy((TreeNode) originalNode.getChildren().get(i), visibleNodes);
if(child != null){
children.add(child);
}
}
((DefaultTreeNode) newNode).setChildren(children);
return newNode;
}
private void eraseHierarchyStructure() {
for (TreeNode node : duplicateCompleteModelNodes.values()) {
node.setParent(null);
}
}
It was necessary to do it the opposite way - to get the impl to be able to call the setChildren method and at first erase the hierarchy...
Upvotes: 1
Reputation: 5709
You are trying to modify the collection you are currently iteratring over.
Have you tried working on a copy of the collection in question instead of modifying the current one?
From http://docs.oracle.com/javase/7/docs/api/java/util/ConcurrentModificationException.html:
Note that this exception does not always indicate that an object has been concurrently modified by a different thread. If a single thread issues a sequence of method invocations that violates the contract of an object, the object may throw this exception. For example, if a thread modifies a collection directly while it is iterating over the collection with a fail-fast iterator, the iterator will throw this exception.
In your recursive function, you have the following scenario:
1 step before you come to the leaf of your tree:
Let's say you have a parent node "5" an only child (leaf) node "11". ("11" has "5" as its parent)
You start iterating over children ("11"), and call copyHierarchy on him.
Since he has no children, the loop is skipped and his parent becomes newNode, "17" for example.
Then you return one level up.
You were supposed to be iterating over nodes who have "5" as their parent, but node "11" actually has had its parent changed from "5" to "17".
Upvotes: 2