AS OF TAPESTRY 5.1 this code no longer seems to work. The classes Base64ObjectInputStream and Base64ObjectOutputStream no longer exist in org.apache.tapestry.internal.services.*;

Look at the Cookies API instead

http://tapestry.apache.org/tapestry5/tapestry-core/apidocs/org/apache/tapestry/services/Cookies.html

This example code creates a basic PersistentFieldStrategy for "cookie" and "flashcookie" @Persist values

This code is a 'trimmed back' version of what I use... Everything is b64 encoded. You can encode a key of some sort (ValueEncoder index, or class type / name) but it adds extra complexity.

The reason that I wanted to do this was because none of the existing persistence strategies fit my requirements.

The driving factor, as in so many web applications, was to avoid creating a session, and I really didn't like the large t:client:state cgi param.

I have this defined in my common base class:

@Meta("tapestry.persistence-strategy=flashcookie")

So, here you have it... my contribution to the tapestry corner. Enjoy!

Oh - a few Caveats, etc

first - when developing add a showAllCookies t:grid into your border component from RequestGlobals.

flashcookie is safe, it doesn't hang around. I recommend you use this for all flash / instant requirements

Here is an example code to add to your border or footer-type component, to display the cookies. Note that it displays all cookies, not just ones with a specific prefix.

	@Inject
	private RequestGlobals _requestGlobals;

	@Component(id = "cookieJar", parameters = { "source=allCookies", "rowsPerPage=100", "pagerPosition=both", "row=currentCookie" })
	private Grid cookieJar;

	@Property
	private Cookie currentCookie;
	
	public List<Cookie> getAllCookies() {
		List<Cookie> cookieList = new ArrayList<Cookie>();
		Cookie cookies[] = _requestGlobals.getHTTPServletRequest().getCookies();
		if (cookies != null)
			for (int i = 0; i < cookies.length; i++)
				cookieList.add(cookies[i]);
		return cookieList;
	}

Then add this to the corresponding tml:

<t:cookieJar />

cookie can hang around after sessions... I delete all cookies on logout... but if you don't login (or just exit the browser), they are still there. I don't think this is safe.

all cookies are mapped to the entire app (context root). I don't know if this is strictly necessary, but it works.

Possible extensions:

set cookie max age (inactivity time)... where to get session timeout value from?

how to make cookies expire as soon as session is closed

First of all, a contribution had to made in AppModule:

public void contributePersistentFieldManager(MappedConfiguration<String, PersistentFieldStrategy> configuration,
		RequestGlobals requestGlobals, Request request) {
	System.out.println("################## in contributePersistentFieldManager of " + PostbackModule.class.getName());
	configuration.add(CookiePersistentField.COOKIE, new CookiePersistentField(requestGlobals, request, CookiePersistentField.COOKIE));
	configuration.add(CookiePersistentField.FLASHCOOKIE, new CookiePersistentField(requestGlobals, request, CookiePersistentField.FLASHCOOKIE));
}

and the CookiePersistentField source (simplified to use B64 only)

/*
 * Created on 12 Apr 2008
 * 
 * @author nicholas[dot] krul _at_ gmail com
 *
 */
package koncept.postback.base;

import static org.apache.tapestry.ioc.internal.util.Defense.notBlank;

import java.util.*;

import javax.servlet.http.*;

import koncept.postback.base.encoders.*;

import org.apache.tapestry.internal.services.*;
import org.apache.tapestry.services.*;

public class CookiePersistentField implements PersistentFieldStrategy {
	public static final String COOKIE = "cookie";
	public static final String FLASHCOOKIE = "flashcookie";
	
	private String persistenceType;
	private static final String SEPERATOR = "|";

	private RequestGlobals requestGlobals;
	private Request request;

	private EncoderBase64 encoder;
	
	public CookiePersistentField(RequestGlobals requestGlobals, Request request, String persistenceType) {
		this.requestGlobals = requestGlobals;
		this.request = request;
		this.persistenceType = persistenceType;
		encoder = new EncoderBase64();
	}

	public void postChange(String pageName, String componentId, String fieldName, Object newValue) {
        notBlank(pageName, "pageName");
        notBlank(fieldName, "fieldName");

        StringBuilder builder = new StringBuilder(persistenceType);
        builder.append(SEPERATOR);
        builder.append(pageName);
        builder.append(SEPERATOR);

        if (componentId != null) builder.append(componentId);

        builder.append(SEPERATOR);
        builder.append(fieldName);

        String key = builder.toString();
        
        if (newValue != null) {
        	String value = encoder.toClient(newValue); 
        	createCookie(key, value);
        } else { //newValue == null
        	deleteCookie(key);
        }
	}

	 private PersistentFieldChange buildChange(Cookie cookie) {
		String value = cookie.getValue();
		if (value == null || value.isEmpty()) return null; //not needed
		
        String[] chunks = cookie.getName().split("\\" + SEPERATOR); //oops... regexp
        String componentId = chunks[2];
        String fieldName = chunks[3];
        
        Object attribute = encoder.toValue(value);
        return new PersistentFieldChangeImpl(componentId, fieldName, attribute);
    }
	
   public Collection<PersistentFieldChange> gatherFieldChanges(String pageName) {
	   Collection<PersistentFieldChange> changes = new ArrayList<PersistentFieldChange>();
	   String fullPrefix = persistenceType + SEPERATOR + pageName + SEPERATOR;
	   for (Cookie cookie: getCookiesStartingWith(fullPrefix)) {
		   try {
			   PersistentFieldChange fieldChange = buildChange(cookie);
			   if (fieldChange != null) changes.add(fieldChange);
			   if (persistenceType.equals(FLASHCOOKIE)) deleteCookie(cookie.getName());
		   } catch (RuntimeException e) {
			   throw new RuntimeException("Error with cookie name: " + cookie.getName(),e);
		   }
	   }
	   return changes;
   }

   public void discardChanges(String pageName) {
       String fullPrefix = persistenceType + SEPERATOR + pageName + SEPERATOR;
       for (Cookie cookie: getCookiesStartingWith(fullPrefix)) {
    	   deleteCookie(cookie.getName());
       }
   }
	
    private List<Cookie> getCookiesStartingWith(String prefix) {
    	List<Cookie> cookieList = new ArrayList<Cookie>();
    	Cookie cookies[] = requestGlobals.getHTTPServletRequest().getCookies();
    	if (cookies != null) for (int i = 0; i < cookies.length; i++) if (cookies[i].getName().startsWith(prefix)) cookieList.add(cookies[i]);
    	return cookieList;
    }
    
    private void createCookie(String name, String value) {
    	Cookie cookie = new Cookie(name, value);
    	cookie.setPath(request.getContextPath());
    	requestGlobals.getHTTPServletResponse().addCookie(cookie);
    }
    
    private void deleteCookie(String name) {
    	Cookie cookie = new Cookie(name, "_"); //'empty values may cause problems'
    	cookie.setMaxAge(0);
    	cookie.setPath(request.getContextPath());
    	requestGlobals.getHTTPServletResponse().addCookie(cookie);
    }
   
}

And the EncoderBase64 class ...

/*
 * Created on 13 Apr 2008
 *
 */
package koncept.postback.base.encoders;

import java.io.*;

import org.apache.tapestry.*;
import org.apache.tapestry.internal.util.*;
import org.apache.tapestry.ioc.internal.util.*;

public class EncoderBase64 implements ValueEncoder<Object> {

	public Object toValue(String clientValue) {
    	Object value = null;
    	ObjectInputStream in = null;
        try {
            in = new Base64ObjectInputStream(clientValue);
            value = in.readObject();

        } catch (Exception e) {
            throw new RuntimeException("client state corrupted", e);
        } finally {
            InternalUtils.close(in);
        }
        return value;
    }
	    
	 public String toClient(Object value) {
    	 Base64ObjectOutputStream os = null;
         try {
             os = new Base64ObjectOutputStream();
             os.writeObject(value);
         } catch (Exception ex) {
             throw new RuntimeException(ex.getMessage(), ex);
         } finally {
             InternalUtils.close(os);
         }
         return os.toBase64();
    }
	

}

There you go - an easy solution for

@Persist("cookie")
@Persist("flashcookie")

annotations.

--nK

--nicholas Krul

  • No labels