<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>LogikDevelopment &#187; AjaxStateManager</title>
	<atom:link href="http://www.logikdev.com/tag/ajaxstatemanager/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.logikdev.com</link>
	<description>&#34;Il n&#039;y a pas de problème, il n&#039;y a que des solutions. L&#039;esprit de l&#039;homme invente ensuite le problème.&#34; André Gide</description>
	<lastBuildDate>Tue, 20 Dec 2011 12:25:13 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>RichFaces is too greedy</title>
		<link>http://www.logikdev.com/2009/11/17/richfaces-is-too-greedy/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=richfaces-is-too-greedy</link>
		<comments>http://www.logikdev.com/2009/11/17/richfaces-is-too-greedy/#comments</comments>
		<pubDate>Tue, 17 Nov 2009 21:33:09 +0000</pubDate>
		<dc:creator>smoreau</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[Ajax]]></category>
		<category><![CDATA[AjaxStateManager]]></category>
		<category><![CDATA[compression]]></category>
		<category><![CDATA[COMPRESS_STATE_IN_SESSION]]></category>
		<category><![CDATA[JSF]]></category>
		<category><![CDATA[memory]]></category>
		<category><![CDATA[RichFaces]]></category>
		<category><![CDATA[state manager]]></category>

		<guid isPermaLink="false">http://www.logikdev.com/?p=18</guid>
		<description><![CDATA[For my first post, let&#8217;s talk a little bit about RichFaces. RichFaces is a very powerful JSF library which allows you to easily integrate Ajax capabilities into your website. To see all the components RichFaces has to offer, please click on the following link: http://livedemo.exadel.com/richfaces-demo/richfaces/actionparam.jsf I am now using this framework for about two years [...]]]></description>
			<content:encoded><![CDATA[<p>For my first post, let&#8217;s talk a little bit about <strong>RichFaces</strong>.</p>
<p>RichFaces is a very powerful JSF library which allows you to easily integrate Ajax capabilities into your website. To see all the components RichFaces has to offer, please click on the following link: <a href="http://livedemo.exadel.com/richfaces-demo/richfaces/actionparam.jsf">http://livedemo.exadel.com/richfaces-demo/richfaces/actionparam.jsf</a></p>
<p>I am now using this framework for about two years and I quite like how easy it is to use! <img src='http://www.logikdev.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' />  However, at the start, I was only using it in small projects with less than two hundred of unique visitors a day.</p>
<p>That is why I didn&#8217;t discover earlier that RichFaces was using so much memory! Indeed, I am now working on a project that has nearly 50,000 unique visitors a day. When we decided to rebuild it using JSF and RichFaces, I didn&#8217;t think that 6Gb of RAM wouldn&#8217;t be enough to keep it running! <img src='http://www.logikdev.com/wp-includes/images/smilies/icon_surprised.gif' alt=':o' class='wp-smiley' /> </p>
<p>People who knows JSF would say: &#8220;Why don&#8217;t you use &#8216;client&#8217; as state saving method?&#8221;. Well, because of the complexity of my pages, the size of these pages increases so much that it can easily reach 2Mb (which is not acceptable). So, I need to keep the view state on server side.</p>
<p>Still in JSF, there is a way of compressing the view state before saving it in session (part of the web.xml file):</p>
<pre class="brush: xml; title: ; notranslate">
&lt;context-param&gt;
    &lt;param-name&gt;org.apache.myfaces.SERIALIZE_STATE_IN_SESSION&lt;/param-name&gt;
    &lt;param-value&gt;true&lt;/param-value&gt;
&lt;/context-param&gt;

&lt;context-param&gt;
    &lt;param-name&gt;org.apache.myfaces.COMPRESS_STATE_IN_SESSION&lt;/param-name&gt;
    &lt;param-value&gt;true&lt;/param-value&gt;
&lt;/context-param&gt;
</pre>
<p>Strangely enough, the second parameter (<code>COMPRESS_STATE_IN_SESSION</code>) doesn&#8217;t work with RichFaces. After investigating the code, it seems that the RichFaces state manager (<code>org.ajax4jsf.application.AjaxStateManager</code>) doesn&#8217;t support compression at all! <img src='http://www.logikdev.com/wp-includes/images/smilies/icon_sad.gif' alt=':(' class='wp-smiley' /> </p>
<p>Why not? Well, I didn&#8217;t find a good reason so I decided to compress it myself. <img src='http://www.logikdev.com/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' />  The solution is to extend the AjaxStateManager and compress the view state before saving it in session and uncompress it when retrieving it from the session. Pretty easy!!! <img src='http://www.logikdev.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p>Enough talking, here is the code of this custom state manager:</p>
<pre class="brush: java; title: ; notranslate">
package org.ajax4jsf.application;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamClass;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

import javax.faces.FacesException;
import javax.faces.application.StateManager;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import javax.faces.render.ResponseStateManager;

import org.ajax4jsf.context.ContextInitParameters;

/**
 * Overrides the Ajax state manager to enable the state compression.
 * @author Stéphane Moreau
 */
public class MyAjaxStateManager extends AjaxStateManager {
	/**
	 * Only applicable if state saving method is &quot;server&quot; (= default) and if
	 * &lt;code&gt;org.apache.myfaces.SERIALIZE_STATE_IN_SESSION&lt;/code&gt; is
	 * &lt;code&gt;true&lt;/code&gt; (= default).
	 * If &lt;code&gt;true&lt;/code&gt; (default) the serialized state will be compressed before
	 * it is written to the session.
	 * If &lt;code&gt;false&lt;/code&gt; the state will not be compressed.
	 */
	private static final String COMPRESS_SERVER_STATE_PARAM = &quot;org.apache.myfaces.COMPRESS_STATE_IN_SESSION&quot;;

	/**
	 * Default value for &lt;code&gt;org.apache.myfaces.COMPRESS_STATE_IN_SESSION&lt;/code&gt;
	 * context parameter.
	 */
	private static final boolean DEFAULT_COMPRESS_SERVER_STATE_PARAM = true;

	private static final int UNCOMPRESSED_FLAG = 0;
	private static final int COMPRESSED_FLAG = 1;

	private final ComponentsLoader componentLoader;

	public MyAjaxStateManager(StateManager stateManager) {
		super(stateManager);
		componentLoader = new ComponentsLoaderImpl();
	}

	/**
	 * Reads the value of the &lt;code&gt;org.apache.myfaces.COMPRESS_STATE_IN_SESSION&lt;/code&gt;
	 * context parameter.
	 * @see COMPRESS_SERVER_STATE_PARAM
	 * @param context &lt;code&gt;FacesContext&lt;/code&gt; for the request we are processing.
	 * @return boolean true, if the server state steam should be compressed
	 */
	protected static boolean isCompressStateInSession(FacesContext context) {
		String value = context.getExternalContext().getInitParameter(
				COMPRESS_SERVER_STATE_PARAM);
		boolean compress = DEFAULT_COMPRESS_SERVER_STATE_PARAM;
		if (value != null) {
			compress = Boolean.valueOf(value);
		}
		return compress;
	}

	@Override
	protected Object[] buildViewState(FacesContext context) {
		Object[] viewStateArray = null;
		UIViewRoot viewRoot = context.getViewRoot();
		if (null != viewRoot &amp;&amp; !viewRoot.isTransient()) {
			TreeStructureNode treeStructure = (TreeStructureNode) getTreeStructureToSave(context);
			Object state = getComponentStateToSave(context);
			if (isSavingStateInClient(context)) {
				viewStateArray = new Object[]{treeStructure, state};
			} else {
				viewStateArray = saveStateInSession(context, treeStructure,
						handleSaveState(context, state));
			}

		}
		return viewStateArray;
	}

	@Override
	public UIViewRoot restoreView(FacesContext context, String viewId,
			String renderKitId) {
		UIViewRoot viewRoot = null;
		ResponseStateManager responseStateManager = getRenderKit(context,
				renderKitId).getResponseStateManager();
		TreeStructureNode treeStructure = null;
		Object[] state = null;
		Object[] serializedView = null;
		if (isSavingStateInClient(context)) {
			serializedView = (Object[]) responseStateManager.getState(context,
					viewId);

			if (null != serializedView) {
				treeStructure = (TreeStructureNode) serializedView[0];
				state = (Object[]) serializedView[1];
			}
		} else {
			serializedView = restoreStateFromSession(context, viewId,
					renderKitId);

			if (null != serializedView) {
				treeStructure = (TreeStructureNode) serializedView[0];
				state = (Object[]) handleRestoreState(context, serializedView[1]);
			}
		}

		if (null != treeStructure) {
			viewRoot = (UIViewRoot) treeStructure.restore(componentLoader);
			if (null != viewRoot &amp;&amp; null != state) {
				viewRoot.processRestoreState(context, state[0]);
				restoreAdditionalState(context, state[1]);
			}
		}
		return viewRoot;

	}

	private static final Object handleSaveState(FacesContext context, Object state) {
		if (ContextInitParameters.isSerializeServerState(context)) {
			ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
			ObjectOutputStream oas = null;
			try {
				OutputStream os = baos;
				if(isCompressStateInSession(context))
				{
					os.write(COMPRESSED_FLAG);
					os = new GZIPOutputStream(os, 1024);
				}
				else
				{
					os.write(UNCOMPRESSED_FLAG);
				}

				oas = new ObjectOutputStream(os);
				oas.writeObject(state);
				oas.flush();
			} catch (Exception e) {
				throw new FacesException(e);
			} finally {
				if (oas != null) {
					try {
						oas.close();
					} catch (IOException ignored) { }
				}
			}
			return baos.toByteArray();
		} else {
			return state;
		}
	}

	private static final Map&lt;String,Class&lt;?&gt;&gt; PRIMITIVE_CLASSES =
		new HashMap&lt;String,Class&lt;?&gt;&gt;(9, 1.0F);

	static {
		PRIMITIVE_CLASSES.put(&quot;boolean&quot;, boolean.class);
		PRIMITIVE_CLASSES.put(&quot;byte&quot;, byte.class);
		PRIMITIVE_CLASSES.put(&quot;char&quot;, char.class);
		PRIMITIVE_CLASSES.put(&quot;short&quot;, short.class);
		PRIMITIVE_CLASSES.put(&quot;int&quot;, int.class);
		PRIMITIVE_CLASSES.put(&quot;long&quot;, long.class);
		PRIMITIVE_CLASSES.put(&quot;float&quot;, float.class);
		PRIMITIVE_CLASSES.put(&quot;double&quot;, double.class);
		PRIMITIVE_CLASSES.put(&quot;void&quot;, void.class);
	}

	private static final Object handleRestoreState(FacesContext context, Object state) {
		if (ContextInitParameters.isSerializeServerState(context)) {
			ObjectInputStream ois = null;
			try {
				ByteArrayInputStream bais = new ByteArrayInputStream((byte[]) state);
                InputStream is = bais;
                if(is.read() == COMPRESSED_FLAG) {
                	is = new GZIPInputStream(is);
                }
				ois = new ObjectInputStream(is) {
					@Override
					protected Class&lt;?&gt; resolveClass(ObjectStreamClass desc)
					throws IOException, ClassNotFoundException {
						String name = desc.getName();
						try {
							return Class.forName(name, true,
									Thread.currentThread().getContextClassLoader());
						} catch (ClassNotFoundException cnfe) {
							Class&lt;?&gt; clazz = PRIMITIVE_CLASSES.get(name);
							if (clazz != null) {
								return clazz;
							} else {
								throw cnfe;
							}
						}
					}
				};
				return ois.readObject();
			} catch (Exception e) {
				throw new FacesException(e);
			} finally {
				if (ois != null) {
					try {
						ois.close();
					} catch (IOException ignored) { }
				}
			}
		} else {
			return state;
		}
	}

}
</pre>
<p>To enable it, just add the following lines in your faces config file:</p>
<pre class="brush: xml; title: ; notranslate">
&lt;application&gt;
    &lt;state-manager&gt;org.ajax4jsf.application.SearchMedicaStateManager&lt;/state-manager&gt;
&lt;/application&gt;
</pre>
<p>With this state manager, my web application is now using less than 1Gb of memory and is working perfectly fine! <img src='http://www.logikdev.com/wp-includes/images/smilies/icon_biggrin.gif' alt=':D' class='wp-smiley' /> </p>
]]></content:encoded>
			<wfw:commentRss>http://www.logikdev.com/2009/11/17/richfaces-is-too-greedy/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
	</channel>
</rss>

