tapestry-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Apache Wiki <wikidi...@apache.org>
Subject [Tapestry Wiki] Update of "SessionPagePersistence" by GregWoolsey
Date Wed, 12 Jul 2006 17:27:33 GMT
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: dev-unsubscribe@tapestry.apache.org
For additional commands, e-mail: dev-help@tapestry.apache.org


Mime
View raw message