ok, I forgot a small fix which I did today:
@SuppressWarnings(value={"unchecked"})
        private Object getObjectFromCache() {
                if (_cacheWindow.size() == 0)
                        return null;
                if(getRowIndex() - _cacheWindowPos >= _cacheWindow.size())
                        refillCache();
return _cacheWindow.get(Math.min(_cacheWindow.size()-1, Math.max(0, getRowIndex() - _cacheWindowPos)));
        }


replace the same method below with the fixed one here



Werner Punz wrote:
Joao Bortoletto wrote:

Hi friends,

    Following your suggestions I built my own
DataModel...
    (Werner, you warned me about "a" or "b" ways...
Option "b" really seemed usefull, but I chose "a"
because I have a rope rounding my neck... :-D)
        Ok... now I have my own model, but it is static
yet. So... How can I access other data from controller
or view from it?
Let me explain better... I'm working with a managed bean and its gives data
to a x:dataTable through a "getList" method. These
data may depends on criteria inside a request, for
example... how can I achieve this task?
        Is there another way to accomplish it?

Yes, you dont have to serve an entire list... you can implement your own Datamodel and
have the datamodel itself serving it...

Here is a rather huge example from my code which has not really publish quality yet:

note as you can see the code is rather overly complicated the reason for this is sort of the prefetching of single pages, with a pageview controller I probably could have skipped half the lines of code, hence, I warned against approach a of prefetching:

Explanation follows below the code:

public class HibernateDataModel extends DataModel implements ICacheObject {

    // define a cachingWindow to keep the number of
    // connections/accessno at a sane level
    // from time to time an isolation level error
    // can occur since we seal the objects off
    // and then close the connection
    // so no real data integrity can be performed that way
    // but it should be good enough for the average webapp
    int                _cacheWindowSize    = 30;
    // array list due to the fact that this one has the fastes
    // absolute access which is necessary for paging
    List            _cacheWindow        = new ArrayList(_cacheWindowSize);
    // helper idcache for faster access
    Map                _idCache            = new TreeMap();
    // the cache window Position relative to the 0 absolute
    int                _cacheWindowPos        = 0;
    boolean            _cacheInvalidated    = true;
    // the standard variables which are defined by the system
    int                _rowCount            = -1;
    int                _rowIndex            = -1;
    // the associated query accessor
    IDataAccessor    _queryDelegate        = null;

    public void resetModel() {
        _rowCount = -1;
        _rowIndex = -1;
        invalidateCache();
        refillCache();
    }

    /**
     *
     */
    public HibernateDataModel(IDataAccessor queryDelegate) {
        super();
        setQueryDelegate(queryDelegate);
        // refillCache();
    }

    public HibernateDataModel() {
        super();
    }

    /*
     * (non-Javadoc)
     *
* @see javax.faces.model.DataModel#getRowCount() Return the number of rows * of data objects represented by this DataModel. If the number of rows
     *      is unknown, or no wrappedData is available, return -1.
     */
    public int getRowCount() {
        if (_rowCount == 0) {
            return -1;
        }
        return _rowCount;
    }

    /**
     * setter accessed by the query object
     *
     * @param rowCount
     */
    public void setRowCount(int rowCount) {
        this._rowCount = rowCount;
        // if(_rowIndex >= _rowCount-1)
        // _rowIndex = -1;
    }

    // returns true if the dataset is within
    boolean isCached() {
        if (_cacheWindow == null || _cacheWindow.size() == 0)
            return false;
        else
return ((_cacheWindowPos <= getRowIndex()) && (getRowIndex() < (_cacheWindowPos + _cacheWindow.size())));
    }

    /**
* refills the cache window upon the given row index this one in the long
     * term will possible be called from outside also
     */
    @SuppressWarnings(value={"unchecked"})
    public void refillCache() {
// calculate a decent new cache window pos, if possible with the current
        // row index
        // in the middle
_cacheWindowPos = Math.max(0, getRowIndex() - (_cacheWindowSize / 2));
        // refill the cache with the query
        if (_cacheWindowSize > 0) {
_cacheWindow = _queryDelegate.queryDatasets(_cacheWindowPos, _cacheWindowSize);
            _idCache = new TreeMap();
for (Object elem : _cacheWindow) {
                _idCache.put(((BaseDatabaseObject) elem).getId(), elem);
            }
            setRowCount(_queryDelegate.getSize() );
setRowIndex(Math.min(Math.max(_queryDelegate.getSize()-1, 0), getRowIndex())); NavigationBean navBean2 = (NavigationBean) JSFUtil.getManagedBean(Constants.MBEAN_NAVBEAN); navBean2.setCurrentNavSelection(_cacheWindow);
        }
        _cacheInvalidated = false;
    }

    /*
     * (non-Javadoc)
     *
     * @see jsf.common.ICacheObject#invalidateCache()
     */
    public void invalidateCache() {
        _cacheInvalidated = true;
    }

    /*
     * (non-Javadoc)
     *
     * @see javax.faces.model.DataModel#getRowData() Return an object
* representing the data for the currenty selected row index. If no
     *      wrappedData is available, return null.
     */
    public Object getRowData() {
        // synchronize this just in case we implemnt
        // an internal cache dumping mechanism
        // for hit fault reduction
        try {
            synchronized (_cacheWindow) {
                // fetch a cached object
                if ((!_cacheInvalidated) && isCached() ) {
                    return getObjectFromCache();
                }
                // reload the cache and get the object
                refillCache();
                return getObjectFromCache();
            }
        } catch (RuntimeException ex) {
            invalidateCache();
            throw ex;
        }
    }

    /**
     * @return
     */
    @SuppressWarnings(value={"unchecked"})
    private Object getObjectFromCache() {
        if (_cacheWindow.size() == 0)
            return null;
        //TODO range check problem sometimes
return _cacheWindow.get(Math.min(_cacheWindow.size(), Math.max(0, getRowIndex() - _cacheWindowPos)));
    }

    /*
     * (non-Javadoc)
     *
* @see javax.faces.model.DataModel#getRowIndex() Return the zero-relative
     *      index of the currently selected row. If we are not currently
     *      positioned on a row, or no wrappedData is available, return -1.
     */
    public int getRowIndex() {
        return _rowIndex;
    }

    /*
     * (non-Javadoc)
     *
     * @see javax.faces.model.DataModel#getWrappedData() Return the object
     *      representing the data wrapped by this DataModel, if any.
     */
    public Object getWrappedData() {
        // TODO Auto-generated method stub
        return null;
    }

    /*
     * (non-Javadoc)
     *
     * @see javax.faces.model.DataModel#isRowAvailable() Return a flag
     *      indicating whether there is rowData available at the current
     *      rowIndex. If no wrappedData is available, return false.
     */
    public boolean isRowAvailable() {
        return getRowIndex() < getRowCount();
    }

    /*
     * (non-Javadoc)
     *
* @see javax.faces.model.DataModel#setRowIndex(int) Set the zero-relative * index of the currently selected row, or -1 to indicate that we are * not positioned on a row. It is possible to set the row index at a * value for which the underlying data collection does not contain any * row data. Therefore, callers may use the isRowAvailable() method to
     *      detect whether row data will be available for use by the
* getRowData() method. If there is no wrappedData available when this
     *      method is called, the specified rowIndex is stored (and may be
* retrieved by a subsequent call to getRowData()), but no event is * sent. Otherwise, if the currently selected row index is changed by * this call, a DataModelEvent will be sent to the rowSelected() method
     *      of all registered DataModelListeners.
     */
    public void setRowIndex(int arg0) {
        _rowIndex = arg0;
    }

    /*
     * (non-Javadoc)
     *
* @see javax.faces.model.DataModel#setWrappedData(java.lang.Object) Set the * object representing the data collection wrapped by this DataModel.
     *      If the specified data is null, detach this DataModel from any
* previously wrapped data collection instead. If data is non-null, the
     *      currently selected row index must be set to zero, and a
     *      DataModelEvent must be sent to the rowSelected() method of all
     *      registered DataModelListeners indicating that this row is now
     *      selected.
     */
    public void setWrappedData(Object arg0) {
        // TODO Auto-generated method stub
    }

    /**
     * sets a query delegate, use a cached one if wanted/needed
     *
     * @param queryDelegate
     * @param useCached
     */
    @SuppressWarnings(value={"unchecked"})
public void setQueryDelegate(IDataAccessor queryDelegate, boolean useCached) {
        Object tmpQueryDelegate = null;
        if (useCached) {
tmpQueryDelegate = JSFUtil.getSessionMap().get(createQueryKey(queryDelegate));
            if (tmpQueryDelegate == null) {
JSFUtil.getSessionMap().put(createQueryKey(queryDelegate), queryDelegate);
            } else {
                queryDelegate = (IDataAccessor) tmpQueryDelegate;
            }
        }
        setQueryDelegate(queryDelegate);
    }

    public IDataAccessor getQueryDelegate() {
        return _queryDelegate;
    }

    /**
     * @param queryDelegate
     * @return
     */
    private String createQueryKey(IDataAccessor queryDelegate) {
return "QUERY" + this.getClass().getName() + queryDelegate.getClass().getName();
    }

    /**
     * query delegate setter
     *
     * @param queryDelegate
     */
    public void setQueryDelegate(IDataAccessor queryDelegate) {
        _queryDelegate = queryDelegate;
        _queryDelegate.setDataModel(this);
    }

    /**
     * @param mode
     */
    public void setOrderMode(String mode) {
        _queryDelegate.setOrderMode(mode);
    }

    /**
     * returns an object from the page cache just to be sure
     *
     * @param id
     * @return
     */
    @SuppressWarnings(value={"unchecked"})
    public BaseDatabaseObject getPageObjectFromId(Integer id) {
        return (BaseDatabaseObject) _idCache.get(id);
    }

    /**
     * @return Returns the _cacheWindow.
     */
    public List get_cacheWindow() {
        return _cacheWindow;
    }

    /**
     * @param window
     *            The _cacheWindow to set.
     */
    public void set_cacheWindow(List window) {
        _cacheWindow = window;
    }
}



What happens here, that an unnecessarily complicated datamodel prefetches the entire page window into a local cache with criteria predefined by a query delegate object and serves the data row by row
into the current datamodel. The positional state is set by the datamodel
via callbacks so that once a cache boundary is hit, the caching windows is refreshed.
As I said that is my approach (not quite publish quality yet, but works)

If you implement the same approach via a callback mechanism which is called before and after rendering, you probably can implement the same in around 30 lines of code, you just have to aquire the resources for the db access before rendering, then open a session start the query, get some kind of db-point/iterator on the query position it at the first set rowindex call and then serve it row by row, you can be pretty sure that the data itself once the first row index is in place is called sequentially.

At the end of rendering, you can release the resources close the transactions and free the session. Note that the code above was written 5 months ago, at a time when I neither was aware of the Script Phase Listeners nor of Servlet filters, nor of the stuff clanahan is doing with the PageView controllers in Shale. I had to work around that, and basically implemented the buggy, and not very satisfying but working code above during a learning phase.
But I am posting it anyway, to give you the idea on how to do such things.




Reply via email to