Session-enabled display tag library
-----------------------------------

         Key: DISPL-263
         URL: http://jira.codehaus.org/browse/DISPL-263
     Project: DisplayTag
        Type: New Feature
  Components: Paging/Sorting  
    Versions: 1.1    
    Priority: Minor
 Attachments: ParamEncoder.java, TableTag.java

                                                                                
Session-enabled display tag library
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
The display tag library is not session-enabled i.e., the sorting and 
paginnation is not preserved across the session. Sometimes its desirable that 
the previously selected sorting or page is shown again to the user when the 
user revists a page. To make display tag library session-aware we did the 
following changes:

org.displaytag.tags.TableTag class
---------------------------------------------

The initParameters( ) method of TableTag class encodes the current page number, 
sort column index and sort order and stores it in instance variables. These 
instance variables form part of the request URL to ensure that the sorting ( 
ascending / descending ) is preserved across multiple interaction with the 
table shown on the web page. To encode current page number, sort column index 
and sort order, the TableTag class makes use of the encodeParameter ( .. ) 
method, which accepts a prefix to be used when encoding a parameter. The 
following code from org.displaytag.tags.TagTagParameters class shows the values 
that are passed to the encodeParameter ( ... )method:

    /**
     * name of the parameter specifying the current sorted column index.
     */
    public static final String PARAMETER_SORT = "s"; //$NON-NLS-1$

    /**
     * name of the parameter specifying the current page number.
     */
    public static final String PARAMETER_PAGE = "p"; //$NON-NLS-1$

    /**
     * name of the parameter specifying the current sorting order.
     */
    public static final String PARAMETER_ORDER = "o"; //$NON-NLS-1$

The encodeParameter (...s) method makes use of org.displaytag.util.ParamEncoder 
class to generate unique parameter names. The contructor of ParamEncoder class 
accepts a unique id. This unique id is nothing but the uid attribute of 
<display:table> tag. The display tag documentation mentions that two tables on 
the same page MUST NOT have same uid, because if you keep them same then it 
won't be possible for the display tag library to distinguish between the two 
tables. 

If two tables on the same web page have same uid then the actions taken on one 
table will apply on the other table also because display tag won't be able to 
distinguish between the two tables. If the uid for two tables ( on the same web 
page ) are different then the encodeParameter method will create different 
unique parameter names because it makes use of uid attribute when generating 
parameter names.

Note: We earlier modified the display tag library to fix problem related to 
pagination because of which our first page is designated as 1. If you are using 
the fixes provided by display tag team then instead of 1, use 0 before using 
the code below.
When using display tag you may have seen something like the following in the 
URL:

d-49653-p=1&d-49653-o=2&d-49653-s=2

d-49653-p - encoded parameter corresponding to the current page number
d-49653-o - encoded parameter corresponding to the sort order
d-49653-s - encoded parameter corresponding to the sort column

The p, o and s in the encoded parameter comes from the constants defined in 
TableTagParameters ( see above ).

Problems involved in making display tag library session-aware

•       Adding the encoded parameter name and value into the session
•       Lets say a user moves from web page A ( which contains table T1 ) to a 
web page B ( which contains table T2 ). If T1 and T2 tables have the same uid 
attribute then the resulting encoded parameter names will have the same name 
i.e., if on web page B we add encoded parameter name and value corresponding to 
table T2 in the session then it will overwrite the encoded parameter 
corresponding to the table T1.
•       If it is required to remove the encoded parameters from the session 
then how a developer can generate the encoded parameter names programmatically.

Adding the encoded parameter name and value into the session

The initParameter( ) method of TableTag was modified to add the encoded 
parameter name and value into the session:
 
•       Adding encoded page parameter to the session

Integer pageNumberParameter = 
requestHelper.getIntParameter(encodeParameter(TableTagParameters.PARAMETER_PAGE));
if(pageNumberParameter == null) {
// If the encoded page parameter exists in the session then obtain its value. 
// Else add the encoded page parameter to the session and sets its value to 1.
Object objPageParameter = 
this.pageContext.getSession().getAttribute(encodeParameter(TableTagParameters.PARAMETER_PAGE));
if(objPageParameter != null) {
pageNumberParameter = (Integer)objPageParameter;
        } else {
                pageNumberParameter = new Integer(1);
                
this.pageContext.getSession().setAttribute(encodeParameter(TableTagParameters.PARAMETER_PAGE),
 pageNumberParameter);
        }
}
this.pageContext.getSession().setAttribute(encodeParameter(TableTagParameters.PARAMETER_PAGE),
 pageNumberParameter);
this.pageNumber = pageNumberParameter.intValue();


Integer sortColumnParameter =  
requestHelper.getIntParameter(encodeParameter(TableTagParameters.PARAMETER_SORT));
if( sortColumnParameter == null ) {
        Object objSortParameter =  
this.pageContext.getSession().getAttribute(encodeParameter(TableTagParameters.PARAMETER_SORT));
        if( objSortParameter != null ) {
                sortColumnParameter = (Integer)objSortParameter;
        } else {
                sortColumnParameter = new Integer(this.defaultSortedColumn);
                
this.pageContext.getSession().setAttribute(encodeParameter(TableTagParameters.PARAMETER_SORT),
 sortColumnParameter);
        }
}
this.pageContext.getSession().setAttribute(encodeParameter(TableTagParameters.PARAMETER_SORT),
 sortColumnParameter);
int sortColumn = sortColumnParameter.intValue();


Integer sortColumnParameter =  
requestHelper.getIntParameter(encodeParameter(TableTagParameters.PARAMETER_SORT));
if( sortColumnParameter == null ) {
        Object objSortParameter =  
this.pageContext.getSession().getAttribute(encodeParameter(TableTagParameters.PARAMETER_SORT));
        if( objSortParameter != null ) {
                sortColumnParameter = (Integer)objSortParameter;
        } else {
                sortColumnParameter = new Integer(this.defaultSortedColumn);
                
this.pageContext.getSession().setAttribute(encodeParameter(TableTagParameters.PARAMETER_SORT),
 sortColumnParameter);
        }
}
this.pageContext.getSession().setAttribute(encodeParameter(TableTagParameters.PARAMETER_SORT),sortColumnParameter);
int sortColumn = sortColumnParameter.intValue();


Integer sortCode = 
requestHelper.getIntParameter(encodeParameter(TableTagParameters.PARAMETER_ORDER));
SortOrderEnum paramOrder = SortOrderEnum.fromCode(sortCode);
if(paramOrder == null) {
Object objSortParameter =
        
this.pageContext.getSession().getAttribute(encodeParameter(TableTagParameters.PARAMETER_ORDER));
        if(objSortParameter != null) {
                paramOrder = (SortOrderEnum)objSortParameter;
        } else {
                paramOrder = this.defaultSortOrder;
        }
}
this.pageContext.getSession().setAttribute(encodeParameter(TableTagParameters.PARAMETER_ORDER),
 paramOrder);

Differentiating two tables on different web pages with same uid

To take care of this situation we modified the ParamEncoder class constructor 
to accept 'requestURI' ( you can use 'uid' attribute instead ) attribute value 
also.
private String encodeParameter(String parameterName) {
        // paramEncoder has been already instantiated?
        if (this.paramEncoder == null)
        {
                // use the id attribute to get the unique identifier
                // If the <display:table> tag encloses another <display:table> 
then pass this information to ParamEncoder to ensure
                // different encoded parameter is generated.
                TableTag tableTag = (TableTag)findAncestorWithClass(this, 
TableTag.class);
                 HttpServletRequest request = 
(HttpServletRequest)this.pageContext.getRequest();
                if(tableTag != null) {
this.paramEncoder = new ParamEncoder(getUid(), true, requestUri);
                } else {
                this.paramEncoder = new ParamEncoder(getUid(), false, 
requestUri);
        }
        }
        return this.paramEncoder.encodeParameterName(parameterName);
}

public ParamEncoder(String idAttribute, boolean parentFound, String uri)
{
// use name and id to get the unique identifier
                String stringIdentifier = "x-" + idAttribute;  //$NON-NLS-1$
                if(uri != null) {
                stringIdentifier = stringIdentifier + uri;
        }
        ....
        ....
if( parentFound ) {
                this.parameterIdentifier = "z-" + checkSum + "-"; //$NON-NLS-1$ 
//$NON-NLS-2$
        } else {
                this.parameterIdentifier = "d-" + checkSum + "-"; //$NON-NLS-1$ 
//$NON-NLS-2$
        }
    }

Removing encoded parameters from the session

Lets say that you use display tag to show 10 records per page. If there are 15 
records then two page will be shown to the user. The problem occurs when the 
user has the option to delete records from the table shown. If the user deletes 
the last 5 records from the last page then the session is corrupted because 
encoded parameter for the page number will refer to the last page i.e, 2nd 
page, which no longer exists. Its been observed that this results in distorted 
table being shown to the user. In such cases it is required that the encoded 
page number parameter should be set to the second last page. The simplest way 
to take care of this is to do the following in your JSP page ( using scriptlet 
or JSTL ) :

if((recordCount % 10 == 0) && !code.equals("coursemap.delete.required")) {
Enumeration names = session.getAttributeNames();
        while(names.hasMoreElements()) {
                String name = (String)names.nextElement();
                if((name.startsWith("d-") && name.endsWith("-p")) || 
(name.startsWith("z-") && name.endsWith("-p"))) {
                        int value = 
((Integer)session.getAttribute(name)).intValue();
                        if(value > 1) {
                                session.setAttribute(name, new 
Integer(value-1));
                        } else {
                                session.removeAttribute(name);
                        }
                } 
        }
}

Summary:

This is really not a great solution because we modified the existing display 
tag code to make it session-aware. Ideally, there should be an attribute 
defined in the <display:table> tag which should specify whether user wants 
Pagination/Sorting function to be session-enabled or not. Also it should 
provide some simpler way to get to know the name of the session attribute which 
stores the sort order, current page number, sort column index, so that a user 
doesnt need to iterate over the session attributes to remove it.


-- 
This message is automatically generated by JIRA.
-
If you think it was sent incorrectly contact one of the administrators:
   http://jira.codehaus.org/secure/Administrators.jspa
-
For more information on JIRA, see:
   http://www.atlassian.com/software/jira



-------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc. Do you grep through log files
for problems?  Stop!  Download the new AJAX search engine that makes
searching your log files as easy as surfing the  web.  DOWNLOAD SPLUNK!
http://ads.osdn.com/?ad_idv37&alloc_id865&op=click
_______________________________________________
displaytag-devel mailing list
displaytag-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/displaytag-devel

Reply via email to