Here's a proof of concept I just wipped up. It seems to work just
fine. The only problem is that turning everything into a post may be a
bit tricky and is probably where all the work is.

-Phil

On 6/30/05, Phil Kulak <[EMAIL PROTECTED]> wrote:
> 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;
			}
		});
	}
}

Reply via email to