3

I'm trying to use a Datascroller with a LazyDataModel and the load method from lazy data model is getting called twice.

Appart from thinking that it is not so good to call the load method multiple times (that may perform costly server/DB roundtrips), since my lazy data model not idempotent (meaning, two consecutive calls to the model on the same window/page size returns different outcomes) the fact that it's being called twice means: the presented results are not correct.

Is it normal for the load method in datascroller to be called twice? If so, any workarounds suggested for my alternative to work correctly? (appart from changing from statefull to stateless data model)

Using Primefaces 5.1, JSF2, Glassfish 4.1

nuno
  • 1,771
  • 1
  • 19
  • 48
  • I'm not an expert, but I fail to understand what statefull vs stateless has to do with this (and even think it is the other way around). Secondly WHEN is it called multiple times. Moving the mouse at the bottom? Then check the PF releasenotes, a fix for a very related issue has been introduced in Elite releases. – Kukeltje Mar 27 '15 at 14:55
  • The error happens right on page load, i.e., on component render phases. As I debug through PF source code (DataScrollerRenderer) I saw that the ``loadChunk`` method is effectively called twice when dealing with lazy. Going to try and provide a changed version of a DataScrollerRenderer – nuno Mar 27 '15 at 14:58
  • And btw, statefull, in my case, means that calling load multiple times is unsafe - the data returned in the first call is different from the second call. If it returned the same data there would be no problem - appart from performing redundant db calls – nuno Mar 27 '15 at 15:01
  • That is more [indempotent](http://stackoverflow.com/questions/19320115/what-does-idempotent-method-mean-and-what-are-the-side-effects-in-case-of-callin) related than stateful/stateless. I would assume the parameters you get in your load method are the identical both time, so then you **should** return the same result. And if they are but you do not use them (or do not have the previous ones) then you have a stateless component now and can only fix this when creating a stateful one (or do I miss something). And creating/overriding the renderer seems a fix while the actual problem is still there – Kukeltje Mar 27 '15 at 15:18
  • I changed the statefull term to *not ìdempotent* – nuno Mar 27 '15 at 15:31

2 Answers2

4

No, this is not normal. This is indeed a bug in PrimeFaces. We also discovered it a while ago when using it at zeef.com. We bypassed it by creating a custom renderer extending DataScrollerRenderer and overriding only encodeMarkup() method with the original implementation copypasted and then only the following piece outcommented:

// BUGFIX: outcommented as this is already done in loadChunk() later on.
/*if(ds.isLazy()) {
    loadLazyData(ds, 0, chunkSize);
}*/

You can get it to run by registering it as below in webapp's faces-config.xml:

<render-kit>    
    <renderer>
        <component-family>org.primefaces.component</component-family>
        <renderer-type>org.primefaces.component.DataScrollerRenderer</renderer-type>
        <renderer-class>com.example.YourDataScrollerRenderer</renderer-class>
    </renderer>
</render-kit>
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • The header facet is not correctly rendered if the first load (commented section) does not run. Will provide an alternative implementation bellow – nuno Mar 27 '15 at 16:18
  • Right, we aren't using the header facet. Your implementation looks reasonable. – BalusC Mar 27 '15 at 16:33
1

Since the header facet, in BalusC post, is not correctly rendered if the first load (commented section) does not run, a slightly different implementation is needed

public class DataScrollerRenderer2 extends DataScrollerRenderer {

    @Override
    protected void encodeMarkup(FacesContext context, DataScroller ds, int chunkSize) throws IOException {
        // ... 
        boolean alreadyLoaded = false;
        if (ds.isLazy()) {            
            alreadyLoaded = true;
            loadLazyData(ds, 0, chunkSize);
        }
        // ... 
        loadChunk(context, ds, 0, chunkSize, alreadyLoaded);
        // ... 
    }

    @Override
    protected void loadChunk(FacesContext context, DataScroller ds, int start, int size) throws IOException {
        loadChunk(context, ds, start, size, false);
    }

    private void loadChunk(FacesContext context, DataScroller ds, int start, int size, boolean alreadyLoaded) throws IOException {
        // ... 
        if (ds.isLazy() && !alreadyLoaded) {
            loadLazyData(ds, start, size);
        }
        // ... 
    }

}

Not sure if this is the best implementation, but it worked.

EDIT: an issue has been filed in PrimeFaces GitHub

nuno
  • 1,771
  • 1
  • 19
  • 48