java.lang.IndexOutOfBoundsException during view state saving when using primeface treeTable inside TabView of a composite component

alpha2011 Source

I am facing an java.lang.IndexOutOfBoundsException during view state saving with the combination of TabView and TreeTable. I have a list of TreeNodes and I create a TreeTable for each of them. Every p:treeTable is inside a composite component which in turn is contained in a p:tab. Some of the column of the treetables have p:input element and I am using p:ajax to process the value and update some dom objects using primefaces "update" attribute of "p:ajax". For treetable of treenode with index > 0, when the ajax get fired for any row with index superior to the first treenode (index =0) size I get the following error:

    java.lang.IndexOutOfBoundsException: Index: 19, Size: 4
    at java.util.ArrayList.RangeCheck(ArrayList.java:547)
    at java.util.ArrayList.get(ArrayList.java:322)
    at org.primefaces.component.api.UITree.findTreeNode(UITree.java:121)
    at org.primefaces.component.api.UITree.setRowKey(UITree.java:80)
    at org.primefaces.component.api.UITree.visitTree(UITree.java:417)
    at javax.faces.component.UIComponent.visitTree(UIComponent.java:1623)
    at javax.faces.component.UIComponent.visitTree(UIComponent.java:1623)
    at javax.faces.component.UINamingContainer.visitTree(UINamingContainer.java:163)
    at javax.faces.component.UIComponent.visitTree(UIComponent.java:1623)
    at javax.faces.component.UIComponent.visitTree(UIComponent.java:1623)
    at javax.faces.component.UIComponent.visitTree(UIComponent.java:1623)
    at javax.faces.component.UIForm.visitTree(UIForm.java:362)
    at javax.faces.component.UIComponent.visitTree(UIComponent.java:1623)
    at org.primefaces.component.api.UIData.visitRows(UIData.java:748)
    at org.primefaces.component.api.UIData.visitTree(UIData.java:654)
    at org.primefaces.component.tabview.TabView.visitTree(TabView.java:433)
    at javax.faces.component.UIComponent.visitTree(UIComponent.java:1623)
    at javax.faces.component.UIComponent.visitTree(UIComponent.java:1623)
    at javax.faces.component.UIComponent.visitTree(UIComponent.java:1623)
    at javax.faces.component.UIComponent.visitTree(UIComponent.java:1623)
    at javax.faces.component.UINamingContainer.visitTree(UINamingContainer.java:163)
    at javax.faces.component.UIComponent.visitTree(UIComponent.java:1623)
    at org.primefaces.component.accordionpanel.AccordionPanel.visitTree(AccordionPanel.java:395)
    at javax.faces.component.UIComponent.visitTree(UIComponent.java:1623)
    at javax.faces.component.UIComponent.visitTree(UIComponent.java:1623)
    at javax.faces.component.UINamingContainer.visitTree(UINamingContainer.java:163)
    at javax.faces.component.UIComponent.visitTree(UIComponent.java:1623)
    at javax.faces.component.UIComponent.visitTree(UIComponent.java:1623)
    at com.sun.faces.application.view.FaceletPartialStateManagementStrategy.saveView(FaceletPartialStateManagementStrategy.java:473)
    at com.sun.faces.application.StateManagerImpl.saveView(StateManagerImpl.java:89)
    at javax.faces.application.StateManager.getViewState(StateManager.java:553)
    at com.sun.faces.context.PartialViewContextImpl.renderState(PartialViewContextImpl.java:418)
    at com.sun.faces.context.PartialViewContextImpl.processPartial(PartialViewContextImpl.java:301)
    at javax.faces.context.PartialViewContextWrapper.processPartial(PartialViewContextWrapper.java:183)
    at javax.faces.component.UIViewRoot.encodeChildren(UIViewRoot.java:982)
    at javax.faces.component.UIComponent.encodeAll(UIComponent.java:1779)
    at com.sun.faces.application.view.FaceletViewHandlingStrategy.renderView(FaceletViewHandlingStrategy.java:411)
    at com.sun.faces.application.view.MultiViewHandler.renderView(MultiViewHandler.java:124)
    at com.sun.faces.lifecycle.RenderResponsePhase.execute(RenderResponsePhase.java:120)
    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.primefaces.webapp.filter.FileUploadFilter.doFilter(FileUploadFilter.java:79)
    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:562)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:395)
    at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:250)
    at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:188)
    at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:302)
    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
    at java.lang.Thread.run(Thread.java:662)
Aug 21, 2013 11:43:44 AM org.apache.catalina.core.StandardWrapperValve invoke
SEVERE: Servlet.service() for servlet [Faces Servlet] in context with path [/igamt-ui] threw exception
java.lang.IllegalStateException: CDATA tags may not nest
    at com.sun.faces.renderkit.html_basic.HtmlResponseWriter.startCDATA(HtmlResponseWriter.java:664)
    at javax.faces.context.ResponseWriterWrapper.startCDATA(ResponseWriterWrapper.java:172)
    at javax.faces.context.PartialResponseWriter.startError(PartialResponseWriter.java:342)
    at org.primefaces.context.PrimePartialResponseWriter.startError(PrimePartialResponseWriter.java:155)
    at com.sun.faces.context.AjaxExceptionHandlerImpl.handlePartialResponseError(AjaxExceptionHandlerImpl.java:200)
    at com.sun.faces.context.AjaxExceptionHandlerImpl.handle(AjaxExceptionHandlerImpl.java:124)
    at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:119)
    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.primefaces.webapp.filter.FileUploadFilter.doFilter(FileUploadFilter.java:79)
    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)

It seems like the p:ajax is refering always to the first treenode (index =0) which size (number of children) is 4.

Here is my composite component: treeTableComponent.xhtml
    <ui:composition xmlns="http://www.w3.org/1999/xhtml"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:p="http://primefaces.org/ui"
    xmlns:ui="http://java.sun.com/jsf/facelets"
    xmlns:composite="http://java.sun.com/jsf/composite"
    xmlns:myComponents="http://java.sun.com/jsf/composite/myComponents">

    <composite:interface>
        <composite:attribute name="managerBean" required="true" />
        <composite:attribute name="node" required="true" />
    </composite:interface>

    <composite:implementation>
        <div id="#{cc.id}">

            <ui:param name="currentNode" value="#{cc.attrs.node}" />

            <p:treeTable resizableColumns="true" value="#{currentNode}"
                id="segmentmaker_segment_tree" var="nodeData"
                scrollable="true">

                <p:ajax event="collapse" global="true"
                    listener="#{cc.attrs.managerBean.onNodeCollapse}" process="@this" />
                <p:ajax event="expand" global="true"
                    listener="#{cc.attrs.managerBean.onNodeExpand}" process="@this" />

                <p:column style="width:100px"
                    headerText="Column A">
                    <p:outputPanel layout="block">
                        <p:outputPanel id="column_a">
                            <p:inputText
                                value="#{nodeData.a}" size="5"
                                required="false">
                                <p:ajax event="blur" partialSubmit="true"
                                    update="column_a column_b"
                                    global="true" async="true"
                                    listener="#{cc.attrs.managerBean.process}">
                                </p:ajax>
                            </p:inputText>
                        </p:outputPanel>
                    </p:outputPanel>
                </p:column>
                <p:column style="width:100px"
                    headerText="Column B">
                     <p:outputPanel id="column_b">
                            <p:inputText
                                value="#{nodeData.b}" size="5"
                                required="false">
                                <p:ajax event="blur" partialSubmit="true"
                                    update="column_a column_b"
                                    global="true" async="true"
                                    listener="#{cc.attrs.managerBean.process}">
                                </p:ajax>
                            </p:inputText>
                        </p:outputPanel>
                    </p:outputPanel>
            </p:column>
        </p:treeTable>
        </div>
    </composite:implementation>




</ui:composition>

Here is my backing bean: ManagerBean.java

public class ManagerBean implements Serializable {

    public ManagerBean(){
    nodes = new ArrayList<TreeNode>();
    nodes.add(new DefaultTreeNode("one", new DataModel("one"), null));
    nodes.add(new DefaultTreeNode("two", new DataModel("two"), null));
    nodes.add(new DefaultTreeNode("three", new DataModel("three"), null));
    nodes.add(new DefaultTreeNode("four", new DataModel("four"), null));

    //add childrens 
    for(int i = 0; i < nodes.size(); i++){
        for(int j = 0; j < 10; i++){
            new DefaultTreeNode("one"+(j+1), new DataModel("one"+(j+1)), nodes.get(i);
        }
    }

    }  


    private List<TreeNode> nodes = null;

    public void process(AjaxBehaviorEvent event) {
    try {
         FacesContext context = FacesContext.getCurrentInstance();
        DataModel currentSegmentChildModel =
            context.getApplication().evaluateExpressionGet(context, "#{nodeData}",
                DataModel.class);
        TreeNode node =
            context.getApplication().evaluateExpressionGet(context, "#{currentNode}",
                EditorTreeNode.class);

        //business logic 

    } catch (ValidationException e) {}
  } 


  public void onNodeExpand(NodeExpandEvent event) {
    event.getTreeNode().setExpanded(true);
  }

  public void onNodeCollapse(NodeCollapseEvent event) {
    event.getTreeNode().setExpanded(false);
  }
}

Here is the client calling treeTableComponent composite component. The client is itself a composite component: treeTableComponents.xhtml

<ui:composition xmlns="http://www.w3.org/1999/xhtml"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:p="http://primefaces.org/ui"
    xmlns:ui="http://java.sun.com/jsf/facelets"
    xmlns:composite="http://java.sun.com/jsf/composite"
    xmlns:myComponents="http://java.sun.com/jsf/composite/myComponents">
    <composite:interface>
        <composite:attribute name="managerBean" required="true" />
    </composite:interface>

    <composite:implementation>
        <div id="#{cc.id}">
                    <p:tabView
                        cache="true" dynamic="true" var="myNode"
                        value="#{cc.attrs.managerBean.nodes}">
                        <p:tab header="#{myNode.name}">
                            <h:form prependId="false">
                                    <p:outputPanel layout="block">
                                        <myComponents:treeTableComponent 
                                            node="#{myNode}"
                                            managerBean="#{cc.attrs.managerBean}" />
                                    </p:outputPanel>
                            </h:form>
                        </p:tab>
                    </p:tabView>
        </div>
    </composite:implementation>
</ui:composition>

Here is the datamodel code: DataModel.java

public  class DataModel implements Serializable {

   private String a; 

   private String b; 


   public String getA(){
    return a;
   }

   public String getB(){
    return b;
   }

    public void setA(String a){
        this.a = a;
    }
        public void setB(String b){
        this.b = b;
    }


}

I am using Primefaces 3.5 with Mojarra 2.1.17.

Any help will be very appreciated.

jsf-2primefaces

Answers

answered 3 years ago wutzebaer #1

hi this is a bug in primefaces which seems not to be fixed https://code.google.com/p/primefaces/issues/detail?id=5255

what helped me was resetting the three after changing my nodes in my action-function

// update/remove etc nodes
super.initTree(user);
// find the UITree component
List<UIComponent> findComponent2 = Utils.findComponent("tree");
Tree uiComponent = (Tree) findComponent2.get(1);
// reset tree
uiComponent.setRowKey(null);

my find function looks like that, but i think using a binding-variable is much better... this was just my first shot

public static List<UIComponent> findComponent(final String id) {
    FacesContext context = FacesContext.getCurrentInstance();
    UIViewRoot root = context.getViewRoot();
    final List<UIComponent> result = new ArrayList<>();

    try {
        root.visitTree(new FullVisitContext(context), new VisitCallback() {
            @Override
            public VisitResult visit(VisitContext context, UIComponent component) {
                if (component.getId().toLowerCase().contains(id)) {
                    result.add(component);
                }
                return VisitResult.ACCEPT;
            }
        });
    } catch (Exception e) {
    }
    return result;
}

comments powered by Disqus