DUE TO SPAM, SIGN-UP IS DISABLED. Goto Selfserve wiki signup and request an account.
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