7 Replies Latest reply on Oct 26, 2011 8:14 AM by niss

    a4j:jsFunction and "row is unavailable" problem

    niss

      Hello everybody,

       

      I have a problem when I use a4j:jsFunction and a datatable.

       

      Functionaly

      I have a page with an input field and a search link. You must press [Enter] to submit the form. The search results are displayed just below the input field. When something is entered into this field, a message should appear saying that the input field has been modified and that the displayed results do not match anymore against the search criteria.

       

       

      The problem

      When you quickly modify the input field content and hit [Enter], sometimes, a NullPointerException happens. I guess it because the Ajax call is not completely finished and is maybe modifying my bean.

       

      It works great when I'm waiting a few seconds.

       

       

      I think this is the most relevant part of the stacktrace :

       

      java.lang.IllegalArgumentException: row is unavailable

      at javax.faces.model.ListDataModel.getRowData(ListDataModel.java:66)

      at org.ajax4jsf.model.SequenceDataModel.getRowData(SequenceDataModel.java:147)

      at org.ajax4jsf.component.UIDataAdaptor.getRowData(UIDataAdaptor.java:242)

      at be.cm.apps.prest.beans.prestations.SearchPrestationBean.getCurrentInteger(SearchPrestationBean.java:111)

       

       

      To get more into the details :

       

      The input field has an onkeyup event attached to it, calling a 'pure' Javascript function called 'addSynchronizationErrorMessage()'. This function is itself calling another Javascript function called 'addSynchronizationErrorMessageAjaxCall()'. The latter is not something I wrote myself : it is a Javascript function generated by Richfaces a4j:jsFunction tag.

       

      Below you'll find the codes. I tried to break it down to the minimal code so that it is easy to reproduce the problem. I've read things I could do to eliminate the problem (ignoreDupResponse, limitToList, a4j:form, ...) but nothing worked so far

       

      We are using the following libraries : MyFaces 1.1.5, Tomahawk 1.1.9, jQuery 1.5.2, RichFaces 3.1.6.

       

      This is the .jsp code :

       

      <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
      <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h"%>
      <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f"%>
      <%@ taglib uri="http://richfaces.org/rich" prefix="rich"%>
      <%@ taglib uri="http://richfaces.org/a4j" prefix="a4j"%>
      <%@ taglib uri="http://myfaces.apache.org/tomahawk" prefix="t"%>
      
      <f:view>
          <f:loadBundle var="msg" basename="be.apps.bundle.messages"/>
      
          <html>
              <body onkeydown="return search(event)" onload="setFocusOnLoadInScreen();">    
                  <h:form id="actionForm0">
                      <t:div id="messagesPanel">
                          <t:messages id="errorMessage" globalOnly="false" showDetail="false" forceIdIndex="true"/>
                      </t:div>
      
                      <a4j:jsFunction name="addSynchronizationErrorMessageAjaxCall" 
                          action="#{searchPrestationBean.addDivergenceBetweenSearchCriteriaAndResultsMessage}" 
                          reRender="actionForm0:messagesPanel" 
                          immediate="true" />
      
                      <h:inputText id="origin" value="#{searchPrestationBean.origin}" onkeyup="addSynchronizationErrorMessage();" />
                      <t:commandLink id="defaultButton" value="#{msg.global_search}" action="#{searchPrestationBean.search}"  />
      
                      <rich:dataTable
                          id="dataTableInfoPanels"
                          value="#{searchPrestationBean.integers}"
                          binding="#{searchPrestationBean.dataTable}">
      
                          <rich:column>    
                              <t:htmlTag value="nobr">
                                  <h:outputText value="#{searchPrestationBean.displayInfo}" />
                              </t:htmlTag>
                          </rich:column>        
                      </rich:dataTable>
                  </h:form>
              </body>
          </html>
      </f:view>
      

       

       

      And this is the bean code :

       

      import java.util.Arrays;
      import java.util.List;
      import java.util.Random;
      
      import org.richfaces.component.html.HtmlDataTable;
      
      import be.cm.apps.prest.faces.beans.AbstractScreenBean;
      
      public class SearchPrestationBean extends AbstractScreenBean {
      
          private List<Integer> integers;
          private HtmlDataTable dataTable;
          private String origin;
      
          private Integer randomInteger;
      
          public SearchPrestationBean() {
          }
      
      
          /**
           * {@inheritDoc}
           */
          @Override
          public String initPage() {
              randomInteger = null;
              return "searchPrestation";
          }
      
          public void addDivergenceBetweenSearchCriteriaAndResultsMessage() {
                  getCommonHelper().addMessage("validation.error.searchAndResultSynchronization");
          }
      
          public String search() { 
              Random random = new Random();
              setIntegers(Arrays.asList(new Integer(random.nextInt())));
              return null;
          }
      
          public String getOrigin() {
              return this.origin;
          }
      
          public void setOrigin(String origin) {
              this.origin = origin;
          }
      
          public String getDisplayInfo() {
              randomInteger = getCurrentInteger();
              return randomInteger.toString();
          } 
      
          public HtmlDataTable getDataTable() {
              return this.dataTable;
          }
      
          public void setDataTable(HtmlDataTable dataTable) {
              this.dataTable = dataTable;
          }  
      
          public Integer getCurrentInteger() { 
              if (dataTable != null) {
                  return (dataTable.getRowCount() != 0 ? (Integer) dataTable.getRowData() : null);
              }
              return null;
          }
      
          public List<Integer> getIntegers() {
              return this.integers;
          }
      
          public void setIntegers(List<Integer> integers) {
              this.integers = integers;
          }
      

       

      Tell me if you need more information.

       

      Thank you in advance for your answer.

       

      Nicolas.

        • 1. Re: a4j:jsFunction and "row is unavailable" problem
          mcmurdosound

          You could add an additional nullpointer check:

           

                  if (dataTable != null && dataTable.getRowData() != null) {
                      return (dataTable.getRowCount() != 0 ? (Integer) dataTable.getRowData() : null);
                  }

          • 2. Re: a4j:jsFunction and "row is unavailable" problem
            niss

            Christian,

             

            Thank you for your answer !

             

            Unfortunately, the problem is still there. It looks like something is happening between the null check and the return of the data.

             

            Note that if it has worked, some lines might be not displayed, and that would be another problem.

             

            Nicolas.

            • 3. Re: a4j:jsFunction and "row is unavailable" problem
              mp911de

              Hi Nicolas,

              i have an idea for a hack, which should prevent the sync problem: Build a Servlet Filter and Sync on the SessionId, so each request to jsf-resources has to pass the filter sequentially. Not nice, but it should help to create a defined state and it prevents two requests which concurrently change data...

               

              Best regards,

              Mark

               

              PS: Message me, for a how-to.

              • 4. Re: a4j:jsFunction and "row is unavailable" problem
                mcmurdosound

                For me this should be quite close to your needs:

                 

                <h:form id="myform">

                <h:inputText id="searchField" value="#{manager.testString}" onkeypress="doSearch(event)"/>

                <a4j:commandButton id="doSearchLink" value="Search" action="#{manager.doSearch}" reRender="mylist" oncomplete="alreadySearched = true;"/>

                 

                <rich:dataTable id="mylist" value="#{manager.someIntegers}" var="item">

                <rich:column>

                #{item}

                </rich:column>

                </rich:dataTable>

                 

                </h:form>

                 

                <script type="text/javascript">

                      var alreadySearched = false;

                      function doSearch(event){         

                        // on enter: start search

                        if (alreadySearched){

                            alert("you've already searched!");

                            alreadySearched = false;

                        }

                        if (event.which == "13") {

                          event.preventDefault();

                          document.getElementById('myform:doSearchLink').click();

                        }

                      }

                      </script>

                1 of 1 people found this helpful
                • 5. Re: a4j:jsFunction and "row is unavailable" problem
                  niss

                  Mark,

                   

                  Indeed, the idea of using a filter to solve this bug seems not really nice to me too. If I can't get a better solution, I'll come back to you for the how-to.

                   

                   

                  Christian,

                   

                  I did something similar in the beginning. The problem is that our application is multilingual and when you switch from one language to the other, the error message is lost. I guess I could keep a trace of this message (using f:param or something like that) but I found this dirty, because you have to :

                  - manage to add the message yourself in Javascript and handle the problems related to other messages that might be added ;

                  - modify the bean that manage the languages just to solve a bug.

                   

                   

                  Isn't just something wrong in my code the way I'm using the a4j tags ?

                   

                  Thank you for your answers.

                   

                  Nicolas.

                  • 6. Re: a4j:jsFunction and "row is unavailable" problem
                    mp911de

                    Example Code attached.

                    • 7. Re: a4j:jsFunction and "row is unavailable" problem
                      niss

                      Mark,

                       

                      It seems it's working !

                       

                      Thank you all for your help.

                       

                      Nicolas.