There has been some mention of this, but I've never heard of the
details. Is this something that's planned? I've been thinking about
it, and it may not actually be that bad. All you'd have to do is
serialize all the models under a page into a base64 string (or
equivelent), then plunk them all back into a fresh component tree on
the next request: pretty much how .NET and JSF do it. That would solve
all the back-button issues, you could get a totally stateless
webserver AND have the killer wicket component model. Anyway, just
some thoughs. I'm going to play around with it and probably figure out
that it's a lot more complicated then that in the process. :)
------------------------------------------------------------------------
package com.apropobenefits.wicket;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import org.apache.commons.codec.binary.Base64;
import wicket.Component;
import wicket.Page;
import wicket.WicketRuntimeException;
import wicket.Component.IVisitor;
import wicket.model.IModel;
public class PageState {
private Map<String, IModel> models;
/**
* Creates a new PageState from the given page.
*/
public PageState(Page page) {
models = new HashMap<String, IModel>();
// Add the page model.
IModel pageModel = page.getModel();
if (pageModel != null) {
models.put("", pageModel);
}
page.visitChildren(new IVisitor() {
public Object component(Component component) {
IModel model = component.getModel();
if (model != null) {
models.put(component.getPageRelativePath(), model);
}
return CONTINUE_TRAVERSAL;
}
});
}
/**
* Creates a new PageState from a string.
*/
public PageState(String serializedString) {
ByteArrayInputStream bais = null;
GZIPInputStream unzipper = null;
ObjectInputStream in = null;
try {
bais = new ByteArrayInputStream(
Base64.decodeBase64(serializedString.getBytes()));
unzipper = new GZIPInputStream(bais);
in = new ObjectInputStream(unzipper);
models = (Map<String, IModel>) in.readObject();
in.close();
} catch (Exception e) {
throw new WicketRuntimeException(e.getMessage());
}
}
/**
* Returns a base64 string that can be used to store the page state
* on the client.
*/
public String getString() {
ByteArrayOutputStream baos = null;
GZIPOutputStream zipper = null;
ObjectOutputStream out = null;
// Detach all the models.
for (IModel model: models.values()) {
model.detach();
}
try {
baos = new ByteArrayOutputStream();
zipper = new GZIPOutputStream(baos);
out = new ObjectOutputStream(zipper);
out.writeObject(models);
out.close();
} catch (Exception e) {
throw new WicketRuntimeException(e.getMessage());
}
return new
String(Base64.encodeBase64Chunked(baos.toByteArray()));
}
/**
* Returns the page to the state represented by this page state.
*/
public void populatePage(Page page) {
if (models.containsKey("")) {
page.setModel(models.get(""));
}
page.visitChildren(new IVisitor() {
public Object component(Component component) {
String path = component.getPageRelativePath();
if (models.containsKey(path)) {
component.setModel(models.get(path));
}
return CONTINUE_TRAVERSAL;
}
});
}
}