Dear Wiki user,

You have subscribed to a wiki page or wiki category on "Tapestry Wiki" for 
change notification.

The following page has been changed by GregWoolsey:
http://wiki.apache.org/tapestry/SessionPagePersistence

New page:
Tapestry's session persistence is great, but some times you just want a value 
to work like a "session page instance variable" - only around while the user is 
working within a page (calling listener methods on that page, but not rendering 
any other page).  This strategy avoids all the problems of using real instance 
variables in a page object.

Values are discarded if the last page rendered was not the same as the current 
page being rendered.

You'll need a Persistence strategy, some hivemodule magic, and a Page End 
Render Listener.  Once it's done, just use @Persist("session:page") to use.

The strategy:
{{{
package com.fireapps.tapestry.infrastructure;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;

import org.apache.hivemind.util.Defense;
import org.apache.tapestry.engine.ServiceEncoding;
import org.apache.tapestry.record.PropertyChange;
import org.apache.tapestry.record.PropertyPersistenceStrategy;
import org.apache.tapestry.record.RecordUtils;
import org.apache.tapestry.record.WebSessionAttributeCallback;
import org.apache.tapestry.web.WebRequest;
import org.apache.tapestry.web.WebSession;

/**
 * Derivative of SessionPropertyPersistenceScope
 * - only stores properties while the given page is the most recently rendered 
page.
 */
public class SessionPagePropertyPersistenceStrategy implements 
PropertyPersistenceStrategy {

        /**
         * unique persistence name
         */
        public static final String      STRATEGY_ID     = "session:page";
        
        /**
         * Session key for the last rendered page name
         */
        public static final String LAST_RENDERED_PAGE_ID = "lastPage";

        // Really, the name of the servlet; used as a prefix on all 
HttpSessionAttribute keys
        // to keep things straight if multiple Tapestry apps are deployed
        // in the same WAR.

        private String                          _applicationId;
        private WebRequest                      _request;

        public void store(String pageName, String idPath, String propertyName, 
Object newValue) {
                Defense.notNull(pageName, "pageName");
                Defense.notNull(propertyName, "propertyName");

                WebSession session = _request.getSession(true);

                String attributeName = RecordUtils.buildChangeKey(STRATEGY_ID, 
_applicationId, pageName, idPath, propertyName);

                session.setAttribute(attributeName, newValue);
        }

        public Collection getStoredChanges(String pageName) {
                Defense.notNull(pageName, "pageName");

                WebSession session = _request.getSession(false);

                if (session == null) return Collections.EMPTY_LIST;
                
                /*
                 * Single largest difference between this and Session 
persistence:
                 * If the last rendered page is not this page (or is null) then 
don't return any 
                 * property values, and set any values found to null.
                 */
                String lastPage = (String) 
session.getAttribute(getLastPageKey(_applicationId));
                if (lastPage == null || ! lastPage.equals(pageName)) {
                        // discard values and return empty list
                        discardStoredChanges(pageName);
                        return Collections.EMPTY_LIST;
                }
                
                final Collection result = new ArrayList();

                WebSessionAttributeCallback callback = new 
WebSessionAttributeCallback() {
                        @SuppressWarnings("unchecked")
                        public void handleAttribute(WebSession ws, String name) 
{
                                PropertyChange change = 
RecordUtils.buildChange(name, ws.getAttribute(name));
                                result.add(change);
                        }
                };
                RecordUtils.iterateOverMatchingAttributes(STRATEGY_ID, 
_applicationId, pageName, session, callback);
                return result;
        }

        public void discardStoredChanges(String pageName) {
                WebSession session = _request.getSession(false);
                if (session == null) return;
                WebSessionAttributeCallback callback = new 
WebSessionAttributeCallback() {
                        public void handleAttribute(WebSession ws, String name) 
{
                                ws.setAttribute(name, null);
                        }
                };
                RecordUtils.iterateOverMatchingAttributes(STRATEGY_ID, 
_applicationId, pageName, session, callback);
        }

        public void addParametersForPersistentProperties(ServiceEncoding 
encoding, boolean post) {
                // nothing to do - we don't use query parameters for sessions
        }

        /**
         * @param applicationName (injected by HiveMind) for uniqueness of 
session attribute names
         */
        public void setApplicationId(String applicationName) {
                _applicationId = applicationName;
        }

        /**
         * @param request (injected by HiveMind)
         */
        public void setRequest(WebRequest request) {
                _request = request;
        }
        
        /**
         * @param appId 
         * @return application specific session key name for the last rendered 
page
         * static so the PageEndRenderListener can also use it - there may be a 
better way for this
         */
        public static String getLastPageKey(String appId) {
                return new 
StringBuilder().append(STRATEGY_ID).append(",").append(appId).append(",").toString();
        }
}
}}}

The hivemodule config:
{{{
        <service-point id="SessionPagePropertyPersistenceStrategy" 
interface="org.apache.tapestry.record.PropertyPersistenceStrategy">
                Stores properties in the session, but only until the next 
useage of the page.
                Mapped to the name "session:page".
                <invoke-factory>
                        <construct 
class="com.fireapps.tapestry.infrastructure.SessionPagePropertyPersistenceStrategy">
                                <set-object property="request" 
value="infrastructure:request"/>
                                <set-object property="applicationId" 
value="infrastructure:applicationId"/>
                        </construct>
                </invoke-factory>
    </service-point> 

        <contribution configuration-id="tapestry.persist.PersistenceStrategy">
                <strategy name="session:page" 
object="service:SessionPagePropertyPersistenceStrategy"/>
        </contribution>
}}}

The Page End Render Lister (example shows the definition of an annonymous inner 
class)
{{{
new PageEndRenderListener() {
        /**
         * After rendering, store the page name in the session so the 
"session:page" persistence strategy can use it.
         * @see 
org.apache.tapestry.event.PageEndRenderListener#pageEndRender(org.apache.tapestry.event.PageEvent)
         */
        public void pageEndRender(PageEvent e) {
                final String pageKey = SessionPagePropertyPersistenceStrategy
                         
.getLastPageKey(e.getRequestCycle().getInfrastructure().getApplicationId());
                e.getRequestCycle()
                 .getInfrastructure()
                 .getRequest()
                 .getSession(true)
                 .setAttribute(pageKey, e.getPage().getPageName());
        }
}
}}}

You need to add this PageEndRenderListener to all your pages, unless someone 
has a neat trick for injecting it that I haven't figured out yet (improvements 
highly encouraged!).

What we did was add this pageAttached() method to our base page class (extends 
AppPage, ancestor for all our pages):

{{{
        public void pageAttached(PageEvent event) {
                addPageEndRenderListener([above listener code, to create an 
anonymous inner class]);
        }
}}}

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to