3 Replies Latest reply on Feb 9, 2009 8:44 AM by Jan Hinzmann

    Bug: a4j:commandLinks not working inside t:datatable

    Jan Hinzmann Newbie

      Hi *,

      I've been messing around with this problem for quite some time now.
      So I think it's time to get some more help with it. I've searched the forums but could not find any advice.

      But lets start at the beginning. We are developing an JSF application using
      - tomcat 6.0.14 ( also tested: Tomcat 6.0.18, Tomcat 5.5.27 and Jetty 6.1.14)
      - jstl 1.2
      - facelets 1.1.11
      - myfaces 1.2.2
      - richfaces 3.3.0.GA
      - tomahawk 1.1.6
      (I've tried different combinations of the dependencies but this one is the closest to the documentation.)

      On the page a t:datatable representing a list of Organization beans is implemented. In the column "Id" we have a4j:commandLinks to select the row. The row is identified by the uid of the underlying bean.
      The a4j:commandLinks are calling the actionListener="#{bean.handleSelectUidAction}" which sets the selected uid in the OrganizationBean so it is rendered properly (black, not underlined).

      The bug is now: only the last a4j:commandLink is working.

      Here is the code of the page:

      <a4j:form id="customersForm">
       <t:div id="listContainer">
       <t:dataTable id="bupas"
       value="# {organizationBean.resultPageBeans}"
       var="customer"
       rowIndexVar="customerTableRowIndex"
       preserveSort="true"
       sortAscending="# {organizationBean.sortAscending}"
       forceIdIndexFormula="# {customer.uid}"
       styleClass="datatable"
       headerClass="datatable-header"
       rowClasses="datatable-row-even, datatable-row-odd">
      
       <h:column>
       <f:facet name="header">
       <t:commandSortHeader columnName="id" arrow="true" immediate="false">
       <h:outputText value="Id" />
       </t:commandSortHeader>
       </f:facet>
      
       <a4j:commandLink id="a4j-selectDetails"
       ajaxSingle="true"
       value="# {customer.uid}"
       title="showDetails"
       actionListener="# {bean.handleSelectUidAction}"
       reRender="bupas"
       style="# {(customer.uid eq bean.selectedUid) ? 'color: black; font-weight: bold; text-decoration: none;' : ''}">
      
       <f:param name="selectedUid" value="# {customer.uid}" />
       </a4j:commandLink>
       </h:column>
      ...
      
      



      The bug occures if we use the attriute forceIdIndexFormula="#{customer.uid}". The example works only iff the bean.uids are starting with zero.

      The uid of each bean is generated uniquely in a for loop. The uid starts with the value of bean.lower and ends with bean.upper. Current values are:
      bean.lower: 1
      bean.upper: 3

      You can change these values in src/main/java/net.neobp.jsf.Organization.java. Setting bean.lower to 0 let all commandlinks work correct.

      I've set up an example application using maven2, which can be found here: http://www.neo-partners.de/download/test-jsf.zip

      You need maven 2 installed. Then:

      - unzip the test-jsf.zip
      - cd test-jsf
      - mvn package
      - cp target/test-jsf.war $CATALINA_HOME/webapps
      - cd $CATALINA_HOME
      - bin/startup.sh


      You will then be able to access the application at
      http://localhost:8080/test-jsf/

      Any advice is appreciated!

      regards,
      Jan

        • 1. Re: Bug: a4j:commandLinks not working inside t:datatable
          Ilya Shaikovsky Master

          we didn't tested our partiall processing feature with t:dataTable. Rich Faces dataTable uses invokeOnComponent from JSF 1.2 in order to process and update table partially - so it will works stable for such case for sure.

          • 2. Re: Bug: a4j:commandLinks not working inside t:datatable
            Jan Hinzmann Newbie

            Hi Ilya,

            unfortunatly switching from t:datatable to rich:datatable is not an option.

            I have also found this in the log files.

            Case of working link:

            2009-01-28 13:20:47,828 DEBUG JaHi [org.apache.myfaces.webapp.MyFacesServlet::service(102)] MyFacesServlet service start
            2009-01-28 13:20:47,828 DEBUG JaHi [javax.faces.webapp.FacesServlet::service(146)] service begin
            2009-01-28 13:20:47,828 DEBUG JaHi [org.apache.myfaces.lifecycle.LifecycleImpl::executePhase(89)] entering RESTORE_VIEW(1) in org.apache.myfaces.lifecycle.LifecycleImpl
            2009-01-28 13:20:47,828 DEBUG JaHi [org.ajax4jsf.event.AjaxPhaseListener::beforePhase(102)] Process before phase RESTORE_VIEW(1)
            2009-01-28 13:20:47,828 DEBUG JaHi [org.apache.myfaces.lifecycle.DefaultRestoreViewSupport::calculateViewId(111)] Calculated viewId '/organization.jsf' from request servlet path
            2009-01-28 13:20:47,828 DEBUG JaHi [org.apache.myfaces.lifecycle.RestoreViewExecutor::execute(83)] Request is a postback
            2009-01-28 13:20:47,828 DEBUG JaHi [org.apache.myfaces.application.DefaultViewHandlerSupport::applyDefaultSuffix(213)] view id after applying the context suffix: /organization.xhtml
            2009-01-28 13:20:47,828 DEBUG JaHi [apache.myfaces.shared_impl.util.StateUtils::findAlgorithm(464)] Using algorithm DES
            2009-01-28 13:20:47,828 DEBUG JaHi [apache.myfaces.shared_impl.util.StateUtils::findAlgorithmParams(437)] Using algorithm paramaters ECB/PKCS5Padding
            2009-01-28 13:20:47,828 DEBUG JaHi [apache.myfaces.shared_impl.util.StateUtils::symmetric(366)] decrypting w/ DES/ECB/PKCS5Padding
            2009-01-28 13:20:47,828 DEBUG JaHi [org.ajax4jsf.application.AjaxStateHolder::getInstance(49)] Request for a view states holder instance
            2009-01-28 13:20:47,828 DEBUG JaHi [org.richfaces.component.UIComponentControl::setParent(135)] Called setParent for AjaxSupport component with parent : javax.faces.component.html.HtmlPanelGroup
            2009-01-28 13:20:47,828 DEBUG JaHi [org.richfaces.component.UIComponentControl::setParent(144)] Detect newly created component
            2009-01-28 13:20:47,828 DEBUG JaHi [org.ajax4jsf.component.AjaxRegionBrige::restoreState(243)] Restore State of UIAjaxComponent with Id _viewRoot
            2009-01-28 13:20:47,844 DEBUG JaHi [org.ajax4jsf.event.AjaxPhaseListener::afterPhase(69)] Process after phase RESTORE_VIEW(1)
            2009-01-28 13:20:47,844 DEBUG JaHi [org.apache.myfaces.lifecycle.LifecycleImpl::executePhase(118)] exiting RESTORE_VIEW(1) in org.apache.myfaces.lifecycle.LifecycleImpl
            2009-01-28 13:20:47,844 DEBUG JaHi [org.apache.myfaces.lifecycle.LifecycleImpl::executePhase(89)] entering APPLY_REQUEST_VALUES(2) in org.apache.myfaces.lifecycle.LifecycleImpl
            2009-01-28 13:20:47,844 DEBUG JaHi [org.ajax4jsf.event.AjaxPhaseListener::beforePhase(102)] Process before phase APPLY_REQUEST_VALUES(2)
            2009-01-28 13:20:47,844 DEBUG JaHi [org.ajax4jsf.renderkit.RendererBase::decode(71)] Start decoding of component _viewRoot with class org.ajax4jsf.component.AjaxViewRoot
            2009-01-28 13:20:47,844 DEBUG JaHi [org.ajax4jsf.renderkit.AjaxContainerRenderer::doDecode(150)] Decode ajax request status for _viewRoot
            2009-01-28 13:20:47,844 DEBUG JaHi [org.ajax4jsf.component.AjaxRegionBrige::setSubmitted(188)] Submitted AJAX request - Queue Event to AjaxListeners
            2009-01-28 13:20:52,485 DEBUG JaHi [org.ajax4jsf.renderkit.RendererBase::decode(71)] Start decoding of component customersForm:bupas:2:a4j-selectDetails with class org.ajax4jsf.component.html.HtmlAjaxCommandLink
            2009-01-28 13:20:52,485 DEBUG JaHi [org.ajax4jsf.renderkit.AjaxCommandRendererBase::isSubmitted(133)] Decode submit of the Ajax component customersForm:bupas:2:a4j-selectDetails
            2009-01-28 13:20:52,485 DEBUG JaHi [org.ajax4jsf.event.AjaxPhaseListener::afterPhase(69)] Process after phase APPLY_REQUEST_VALUES(2)
            2009-01-28 13:20:52,485 DEBUG JaHi [org.apache.myfaces.lifecycle.LifecycleImpl::executePhase(118)] exiting APPLY_REQUEST_VALUES(2) in org.apache.myfaces.lifecycle.LifecycleImpl
            


            Case or broken Link:
            2009-01-28 13:22:25,237 DEBUG JaHi [org.apache.myfaces.webapp.MyFacesServlet::service(102)] MyFacesServlet service start
            2009-01-28 13:22:25,237 DEBUG JaHi [javax.faces.webapp.FacesServlet::service(146)] service begin
            2009-01-28 13:22:25,237 DEBUG JaHi [org.apache.myfaces.lifecycle.LifecycleImpl::executePhase(89)] entering RESTORE_VIEW(1) in org.apache.myfaces.lifecycle.LifecycleImpl
            2009-01-28 13:22:25,237 DEBUG JaHi [org.ajax4jsf.event.AjaxPhaseListener::beforePhase(102)] Process before phase RESTORE_VIEW(1)
            2009-01-28 13:22:25,253 DEBUG JaHi [org.apache.myfaces.lifecycle.DefaultRestoreViewSupport::calculateViewId(111)] Calculated viewId '/organization.jsf' from request servlet path
            2009-01-28 13:22:25,253 DEBUG JaHi [org.apache.myfaces.lifecycle.RestoreViewExecutor::execute(83)] Request is a postback
            2009-01-28 13:22:25,253 DEBUG JaHi [org.apache.myfaces.application.DefaultViewHandlerSupport::applyDefaultSuffix(213)] view id after applying the context suffix: /organization.xhtml
            2009-01-28 13:22:25,253 DEBUG JaHi [apache.myfaces.shared_impl.util.StateUtils::findAlgorithm(464)] Using algorithm DES
            2009-01-28 13:22:25,253 DEBUG JaHi [apache.myfaces.shared_impl.util.StateUtils::findAlgorithmParams(437)] Using algorithm paramaters ECB/PKCS5Padding
            2009-01-28 13:22:25,253 DEBUG JaHi [apache.myfaces.shared_impl.util.StateUtils::symmetric(366)] decrypting w/ DES/ECB/PKCS5Padding
            2009-01-28 13:22:25,253 DEBUG JaHi [org.ajax4jsf.application.AjaxStateHolder::getInstance(49)] Request for a view states holder instance
            2009-01-28 13:22:25,253 DEBUG JaHi [org.richfaces.component.UIComponentControl::setParent(135)] Called setParent for AjaxSupport component with parent : javax.faces.component.html.HtmlPanelGroup
            2009-01-28 13:22:25,253 DEBUG JaHi [org.richfaces.component.UIComponentControl::setParent(144)] Detect newly created component
            2009-01-28 13:22:25,253 DEBUG JaHi [org.ajax4jsf.component.AjaxRegionBrige::restoreState(243)] Restore State of UIAjaxComponent with Id _viewRoot
            2009-01-28 13:22:25,253 DEBUG JaHi [org.ajax4jsf.event.AjaxPhaseListener::afterPhase(69)] Process after phase RESTORE_VIEW(1)
            2009-01-28 13:22:25,253 DEBUG JaHi [org.apache.myfaces.lifecycle.LifecycleImpl::executePhase(118)] exiting RESTORE_VIEW(1) in org.apache.myfaces.lifecycle.LifecycleImpl
            2009-01-28 13:22:25,253 DEBUG JaHi [org.apache.myfaces.lifecycle.LifecycleImpl::executePhase(89)] entering APPLY_REQUEST_VALUES(2) in org.apache.myfaces.lifecycle.LifecycleImpl
            2009-01-28 13:22:25,253 DEBUG JaHi [org.ajax4jsf.event.AjaxPhaseListener::beforePhase(102)] Process before phase APPLY_REQUEST_VALUES(2)
            2009-01-28 13:22:25,253 DEBUG JaHi [org.ajax4jsf.renderkit.RendererBase::decode(71)] Start decoding of component _viewRoot with class org.ajax4jsf.component.AjaxViewRoot
            2009-01-28 13:22:25,253 DEBUG JaHi [org.ajax4jsf.renderkit.AjaxContainerRenderer::doDecode(150)] Decode ajax request status for _viewRoot
            2009-01-28 13:22:25,253 DEBUG JaHi [org.ajax4jsf.component.AjaxRegionBrige::setSubmitted(188)] Submitted AJAX request - Queue Event to AjaxListeners
            
            
            2009-01-28 13:22:33,534 DEBUG JaHi [org.ajax4jsf.event.AjaxPhaseListener::afterPhase(69)] Process after phase APPLY_REQUEST_VALUES(2)
            2009-01-28 13:22:33,534 DEBUG JaHi [org.apache.myfaces.lifecycle.LifecycleImpl::executePhase(118)] exiting APPLY_REQUEST_VALUES(2) in org.apache.myfaces.lifecycle.LifecycleImpl
            
            


            also using a modified version of javax.faces.component (see code below) shows, that the application gets confused with the ids.

            I've added this method at line 147 in the invokeOnComponent method
             public boolean invokeOnComponent(javax.faces.context.FacesContext context, String clientId, javax.faces.component.ContextCallback callback) throws javax.faces.FacesException
             {
             //java.lang.NullPointerException - if any of the arguments are null
             if(context == null || clientId == null || callback == null)
             {
             throw new NullPointerException();
             }
            
             //searching for this component?
             showMeWhatsGoingOn(clientId, this.getClientId(context)); //<- inserted by me
             boolean found = clientId.equals(this.getClientId(context));
             if(found)
             {
             try
             {
             callback.invokeContextCallback(context, this);
             } catch(Exception e)
             {
             throw new FacesException(e);
             }
             return found;
             }
             //Searching for this component's children/facets
             for (Iterator<UIComponent> it = this.getFacetsAndChildren(); !found && it.hasNext();){
             found = it.next().invokeOnComponent(context, clientId, callback);
             }
            
             return found;
             }
            private void showMeWhatsGoingOn(String paramClientId, String contextClientId) {
             // TODO Auto-generated method stub
             try {
            
             FileWriter writer = new FileWriter("C:\\test-jsf.log", true);
            
             writer.append(paramClientId + ", " + contextClientId + "\n");
             writer.flush();
             writer.close();
            
             } catch (IOException e) {
             // TODO Auto-generated catch block
             e.printStackTrace();
             }
             }
            

            This code produces the following log (in the real application):
            NOT working:
            customersForm:bupas:1:a4j-selectDetails, customersForm:bupas:2:j_id81
            customersForm:bupas:1:a4j-selectDetails, customersForm:bupas:j_id82
            customersForm:bupas:1:a4j-selectDetails, customersForm:bupas:j_id83
            customersForm:bupas:1:a4j-selectDetails, customersForm:bupas:2:a4j-selectDetails <-- searching for the wrong one!!
            customersForm:bupas:1:a4j-selectDetails, customersForm:bupas:2:j_id84
            customersForm:bupas:1:a4j-selectDetails, customersForm:bupas:2:j_id85
            customersForm


            working:
            customersForm:bupas:15:a4j-selectDetails, customersForm:bupas:15:j_id81
            customersForm:bupas:15:a4j-selectDetails, customersForm:bupas:j_id82
            customersForm:bupas:15:a4j-selectDetails, customersForm:bupas:j_id83
            customersForm:bupas:15:a4j-selectDetails, customersForm:bupas:15:a4j-selectDetails <-- founr the right one
            customersForm:bupas:15:a4j-selectDetails, _viewRoot
            
            




            • 3. Re: Bug: a4j:commandLinks not working inside t:datatable
              Jan Hinzmann Newbie

              Original from ilya_shaikovsky:

              we didn't tested our partiall processing feature with t:dataTable. Rich Faces dataTable uses invokeOnComponent from JSF 1.2 in order to process and update table partially - so it will works stable for such case for sure.


              Hi again,

              so there is no solution on this? I'm not sure if this is a bug in the richfaces/a4j implementation or in the tomahawk implementation. Any comments?

              Thx,
              Jan