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 MarkLehmacher:
http://wiki.apache.org/tapestry/SessionPagePersistence

The comment on the change is:
added a different way of implementing session:page strategy

------------------------------------------------------------------------------
  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.
+ Values are discarded if the last page rendered was not the same as the 
current page being rendered (see below for another implementation).
  
  You'll need a 
[http://tapestry.apache.org/tapestry4/tapestry/apidocs/org/apache/tapestry/record/PropertyPersistenceStrategy.html
 PropertyPersistenceStrategy], some hivemodule magic, and a 
[http://tapestry.apache.org/tapestry4/tapestry/apidocs/org/apache/tapestry/event/PageEndRenderListener.html
 PageEndRenderListener].  Once it's done, just use 
[http://tapestry.apache.org/tapestry4/tapestry-annotations/apidocs/org/apache/tapestry/annotations/Persist.html
 @Persist]("session:page") to take advantage of it.
  
@@ -33, +33 @@

         * unique persistence name
         */
        public static final String      STRATEGY_ID     = "session:page";
-       
+ 
        /**
         * Session key for the last rendered page name
         */
@@ -63, +63 @@

                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 
+                * 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));
@@ -75, +75 @@

                        discardStoredChanges(pageName);
                        return Collections.EMPTY_LIST;
                }
-               
+ 
                final Collection result = new ArrayList();
  
                WebSessionAttributeCallback callback = new 
WebSessionAttributeCallback() {
@@ -117, +117 @@

        public void setRequest(WebRequest request) {
                _request = request;
        }
-       
+ 
        /**
-        * @param appId 
+        * @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
         */
@@ -140, +140 @@

                                <set-object property="applicationId" 
value="infrastructure:applicationId"/>
                        </construct>
                </invoke-factory>
-     </service-point> 
+     </service-point>
  
        <contribution configuration-id="tapestry.persist.PersistenceStrategy">
                <strategy name="session:page" 
object="service:SessionPagePropertyPersistenceStrategy"/>
@@ -176, +176 @@

        }
  }}}
  
+ A completely different implementation for the same purpose (of implementing a 
session:page strategy) is presented below. The advantage to the solution above 
is, that it is stand alone and does not rely on the PageEndRenderListener impl. 
However, there are some disadvantages to it as well, as it breaks encapsulation 
of some Tapestry internals (RecordUtils in particular) and might incur 
performance-penalties when you have lots and lots of properties in your session 
(because it iterates over *all* session properties for each request).
+ 
+ The strategy:
+ {{{
+ package de.rwth.rz.tapestry.record;
+ 
+ 
+ import java.util.ArrayList;
+ import java.util.Collection;
+ import java.util.Collections;
+ import java.util.Iterator;
+ 
+ import org.apache.hivemind.util.Defense;
+ import org.apache.log4j.Logger;
+ import org.apache.tapestry.IRequestCycle;
+ 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.SessionPropertyPersistenceStrategy;
+ import org.apache.tapestry.record.WebSessionAttributeCallback;
+ import org.apache.tapestry.web.WebRequest;
+ import org.apache.tapestry.web.WebSession;
+ 
+ 
+ /**
+  * <p>
+  * Strategy similar to client:page but for server properties.
+  * </p>
+  *
+  * <h4> Implementation notes: </h4>
+  *
+  * <p>
+  * Can't extend [EMAIL PROTECTED] SessionPropertyPersistenceStrategy} because 
all methods use STRATEGY_ID which
+  * is final in <code>SessionPropertyPersistenceStrategy</code>. So basically 
this is lot's of
+  * duplicated code from said class, with changed strategy id and a one-line 
change in
+  * [EMAIL PROTECTED] #getStoredChanges(String)}.
+  * </p>
+  *
+  * <p>
+  * Moreover implementation of [EMAIL PROTECTED] 
#evictStaleProperties(String)} is based upon internal knowledge
+  * of [EMAIL PROTECTED] RecordUtils} workings (yeah, sucks). It works by 
iterating over all session properties
+  * and discarding those properties which have been recorded by this strategy 
(together with the page
+  * name they originated from) but don't belong to the current page.</p >
+  *
+  * <p>
+  * To top it all off, I don't even know whether or not it's correct to hook 
into
+  * <code>getStoredChanges</code> for evicting properties. I don't really know 
what that method is
+  * doing in the first place but I guess it returns those changed properties 
which should be handled
+  * by the page recorder for distribution over a clustered session or 
something (but that's just a
+  * wild guess).
+  * </p>
+  *
+  * <p>
+  * The strategy seems to work though.
+  * </p>
+  *
+  * <p>
+  * When using this strategy be aware what it means when the user uses the 
browser back button in
+  * order to return from another page to the page before: The page before will 
have lost all it's
+  * page persistent properties, so keep that in mind.
+  * </p>
+  *
+  * @author lehmacher
+  */
+ public class PageSessionPropertyPersistenceStrategy implements 
PropertyPersistenceStrategy {
+ 
+       private static final Logger logger = 
Logger.getLogger(PageSessionPropertyPersistenceStrategy.class);
+ 
+       /**
+        * For page names starting with that prefix no properties are evicted. 
Basing this on the name
+        * is brittle at best, but when I tried retrieving the current page 
from the request cycle in
+        * getStoredChanges method (in order to base that change on an 
implemented IntermediatePage
+        * interface), it did not work (forgot the exact reason why it did not 
work).
+        */
+       public static final String SKIP_PREFIX = "intermediate";
+       public static final String STRATEGY_ID = "session-page";
+ 
+       private String _applicationId;
+       private WebRequest _request;
+       private IRequestCycle _cycle;
+ 
+     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");
+ 
+               evictStaleProperties(pageName);
+ 
+               WebSession session = _request.getSession(false);
+ 
+               if (session == null)
+                       return Collections.EMPTY_LIST;
+ 
+               final Collection result = new ArrayList();
+ 
+               WebSessionAttributeCallback callback = new 
WebSessionAttributeCallback() {
+                       public void handleAttribute(WebSession session, String 
name) {
+                               PropertyChange change = 
RecordUtils.buildChange(name, session.getAttribute(name));
+ 
+                               result.add(change);
+                       }
+               };
+ 
+               RecordUtils.iterateOverMatchingAttributes(STRATEGY_ID, 
_applicationId, pageName, session,
+                       callback);
+ 
+               return result;
+       }
+ 
+       public void addParametersForPersistentProperties(ServiceEncoding 
encoding, boolean post) {
+       }
+ 
+ 
+       public void discardStoredChanges(String pageName) {
+               WebSession session = _request.getSession(false);
+ 
+               if (session == null)
+                       return;
+ 
+               WebSessionAttributeCallback callback = new 
WebSessionAttributeCallback() {
+                       public void handleAttribute(WebSession session, String 
name) {
+                               session.setAttribute(name, null);
+                       }
+               };
+ 
+               RecordUtils.iterateOverMatchingAttributes(STRATEGY_ID, 
_applicationId, pageName, session,
+                       callback);
+       }
+ 
+       /**
+        * Iterates over all persistent properties and removes those which are 
page persistent but where
+        * current page does not match recorded page.<p/>
+        *
+        * Implementation breaks encapsulation of RecordUtils.
+        */
+       protected void evictStaleProperties(String currentPageName) {
+ 
+               // no session? do nothing
+               WebSession session = _request.getSession(false);
+               if (session == null)
+                       return;
+ 
+               // this is for assuring that properties don't get evicted when 
a page is retrieved from the
+               // request cycle from another page. For example CurrentPage is 
the current page and it looks
+               // up AnotherPage in one of it's listener methods. Persistent 
properties will be restored
+               // for AnotherPage but at the same time we don't want the 
CurrentPage properties to get
+               // evicted just yet. The checks works on the assumption that 
when a new page is being surfed
+               // to, the request cycle will still return null when the 
persistence strategy is used to
+               // get stored changes.
+               if (_cycle.getPage() != null)
+                       return;
+ 
+               if (currentPageName.startsWith(SKIP_PREFIX) || 
currentPageName.indexOf("/" + SKIP_PREFIX) != -1)
+                       return;
+ 
+               // that's how RecordUtils build the prefix string:
+               // String prefix = strategyId + "," + applicationId + "," + 
pageName + ",";
+ 
+               // this breaks encapsulation for building the attributeName 
keys in RecordUtils
+               String prefix = STRATEGY_ID + "," + _applicationId + ",";
+ 
+               Iterator names = session.getAttributeNames().iterator();
+               while (names.hasNext()) {
+ 
+                       String name = (String)names.next();
+ 
+                       // if property is not of this strategy or application 
we skip it
+                       if (!name.startsWith(prefix))
+                               continue;
+ 
+                       // means strategy and application match, but page 
doesn't
+                       if (!name.startsWith(prefix + currentPageName)) {
+                               session.setAttribute(name, null);
+                               logger.debug("evicting from session " + name);
+                       }
+ 
+               }
+       }
+ 
+       public void setApplicationId(String applicationName) {
+               _applicationId = applicationName;
+       }
+ 
+       public void setRequest(WebRequest request) {
+               _request = request;
+       }
+ 
+       public void setRequestCycle(IRequestCycle cycle) {
+               _cycle = cycle;
+       }
+ }
+ }}}
+ 
+ The hivemodule config:
+ {{{
+       <service-point id="PageSessionPropertyPersistenceStrategy"
+               
interface="org.apache.tapestry.record.PropertyPersistenceStrategy">
+ 
+               Property persistence Strategy for session properties with page 
scope, similar to "client:page".
+ 
+               <invoke-factory>
+                       <construct 
class="de.rwth.rz.tapestry.record.PageSessionPropertyPersistenceStrategy">
+                               <set-object property="applicationId" 
value="infrastructure:applicationId"/>
+                               <set-object property="request" 
value="infrastructure:request"/>
+                               <set-object property="requestCycle" 
value="infrastructure:requestCycle"/>
+                       </construct>
+               </invoke-factory>
+       </service-point>
+ 
+       <contribution configuration-id="tapestry.persist.PersistenceStrategy">
+               <strategy name="session:page" 
object="service:PageSessionPropertyPersistenceStrategy"/>
+       </contribution>
+ }}}
+ 

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

Reply via email to