9 Replies Latest reply on Mar 11, 2008 12:48 PM by ssilvert

    ajaxSubmit produces duplicate id exception

      Hi,

      I've got a rich:dataTable with a button in the header:

      
      ...
      ...
       <ui:define name="body" >
       <h:form id="clientListForm" style="padding: 5px">
       <rich:dataTable
       id="clientTable"
       value="#{ClientManagementSession.availableClientsModel}"
       var="client" width="100%">
       <f:facet name="header">
       <h:panelGrid columns="2" columnClasses="nintyFiveWidth bold leftAlign, fiveWidth">
       <h:outputText value="#{BackofficeSession.langMap.configured_clients}" />
       <a4j:commandButton id="openCreateClientDialogueButton"
       value="#{BackofficeSession.langMap.create_new_client}"
       eventsQueue="openDialogueQueue"
       action="#{BackofficeSession.showModalPanel}"
       reRender="createClientDialoguePanel" />
       </h:panelGrid>
       </f:facet>
      ...
      ...
      
      


      And I have the following test:

      
       // just check that the create client dialogue is not visible
       Boolean dialogueOpen = (Boolean)
       server.getManagedBeanValue("#{BackofficeSession.showModalPanel}");
       assertFalse("Show modal dialogue should be false", dialogueOpen.booleanValue());
       // and then by checking the dialogue component itself.
       UIModalPanel modalPanel = (UIModalPanel) server.findComponent("createClientDialogue");
       assertFalse("The create client dialogue is visiable when it should not be", modalPanel.isShowWhenRendered());
      
       try {
       // open the create client dialogue
       richClient.ajaxSubmit("openCreateClientDialogueButton");
       } catch (SAXException ex) {
       fail("Recieved a SaxException while trying to open create client dialogue: " + ex.getMessage());
       } catch (IOException ex) {
       fail("Recieved an IOException while trying to open create client dialogue: " + ex.getMessage());
       }
      
      


      Which works fine when there are zero or one rows in the data table, as soon as there are two rows I get the following exception:

      ...
      <error message="openCreateClientDialogueButton matches more than one JSF component ID. Use a more specific ID suffix. Suffix matches: clientListForm:clientTable:openCreateClientDialogueButton, clientListForm:clientTable:openCreateClientDialogueButton" type="org.jboss.jsfunit.facade.DuplicateClientIDException">
      ...
      


      Anyone have any idea's how I can get around this?

        • 1. Re: ajaxSubmit produces duplicate id exception

          Sorry, using JSFUnit Beta 1 with JBoss 4.2.2GA and RichFaces 3.1.4GA

          • 2. Re: ajaxSubmit produces duplicate id exception
            ssilvert

            This was a bug. Thanks for finding it. I've already reproduced it and committed a fix:
            http://jira.jboss.com/jira/browse/JSFUNIT-78

            You will need to checkout and build JSFUnit from SVN to get the fix:
            http://labs.jboss.com/jsfunit/building-jsfunit.html

            Regards,

            Stan

            • 3. Re: ajaxSubmit produces duplicate id exception

              Thanks for the quick response Stan,
              I'll give that a go in the morning.
              Your a star :)

              • 4. Re: ajaxSubmit produces duplicate id exception

                Hi Stan,

                I've just tried the latest snapshot and I get a new error now :(

                11:33:51,072 ERROR [[Faces Servlet]] Servlet.service() for servlet Faces Servlet threw exception
                java.lang.IllegalStateException
                 at com.sun.faces.context.FacesContextImpl.assertNotReleased(FacesContextImpl.java:428)
                 at com.sun.faces.context.FacesContextImpl.getExternalContext(FacesContextImpl.java:149)
                 at org.jboss.jsfunit.context.JSFUnitFacesContext.getExternalContext(JSFUnitFacesContext.java:114)
                 at org.jboss.jsfunit.context.JSFUnitFacesContext.release(JSFUnitFacesContext.java:170)
                 at javax.faces.webapp.FacesServlet.service(FacesServlet.java:262)
                 at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
                 at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
                 at org.ajax4jsf.webapp.BaseXMLFilter.doXmlFilter(BaseXMLFilter.java:141)
                 at org.ajax4jsf.webapp.BaseFilter.doFilter(BaseFilter.java:281)
                 at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
                 at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
                 at org.jboss.web.tomcat.filters.ReplyHeaderFilter.doFilter(ReplyHeaderFilter.java:96)
                 at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
                 at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
                 at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:230)
                 at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:175)
                 at org.jboss.web.tomcat.security.SecurityAssociationValve.invoke(SecurityAssociationValve.java:179)
                 at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:524)
                 at org.jboss.web.tomcat.security.JaccContextValve.invoke(JaccContextValve.java:84)
                 at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
                 at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
                 at org.jboss.web.tomcat.service.jca.CachedConnectionValve.invoke(CachedConnectionValve.java:157)
                 at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
                 at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:262)
                 at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:844)
                 at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:583)
                 at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:446)
                 at java.lang.Thread.run(Thread.java:619)
                11:33:51,175 ERROR [[ServletTestRunner]] Servlet.service() for servlet ServletTestRunner threw exception
                java.lang.IllegalStateException
                 at com.sun.faces.context.FacesContextImpl.assertNotReleased(FacesContextImpl.java:428)
                 at com.sun.faces.context.FacesContextImpl.getExternalContext(FacesContextImpl.java:149)
                 at org.jboss.jsfunit.context.JSFUnitFacesContext.release(JSFUnitFacesContext.java:170)
                 at org.jboss.jsfunit.context.JSFUnitFacesContext.valueUnbound(JSFUnitFacesContext.java:201)
                 at org.apache.catalina.session.StandardSession.removeAttributeInternal(StandardSession.java:1649)
                 at org.apache.catalina.session.StandardSession.removeAttribute(StandardSession.java:1207)
                 at org.apache.catalina.session.StandardSession.removeAttribute(StandardSession.java:1179)
                 at org.apache.catalina.session.StandardSessionFacade.removeAttribute(StandardSessionFacade.java:140)
                 at org.jboss.jsfunit.framework.JSFUnitFilter.doFilter(JSFUnitFilter.java:127)
                 at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
                ...
                ...
                


                The application I'm working on uses form authentication set up in the following manor in the web.xml:

                 <security-constraint>
                 <display-name>Must be logged in</display-name>
                 <web-resource-collection>
                 <web-resource-name>All Pages</web-resource-name>
                 <description>Secure all pages in the application</description>
                 <url-pattern>*.jspx</url-pattern>
                 <http-method>GET</http-method>
                 <http-method>POST</http-method>
                 <http-method>HEAD</http-method>
                 <http-method>PUT</http-method>
                 <http-method>OPTIONS</http-method>
                 <http-method>TRACE</http-method>
                 <http-method>DELETE</http-method>
                 </web-resource-collection>
                 <auth-constraint>
                 <description>Must have login role to view any pages</description>
                 <role-name>log_in</role-name>
                 </auth-constraint>
                 </security-constraint>
                
                 <login-config>
                 <auth-method>FORM</auth-method>
                 <realm-name>xxxxx</realm-name>
                 <form-login-config>
                 <form-login-page>/login.jspx</form-login-page>
                 <form-error-page>/loginError.html</form-error-page>
                 </form-login-config>
                 </login-config>
                
                 <security-role>
                 <description>Must be logged in to view any pages.</description>
                 <role-name>log_in</role-name>
                 </security-role>
                


                And in the tests I use the following code to ensure that the test authenticates with the system:

                 public static JSFClientSession getClientSession(String url)
                 throws MalformedURLException, IOException, SAXException {
                 JSFClientSession client = new JSFClientSession(url);
                 JSFServerSession server = new JSFServerSession(client);
                 if (server.getCurrentViewID().endsWith("login.xhtml")) {
                 // we've been redirected, need to login
                 WebForm loginForm = client.getWebResponse().getFormWithID("loginform");
                 if (loginForm != null) {
                 loginForm.setParameter("j_username", "regaladmin");
                 loginForm.setParameter("j_password", "regaladmin");
                 SubmitButton loginButton = loginForm.getSubmitButton("login");
                 if (loginButton != null) {
                 loginButton.click();
                 } else {
                 throw new IOException("Test for " + url + " was redirected to the login " +
                 "page but we were unable to locate the login button");
                 }
                 FacesContext facesContext = FacesContextBridge.getCurrentInstance();
                 UIViewRoot root = facesContext.getViewRoot();
                 String expectedViewId = url.replace(".jspx", ".xhtml");
                 if (!expectedViewId.equals(root.getViewId())) {
                 throw new IOException("Failed to login to application, last view id was " + root.getViewId());
                 }
                 // no we are logged in, re-request the client to refresh it
                 client = new JSFClientSession(url);
                 } else {
                 throw new IOException("Test for " + url + " was redirected to the login " +
                 "page but we were unable to locate the login form");
                 }
                 }
                 return client;
                 }
                


                I've stuck a debugger on it an it blows up when trying to create the JSFClientSession (JSFClientSession client = new JSFClientSession(url);).

                This was working in the beta-1 code, but not the snapshot.

                • 5. Re: ajaxSubmit produces duplicate id exception

                  Sorry, this is my fault, still had the old beta-1 jar's in the deployment DOH.

                  Thanks again :)

                  • 6. Re: ajaxSubmit produces duplicate id exception

                    I think I may have found another problem :( again with the button in the header. As before I have this set up in the jsf xml:

                    ...
                    ...
                     <ui:define name="body" >
                     <h:form id="clientListForm" style="padding: 5px">
                     <rich:dataTable
                     id="clientTable"
                     value="#{ClientManagementSession.availableClientsModel}"
                     var="client" width="100%">
                     <f:facet name="header">
                     <h:panelGrid columns="2" columnClasses="nintyFiveWidth bold leftAlign, fiveWidth">
                     <h:outputText value="#{BackofficeSession.langMap.configured_clients}" />
                     <a4j:commandButton id="openCreateClientDialogueButton"
                     value="#{BackofficeSession.langMap.create_new_client}"
                     eventsQueue="openDialogueQueue"
                     action="#{BackofficeSession.showModalPanel}"
                     reRender="createClientDialoguePanel" />
                     </h:panelGrid>
                     </f:facet>
                     <rich:column>
                     <f:facet name="header">
                     <h:outputText value="#{BackofficeSession.langMap.client_name}"/>
                     </f:facet>
                     <h:outputText value="#{client.clientName}"/>
                     </rich:column>
                    ...
                    ...
                    
                    


                    And once the JSFClientSession and JSFServerSession are set up the findComponent of server will not find the button component:

                    server.findComponent("openCreateClientDialogueButton")
                    


                    throws a ComponentIDNotFoundException. I've also tried the full client id of the component.

                    Strangely this code works:

                    server.findComponent("clientTable").findComponent("openCreateClientDialogueButton")
                    


                    But the upshot is that when I try and do and ajaxSubmit for the button the ComponentIDNotFoundException is thrown.


                    • 7. Re: ajaxSubmit produces duplicate id exception

                      Sorry, here is the stack trace:

                      −
                       <testsuites>
                      −
                       <testsuite name="com.uk.realistic.regal.web.tests.systemmanagement.ListClientTest" tests="2" failures="0" errors="1" time="290.282">
                      −
                       <testcase name="testCreateClient" time="288.073">
                      −
                       <error message="No component ID was found for clientListForm:clientTable:openCreateClientDialogueButton" type="org.jboss.jsfunit.facade.ComponentIDNotFoundException">
                      org.jboss.jsfunit.facade.ComponentIDNotFoundException: No component ID was found for clientListForm:clientTable:openCreateClientDialogueButton
                       at org.jboss.jsfunit.facade.ClientIDs.findClientID(ClientIDs.java:170)
                       at org.jboss.jsfunit.richfaces.Ajax4jsfClient.setRowIndicies(Ajax4jsfClient.java:92)
                       at org.jboss.jsfunit.richfaces.Ajax4jsfClient.ajaxSubmit(Ajax4jsfClient.java:168)
                       at org.jboss.jsfunit.richfaces.Ajax4jsfClient.ajaxSubmit(Ajax4jsfClient.java:152)
                       at com.uk.realistic.regal.web.tests.systemmanagement.ListClientTest.testCreateClient(ListClientTest.java:72)
                       at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
                       ...
                       ....
                      


                      • 8. Re: ajaxSubmit produces duplicate id exception

                        Looks like this is being caused by the fact there are no rows in the table and the addUIData method of ClientIDs only adds children when the component has a rowCount:

                         private void addUIData(UIData component, FacesContext facesContext)
                         {
                         String parentClientID = component.getClientId(facesContext);
                         // TODO: find out if headers and footers are found
                         for (int i=0; i < component.getRowCount(); i++)
                         {
                         component.setRowIndex(i);
                         addAncestors(parentClientID, component.getClientId(facesContext));
                         for (Iterator facetsAndChildren = component.getFacetsAndChildren(); facetsAndChildren.hasNext();)
                         {
                         UIComponent facetOrChild = (UIComponent)facetsAndChildren.next();
                         addAncestors(component, facetOrChild, facesContext);
                         addAllIDs(facetOrChild, facesContext);
                         }
                         }
                        
                         component.setRowIndex(-1);
                         }
                        
                        


                        I fixed it by processing the UIData's facets and children seprately.

                         private void addUIData(UIData component, FacesContext facesContext) {
                         String parentClientID = component.getClientId(facesContext);
                        
                         // TODO: find out if headers and footers are found
                        
                         int rowCount = component.getRowCount();
                        
                         // process the facets
                         for (UIComponent facet : component.getFacets().values()) {
                         addAncestors(component, facet, facesContext);
                         addAllIDs(facet, facesContext);
                         }
                        
                         // and process any children for each row of the data component
                         for (int i = 0; i < rowCount; i++) {
                         component.setRowIndex(i);
                         addAncestors(parentClientID, component.getClientId(facesContext));
                         for (UIComponent child : component.getChildren()) {
                         addAncestors(component, child, facesContext);
                         addAllIDs(child, facesContext);
                         }
                         }
                        
                         component.setRowIndex(-1);
                         }
                        


                        While this does fix my little problem, I don't know what impacts this will have on other UIData components.

                        • 9. Re: ajaxSubmit produces duplicate id exception
                          ssilvert

                          Thanks for the analysis. I think your code is probably correct. I'll take a closer look very soon. Here is the jira for tracking purposes:
                          http://jira.jboss.com/jira/browse/JSFUNIT-80

                          Stan