1 2 Previous Next 27 Replies Latest reply on Jul 22, 2011 10:03 PM by Carlos juarez

    Troubles to use datascroller and ExtendedDataModel

    Bernix Ning Newbie

      I am trying to use the ExtendedDataModel with rich:dataScroller and rich:dataTable to display data, and want to load a part of data from database for every page, cuz' the data may be has about 1 million rows.


      At first, I found this two old discussions:


      old forum link 115636


      old forum link 411298


      and I then followe supernovasoftware's comment, created
      BaseExtendedDataModel.java
      AccessExtendedDataModel.java


      Added the datascroller and dataTable in my xhtml page.


      But I got a error said:


      java.lang.UnsupportedOperationException




      I tracked the log and found that the problem is, AccessExtendedDataModel.setRowIndex() is called.
      Why? it says this method is no more used....


      Well, I implement the setRowIndex() getRowIndex() methods, the datascroller shown, but when I click the button to navigate to other page, nothing happen, then I found that
      AccessExtendedDataModel.walk() is never called!
      Seam use the org.ajax4jsf.model.SequenceDataModel.walk(), and then SequenceDataModel.walk() just take AccessExtendedDataModel as its data...
      How can I make it work with AccessExtendedDataModel?



      Another question is, I want to reset the page data when user search, I found:
      old forum link 127277


      so I defined a org.richfaces.component.UIDatascroller field in my backing bean, added the getter and setter, then binding it to the datascroller in page, but I then got error says:


      java.lang.IllegalArgumentException: argument type mismatch




      well, I searched again, and found
      old forum link 117840


      My project is generated by Seam-gen and deployed as an ear, I tried to move my backing bean to WEB-INF/classes, but then I got error again:


      Target Unreachable identifier "mybackingbean" resolved to null..





      I have been fighting with it for 3 days, but just can not make it work....
      Does anyone make the pagination completely succeed? Can you help me?


      I am using
      JBoss 4.2.2.GA


      Seam 2.0.1.GA


      Richfaces 3.1.3.GA


      JDK5


      Thanks

        • 1. Re: Troubles to use datascroller and ExtendedDataModel
          Slava Davidovich Newbie

          But I got a error said:

          java.lang.UnsupportedOperationException

          I tracked the log and found that the problem is, AccessExtendedDataModel.setRowIndex() is called. Why? it says this method is no more used....

          This is true only if you have not errors on the page :)
          So, if you got this implement get/setrowIndex and getwrappeddata
          like this:


          @Override
               public int getRowIndex() {
                    return this.rowIndex;
               }
          
               /**
                * Get data which can be wrapped.
                * @return casted to DataModel object 
                */
               @Override
               public Object getWrappedData() {
                    return this;
               }
          
               @Override
               public void setRowIndex(int rowIndex) {
                    this.rowIndex = rowIndex;
               }
          
               /**
                * Really nothing inside, just rudiment.
                * @param obj - current data
                */
               @Override
               public void setWrappedData(Object obj) {
               }



          All will work fine :)



          Another question is, I want to reset the page data when user search

          I think you need map javax.faces.component.UIData for datatable and when you need to reset datascrollerscroller, just set UIData.setFirst(0) and refresh - all will ok.



          I tried to move my backing bean to WEB-INF/classes, but then I got error again

          try this:@Install where precedence is Install.FRAMEWORK, maybe hope.



          • 2. Re: Troubles to use datascroller and ExtendedDataModel
            Bernix Ning Newbie

            Thanks Slava, I follow your advice, but still got problems.


            1. the first time I enter the page, the datascroller shows correctly with the expected pages (e.g. 1 / 3), but NO rows show in dataTabel, I tracked to the log and found that the data had already retrieved from database, why the dataTable didn't show them?


            2. when I click Search button, I got the error:


            Caused by javax.el.PropertyNotFoundException with message: "/orgList.xhtml @50,26 binding="#{orgListBean.dataTable}": Target Unreachable, identifier 'orgListBean' resolved to null"




            Here're my codes:



            1. BaseExtendedDataModel.java


            import java.io.IOException;
            import java.util.ArrayList;
            import java.util.HashMap;
            import java.util.List;
            import java.util.Map;
            
            import javax.faces.context.FacesContext;
            
            import org.ajax4jsf.model.DataVisitor;
            import org.ajax4jsf.model.ExtendedDataModel;
            import org.ajax4jsf.model.Range;
            import org.ajax4jsf.model.SequenceRange;
            import org.jboss.seam.annotations.Logger;
            import org.jboss.seam.log.Log;
            
            public abstract class BaseExtendedDataModel<T extends BaseModel> extends
                      ExtendedDataModel
            {
                 @Logger private Log log;
                 int rowNum = -1;
                 int rowIndex;
                 public List<T> listRow;
                 private Integer currentId;
                 private Map<Integer, T> wrappedData = new HashMap<Integer, T> ();
                 private List<Integer> wrappedKeys;
                 private Integer rowCount; // better to buffer row count locally
            
                 public int getRowNum () {
                      return ++rowNum;
                 }
            
                 public abstract Integer getCount ();
                 public abstract List<T> getList (Integer firstRow, Integer maxResults);
                 public abstract T findById (Integer id);
            
                 public Integer getId (T row) {
                      Integer id = row.getId ();
                      return id;
                 }
            
                 public void wrap (FacesContext context, DataVisitor visitor, Range range,
                           Object argument, List<T> list) throws IOException {
                      wrappedKeys = new ArrayList<Integer> ();
                      wrappedData = new HashMap<Integer, T> ();
                      for (T row : list) {
                           Integer id = getId (row);
                           wrappedKeys.add (id);
                           wrappedData.put (id, row);
                           visitor.process (context, id, argument);
                      }
                 }
            
                 public boolean hasById (Integer id) {
                      for (T row : listRow) {
                           Integer rowId = getId (row);
                           if (rowId.equals (id)) {
                                return true;
                           }
                      }
                      return false;
                 }
            
                 @Override
                 public void walk (FacesContext context, DataVisitor visitor, Range range,
                           Object argument) throws IOException {
                      int firstRow = ((SequenceRange) range).getFirstRow ();
                      int maxResults = ((SequenceRange) range).getRows ();
                      log.info (">>>> walk with first row [#0], size [#1]", firstRow, maxResults);
                      wrap (context, visitor, range, argument, getList (firstRow, maxResults));
                 }
            
                 /*
                  * This method normally called by Visitor before request Data Row.
                  */
                 @Override
                 public void setRowKey (Object key) {
                      this.currentId = (Integer) key;
                 }
            
                 @Override
                 public int getRowCount () {
                      if (rowCount == null)
                           return (rowCount = getCount ()).intValue ();
                      else
                           return rowCount.intValue ();
                 }
            
                 @Override
                 public boolean isRowAvailable () {
                      if (currentId == null)
                      {
                           return false;
                      }
                      else
                      {
                           return hasById (currentId);
                      }
                 }
            
                 /**
                  * This is main way to obtain data row. It is intensively used by framework.
                  * We strongly recommend use of local cache in that method.
                  */
                 @Override
                 public Object getRowData () {
                      if (currentId == null) {
                           return null;
                      } else {
                           T ret = wrappedData.get (currentId);
                           if (ret == null) {
                                ret = this.findById (currentId);
                                wrappedData.put (currentId, ret);
                                return ret;
                           } else {
                                return ret;
                           }
                      }
                 }
            
                 // Unused rudiment from old JSF staff.
                 @Override
                 public int getRowIndex () {
                      return this.rowIndex;
                 }
            
                 @Override
                 public void setRowIndex (int rowIndex) {
                      this.rowIndex = rowIndex;
                 }
            
                 @Override
                 public Object getWrappedData () {
                      return this;
                 }
            
                 @Override
                 public void setWrappedData (Object data) {
                      // do nothing
                 }
            
                 // TODO if this is never called by the framework why is it necessary.
                 @Override
                 public Object getRowKey () {
                      return this.currentId;
                 }
            
            }




            2. OrganizationDataModel.java


            import java.util.List;
            
            public class OrganizationDataModel extends BaseExtendedDataModel<Organization> {
                 private OrganizationDao dao;
                 private Object conditions;
            
                 @Override
                 public Organization findById (Integer id) {
                      return dao.getOrganization (id);
                 }
            
                 @Override
                 public Integer getCount () {
                      return dao.queryTotalResultCount (conditions);
                 }
            
                 @Override
                 public List<Organization> getList (Integer firstRow, Integer maxResults) {
                      return dao.queryResults (conditions, firstRow, maxResults);
                 }
            
                 public void setDao (OrganizationDao dao) {
                      this.dao = dao;
                 }
            
                 public void setConditions (Object conditions) {
                      this.conditions = conditions;
                 }
            
            }




            3. OrganizationListBean.java


            import static org.jboss.seam.ScopeType.PAGE;
            
            import java.util.List;
            
            import javax.faces.component.UIData;
            
            import org.jboss.seam.annotations.Create;
            import org.jboss.seam.annotations.In;
            import org.jboss.seam.annotations.Install;
            import org.jboss.seam.annotations.Logger;
            import org.jboss.seam.annotations.Name;
            import org.jboss.seam.annotations.Scope;
            import org.jboss.seam.log.Log;
            
            @Name("orgListBean")
            @Scope(PAGE)
            @Install(precedence = Install.FRAMEWORK)
            public class OrganizationListBean
            {
            
                @Logger
                private Log log;
            
                 private Organization conditions;
            
                @In
                private OrganizationDao organizationDao;
            
                private UIData dataTable;
                
                private OrganizationDataModel datamodel;
            
                @Create
                public void init () {
                    conditions = new Organization ();
            
                    datamodel = new OrganizationDataModel ();
                    datamodel.setDao (organizationDao);
                    datamodel.setConditions (searchReseller);
                }
            
                public void search () {
                     if (dataTable != null)
                     {
                          log.info (">>>> searching...");
                          dataTable.setFirst (0);
                     }
                }
                 
                 public Organization getConditions () {
                      return conditions;
                 }
            
                 public void setConditions(Organization conditions) {
                      this.conditions = conditions;
                 }
            
                 public UIData getDataTable () {
                      return dataTable;
                 }
            
                 public void setDataTable (UIData dataTable) {
                      this.dataTable = dataTable;
                 }
            
                 public OrganizationDataModel getDatamodel () {
                      return datamodel;
                 }
            
            }
            



            4. BaseModel.java


            public abstract class BaseModel {
                 abstract public Integer getId();
                 
                 public BaseModel() {}
            
                 public String toString() {
                      return this.getClass().getName() + "[id=" + getId() + "]";
                 }
            
            }



            5. Organization.java


            @Entity
            public class Organization extends BaseModel implements java.io.Serializable
            {
                 private Integer id;
                 private String name;
            
                 // getters and setters
            }



            6. orgList.xhtml


                <h:form id="resellerSearch">
                        <s:decorate template="/layout/edit.xhtml">
                            <ui:define name="label">Enter name:</ui:define>
                            <h:inputText id="name" value="#{orgListBean.conditions.name}" />
                        </s:decorate>
                        <h:commandButton id="search" value="Search" action="#{orgListBean.search}"/>
                </h:form>
                </div>
            
                 <a4j:form>
                 <rich:datascroller for="organizationRecords" align="left"/>
                <rich:dataTable id="organizationRecords"
                     binding="#{orgListBean.dataTable}"
                     rows="10"
                     var="org"
                     value="#{orgListBean.datamodel}">
            
                      <rich:column>
                           <f:facet name="header">ID</f:facet>
                           <h:outputText value="#{org.id}" />
                      </rich:column>
            
                      <rich:column>
                           <f:facet name="header">Name</f:facet>
                           <h:outputText value="#{org.name}" />
                      </rich:column>
            
                </rich:dataTable>
                </a4j:form>



            • 3. Re: Troubles to use datascroller and ExtendedDataModel
              Jason Long Novice

              This is what I use.  It must be packaged in the war and called by an event.  You can only set it to the first page, but in 3.2 you can set it to any page number.



              @Name("datascrollerUI")
              public class BaseUIDatascroller implements Serializable
              {
              
              
                  private UIDatascroller datascroller;
                  public UIDatascroller getDatascroller() { return datascroller; }
                  public void setDatascroller(UIDatascroller datascroller) { this.datascroller = datascroller; }
              
                  @Observer("reset.pipe.search.datascroller")
                  public void resetDatascroller() 
                  {
                      datascroller.setPage(UIDatascroller.FIRST_FACET_NAME);
                  }
              }


              • 4. Re: Troubles to use datascroller and ExtendedDataModel
                Slava Davidovich Newbie

                1. the first time I enter the page, the datascroller shows correctly with the expected pages (e.g. 1 / 3), but NO rows show in dataTabel, I tracked to the log and found that the data had already retrieved from database, why the dataTable didn't show them?

                add @In to private UIData dataTable; or will be allways see 0 rows :(


                change scroller and datatable positions vice versa (datatable must be upper than scroller, otherways in does not correctly refresh data (must be fixed in 3.2.0 RF))


                about 2: try to change scope to conversation... must work with, but you will be needed to end/start conversation when you out/return to this page, possibly to end/start in on the search pressed.

                • 5. Re: Troubles to use datascroller and ExtendedDataModel
                  Bernix Ning Newbie

                  Hi Jason, thank you for your comment.


                  I created a class as you provided, but how to instantiate it?
                  add a @AutoCreate? or @In (create = true) in orgListBean ?
                  and what scope should it be?

                  • 6. Re: Troubles to use datascroller and ExtendedDataModel
                    Bernix Ning Newbie

                    Hi Slava, I modified the code to:


                    @Scope(CONVERSATION)
                    ...
                        @In(required = false)
                        @Out(required = false)
                        private UIData dataTable;
                    ...
                         @Begin
                         @Create
                         public void init () {...}
                         
                         @End
                         public void search() {...}



                    and also changed the datascroller's position.


                    but the same problems are still there...
                    in fact, even I click the > button in datascroller, the error Target Unreachable, identifier 'orgListBean' resolved to null raised as well...


                    I have tried to put the OrganizationListBean into WEB-INF/classes, copy a seam.properties file in WEB-INF/classes as well, just didn't work...


                    and also the jar directory(EJB jar floder), but just the same exception...


                    Is there other way or use other JSF UI component to implement the real pagination?


                    Actually I think RF should provide such a component because most of the developers want to do the real pagination.


                    Anyway, thanks a lot for your time, if I just can not figure it out, I have to implement the pagination myself - that's really trouble, we have many list pages need pagination...

                    • 7. Re: Troubles to use datascroller and ExtendedDataModel
                      Jaime Martin Apprentice

                      I find this datascroller with seam a really interesting and important issue.
                      I want to face the problem (lots of records with true pagination) with RichFaces 3.2.1 GA, Seam 2.0.2 GA and JBoss 4.2.2


                      Jason, the recentest version you have is what you have already posted in this thread and in the threads of the old forums or do you happen to have a newer one?
                      Pete, is there any official article or guideline regarding this issue?
                      Slava, did you find any solution to your problem?
                      thank you very much!

                      • 8. Re: Troubles to use datascroller and ExtendedDataModel
                        Alex Savitsky Novice

                        Personally, I found it easier not to bother with the RF datascroller, instead implementing my own. I'll post it here just in case someone with the need for true pagination might find it useful. Requires Facelets and nothing more.


                        First, you implement the following function, that creates a collection of SelectItems for a paging dropdown, similar to what Trinidad has:



                        public class Functions {
                             private static final int BLOCK_SIZE = 20;
                             public static List<SelectItem> pages(Query<?, ?> query) {
                                  if (query.getMaxResults() == null) {
                                       throw new RuntimeException(
                                                 "Cannot use this feature with non-paged queries");
                                  }
                                  int pageSize = query.getMaxResults();
                                  int pageCount = query.getPageCount();
                                  long recordCount = query.getResultCount();
                                  int blockCount = pageCount / BLOCK_SIZE
                                            + (pageCount % BLOCK_SIZE == 0 ? 0 : 1);
                                  int currentPage = (query.getFirstResult() == null ? 0
                                            : query.getFirstResult())
                                            / pageSize;
                                  int currentBlock = currentPage / BLOCK_SIZE;
                                  boolean isBlocking = pageCount > BLOCK_SIZE;
                                  boolean hasNextBlock = isBlocking ? currentBlock < (blockCount - 1)
                                            : false;
                                  boolean hasPrevBlock = isBlocking ? currentBlock > 0 : false;
                                  if (isBlocking) {
                                       pageCount = BLOCK_SIZE;
                                  }
                                  List<SelectItem> pages = new ArrayList<SelectItem>();
                                  long pageStart = isBlocking ? currentBlock * pageSize * BLOCK_SIZE : 0;
                                  if (hasPrevBlock) {
                                       pages.add(new SelectItem(
                                                 (currentBlock - 1) * pageSize * BLOCK_SIZE, "Previous..."));
                                  }
                                  for (long i = 0; i != BLOCK_SIZE; i++) {
                                       long pageEnd = Math.min(pageStart + pageSize, recordCount);
                                       String label = (pageStart + 1) + "-" + pageEnd + " of "
                                                 + recordCount;
                                       pages.add(new SelectItem(pageStart, label));
                                       pageStart += pageSize;
                                       if (pageStart > recordCount) {
                                            break;
                                       }
                                  }
                                  if (hasNextBlock) {
                                       pages.add(new SelectItem(
                                                 (currentBlock + 1) * pageSize * BLOCK_SIZE, "More..."));
                                  }
                                  return pages;
                             }
                        }
                        



                        Then, a Facelet component, that takes an EntityQuery parameter, and display pagination controls for it:



                        <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
                             "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
                        <ui:component xmlns="http://www.w3.org/1999/xhtml"
                             xmlns:ui="http://java.sun.com/jsf/facelets"
                             xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core"
                             xmlns:s="http://jboss.com/products/seam/taglib" xmlns:c="http://component/jsf">
                             <s:div rendered="#{query.pageCount gt 1}">
                                  <h:commandLink value="Prev #{query.maxResults}"
                                       disabled="#{not query.previousExists}" action="#{query.previous}" />
                                  <h:outputText value="&#160;" />
                                  <h:selectOneMenu value="#{query.firstResult}" onchange="this.form.submit()">
                                       <f:selectItems value="#{c:pages(query)}" />
                                  </h:selectOneMenu>
                                  <h:outputText value="&#160;" />
                                  <h:commandLink value="Next #{query.maxResults}"
                                       disabled="#{not query.nextExists}" action="#{query.next}" />
                             </s:div>
                        </ui:component>
                        



                        Now all you have to do is declare both the functionand the component in Facelets taglib.xml. I find it easiest to place this file in META-INF in your classpath, and the component XHTML in a subfolder underneath.


                        <?xml version="1.0"?>
                        <!DOCTYPE facelet-taglib PUBLIC
                          "-//Sun Microsystems, Inc.//DTD Facelet Taglib 1.0//EN"
                          "https://facelets.dev.java.net/source/browse/*checkout*/facelets/src/etc/facelet-taglib_1_0.dtd">
                        <facelet-taglib>
                             <!-- Use your own namespace, of course, just make sure to change it everywhere -->
                             <namespace>http://components/jsf</namespace>
                             <function>
                                  <function-name>pages</function-name>
                                  <!-- Use whatever package the Functions class is in -->
                                  <function-class>some.package.Functions</function-class>
                                  <function-signature>
                                       <![CDATA[java.util.List pages(org.jboss.seam.framework.Query)]]>
                                  </function-signature>
                             </function>
                             <tag>
                                  <tag-name>rangeNavigator</tag-name>
                                  <!-- This assumes that the component file is rangeNavigator.xhtml, in META-INF/tags under your classpath -->
                                  <source>tags/rangeNavigator.xhtml</source>
                             </tag>
                        </facelet-taglib>



                        Now to add pagination to a dataTable, you do:


                        <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
                             "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
                        <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html"
                             xmlns:f="http://java.sun.com/jsf/core" xmlns:c="http://components/jsf">
                        <body>
                        <h:form>
                             <h:dataTable value="#{items.resultList}" var="item" rows="#{items.maxResults}">
                                  <f:facet name="footer">
                                       <c:rangeNavigator query="#{items}" />
                                  </f:facet>
                                  <h:column>
                                       <h:outputText value="#{item.name}" />
                                  </h:column>
                             </h:dataTable>
                        </h:form>
                        </body>
                        </html>
                        



                        Doesn't have to be in table footer, can be anywhere on the page. The items is a Query (JPA or Hibernate, your choice) with maxResults defined.


                        The result is a pagination control that is built with only standard JSF components (s:links or no page parameters involved), works off nothing but a Query component, requires only Facelets to work, and truly feels like a component.


                        Feedback welcome.


                        Thanks,


                        Alex

                        • 9. Re: Troubles to use datascroller and ExtendedDataModel
                          Franco Fernandes Novice

                          Thanks for posting this.
                          I implemented your solution, though I am not sure if I see it behaving any differently than the Richfaces datascroller.


                          I set maxResults to 10 and when I click link for next 10 or select it from the list, it looks like the entire query fires again. So is there really a gain ?
                          Besides, the richfaces datascroller is a Ajax component so there is no complete page refresh when paging controls are clicked.



                          Regards
                          Franco (newbie)


                          • 10. Re: Troubles to use datascroller and ExtendedDataModel
                            Alex Savitsky Novice

                            Shouldn't be the entire query - if your DB supports limits (most do), then it should only select the 10 visible items (look closely at the generated SQL), not, say all 1 million of them :) That's the main idea behind this component - lazy paging for large datasets.


                            And speaking of AJAX, I had so much negative experience with it, I don't even view it as an advantage anymore. Say, if there's something wrong with retrieving the next dataset page, you'll get an error message with the full page refresh - but with AJAX, you won't get anything, the control will simply stop working... good luck debugging that.

                            • 11. Re: Troubles to use datascroller and ExtendedDataModel
                              Franco Fernandes Novice

                              You are right, the query does limit the rows fetched on paging. I use an Oracle 10G database.


                              I also admit that I have seen seen issues with paging large datasets with Ajax calls.


                              The application that I am trying to re-engineer with Seam/Hibernate was built using the SOFIA J2EE framework (now obsolete). This is currently running in my production environment with some very happy users.


                              If I have to save my job, I need the bells and whistles of Ajax.


                              If someone else can chip in and share their experiences ...



                              Thanks
                              Franco



                              • 12. Re: Troubles to use datascroller and ExtendedDataModel
                                David Sheth Newbie

                                As I mentioned elsewhere, I managed to get sorting and paginating using the database working together with richfaces, and wrote up how I did so on my blog.  You can see my post here .

                                • 13. Re: Troubles to use datascroller and ExtendedDataModel
                                  Jaime Martin Apprentice

                                  thank you Alex, Jason and David for your proposals. All of them seem to be really interesting. I´ll have to decide which one to follow, but that´s a minor problem. I wish I had always several options!!

                                  • 14. Re: Troubles to use datascroller and ExtendedDataModel
                                    Franco Fernandes Novice

                                    David, thank you for posting this code. I'm going to give it a try only because I'm so stuck on using the datascroller. I might need help writing the code for IFormsService.findFolders method. I assume it is a createQuery call with parameters for setFirstResult, setMaxResults ?
                                    I don't see the source of parameter descending in the walk method.


                                    If I need further help, I will post comments on your page.


                                    If this does not work, then I think I will put my mind at ease and go with Alex's custom facelets component.


                                    Thanks to everyone else on this discussion.



                                    Franco

                                    1 2 Previous Next