You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by ma...@apache.org on 2010/08/19 18:48:28 UTC
svn commit: r987228 - in /myfaces/trinidad/branches/trinidad-1.2.x:
trinidad-api/src/main/xrts/org/apache/myfaces/trinidad/resource/
trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/application/
trinidad-impl/src/main/java/org/apache/myf...
Author: matzew
Date: Thu Aug 19 16:48:28 2010
New Revision: 987228
URL: http://svn.apache.org/viewvc?rev=987228&view=rev
Log:
TRINIDAD-1747 - zip page state to reduce live memory
Added:
myfaces/trinidad/branches/trinidad-1.2.x/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/util/ObjectInputStreamResolveClass.java
Modified:
myfaces/trinidad/branches/trinidad-1.2.x/trinidad-api/src/main/xrts/org/apache/myfaces/trinidad/resource/LoggerBundle.xrts
myfaces/trinidad/branches/trinidad-1.2.x/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/application/StateManagerImpl.java
myfaces/trinidad/branches/trinidad-1.2.x/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/renderkit/core/CoreResponseStateManager.java
Modified: myfaces/trinidad/branches/trinidad-1.2.x/trinidad-api/src/main/xrts/org/apache/myfaces/trinidad/resource/LoggerBundle.xrts
URL: http://svn.apache.org/viewvc/myfaces/trinidad/branches/trinidad-1.2.x/trinidad-api/src/main/xrts/org/apache/myfaces/trinidad/resource/LoggerBundle.xrts?rev=987228&r1=987227&r2=987228&view=diff
==============================================================================
--- myfaces/trinidad/branches/trinidad-1.2.x/trinidad-api/src/main/xrts/org/apache/myfaces/trinidad/resource/LoggerBundle.xrts (original)
+++ myfaces/trinidad/branches/trinidad-1.2.x/trinidad-api/src/main/xrts/org/apache/myfaces/trinidad/resource/LoggerBundle.xrts Thu Aug 19 16:48:28 2010
@@ -453,4 +453,10 @@
<!-- COMPARETO_TYPE_MISMATCH -->
<resource key="COMPARETO_TYPE_MISMATCH">The type for "{0}" prevents it from being compared to "{1}".</resource>
+<!-- ZIP_STATE_FAILED -->
+<resource key="ZIP_STATE_FAILED">Failed to zip the page state. The state must be serializable to be zipped, so a common reason the page state fails to zip is that the page state is not serializable. You can turn off zipping by setting org.apache.myfaces.trinidad.COMPRESS_VIEW_STATE to false. See the doc about system property org.apache.myfaces.trinidad.CHECK_STATE_SERIALIZATION for how to test various aspects of serialization.</resource>
+
+<!-- UNZIP_STATE_FAILED -->
+<resource key="UNZIP_STATE_FAILED">Failed to unzip the page state.</resource>
+
</resources>
Modified: myfaces/trinidad/branches/trinidad-1.2.x/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/application/StateManagerImpl.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/branches/trinidad-1.2.x/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/application/StateManagerImpl.java?rev=987228&r1=987227&r2=987228&view=diff
==============================================================================
--- myfaces/trinidad/branches/trinidad-1.2.x/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/application/StateManagerImpl.java (original)
+++ myfaces/trinidad/branches/trinidad-1.2.x/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/application/StateManagerImpl.java Thu Aug 19 16:48:28 2010
@@ -18,7 +18,11 @@
*/
package org.apache.myfaces.trinidadinternal.application;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
@@ -26,6 +30,9 @@ import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
+import java.util.zip.DataFormatException;
+import java.util.zip.Deflater;
+import java.util.zip.Inflater;
import javax.faces.FactoryFinder;
import javax.faces.application.StateManager;
@@ -38,25 +45,23 @@ import javax.faces.render.RenderKit;
import javax.faces.render.RenderKitFactory;
import javax.faces.render.ResponseStateManager;
+import org.apache.myfaces.trinidad.bean.util.StateUtils;
import org.apache.myfaces.trinidad.component.UIXComponentBase;
import org.apache.myfaces.trinidad.component.core.CoreDocument;
import org.apache.myfaces.trinidad.context.RequestContext;
+import org.apache.myfaces.trinidad.context.Window;
import org.apache.myfaces.trinidad.logging.TrinidadLogger;
import org.apache.myfaces.trinidad.util.ExternalContextUtils;
+import org.apache.myfaces.trinidad.util.TransientHolder;
+import org.apache.myfaces.trinidadinternal.context.RequestContextImpl;
+import org.apache.myfaces.trinidadinternal.context.TrinidadPhaseListener;
import org.apache.myfaces.trinidadinternal.util.LRUCache;
+import org.apache.myfaces.trinidadinternal.util.ObjectInputStreamResolveClass;
import org.apache.myfaces.trinidadinternal.util.SubKeyMap;
import org.apache.myfaces.trinidadinternal.util.TokenCache;
import com.sun.facelets.FaceletViewHandler;
-import java.io.ByteArrayOutputStream;
-import java.io.ObjectOutputStream;
-
-import org.apache.myfaces.trinidad.bean.util.StateUtils;
-import org.apache.myfaces.trinidad.context.Window;
-import org.apache.myfaces.trinidadinternal.context.RequestContextImpl;
-import org.apache.myfaces.trinidadinternal.context.TrinidadPhaseListener;
-
/**
* StateManager that handles a hybrid client/server strategy: a
* SerializedView is stored on the server, and only a small token
@@ -96,7 +101,6 @@ public class StateManagerImpl extends St
static public final String CACHE_VIEW_ROOT_INIT_PARAM =
"org.apache.myfaces.trinidad.CACHE_VIEW_ROOT";
-
/**
* Servlet context initialization parameter used by
* StateManagerImpl to decide what sort of state should be saved
@@ -115,6 +119,14 @@ public class StateManagerImpl extends St
"org.apache.myfaces.trinidad.CLIENT_STATE_MAX_TOKENS";
/**
+ * Servlet context initialization parameter used by
+ * StateManagerImpl to decide whether to zip state.
+ * Valid values are true and false
+ */
+ static public final String COMPRESS_VIEW_STATE_PARAM_NAME =
+ "org.apache.myfaces.trinidadinternal.COMPRESS_VIEW_STATE";
+
+ /**
* Value indicating that only a simple token will be stored
* on the client.
*/
@@ -1201,7 +1213,7 @@ public class StateManagerImpl extends St
{
private static final long serialVersionUID = 1L;
- private final Object _structure, _state;
+ private Object _structure, _state;
// use transient since UIViewRoots are not Serializable.
private transient ViewRootState _cachedState;
@@ -1210,12 +1222,22 @@ public class StateManagerImpl extends St
{
_structure = structure;
_state = state;
-
+
+ boolean zipState = _zipState(fc);
+
+ if (zipState || StateUtils.checkComponentTreeStateSerialization(fc))
+ {
+
+ if (zipState)
+ {
+ // zip the page state. This will also catch any serialization problems.
+ _zipToBytes(state, structure);
+ }
+ else
+ {
// if component tree serialization checking is on (in order to validate
// fail over support, attempt to Serialize all of the component state
// immediately
- if (StateUtils.checkComponentTreeStateSerialization(fc))
- {
try
{
new ObjectOutputStream(new ByteArrayOutputStream()).writeObject(state);
@@ -1225,7 +1247,7 @@ public class StateManagerImpl extends St
throw new RuntimeException(_LOG.getMessage("COMPONENT_TREE_SERIALIZATION_FAILED"), e);
}
}
-
+ }
// we need this state, as we are going to recreate the UIViewRoot later. see
// the popRoot() method:
_cachedState = (root != null)
@@ -1235,11 +1257,21 @@ public class StateManagerImpl extends St
public Object getStructure()
{
+ if (_zipState(FacesContext.getCurrentInstance()))
+ {
+ return _unzipBytes((byte[])_structure);
+ }
+
return _structure;
}
public Object getState()
{
+ if (_zipState(FacesContext.getCurrentInstance()))
+ {
+ return _unzipBytes((byte[])_state);
+ }
+
return _state;
}
@@ -1313,7 +1345,167 @@ public class StateManagerImpl extends St
}
return null;
- }
+ }
+ private boolean _zipState(FacesContext fc)
+ {
+ // default is false
+ Object zipStateObject =
+ fc.getExternalContext().getInitParameter(COMPRESS_VIEW_STATE_PARAM_NAME);
+
+ if (zipStateObject == null)
+ return false;
+
+ return zipStateObject.toString().equalsIgnoreCase("true");
+ }
+
+ private Object _unzipBytes(byte[] zippedBytes)
+ {
+ Inflater decompressor = null;
+ ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
+ Map<String,Object> sessionMap = externalContext.getSessionMap();
+
+ try
+ {
+ //Get inflater from session cope
+ TransientHolder<Inflater> th =
+ (TransientHolder<Inflater>)sessionMap.remove("PAGE_STATE_INFLATER");
+
+ if (th != null)
+ {
+ decompressor = th.getValue();
+ }
+
+ if(decompressor == null)
+ {
+ decompressor = new Inflater();
+ }
+
+ decompressor.setInput(zippedBytes);
+ ByteArrayOutputStream bos = new ByteArrayOutputStream(zippedBytes.length);
+ byte[] buf = new byte[zippedBytes.length*5];
+
+ while (!decompressor.finished())
+ {
+ try
+ {
+ int count = decompressor.inflate(buf);
+ bos.write(buf, 0, count);
+ }
+ catch (DataFormatException e)
+ {
+ throw new RuntimeException(_LOG.getMessage("UNZIP_STATE_FAILED"), e);
+ }
+ }
+
+ ByteArrayInputStream baos = new ByteArrayInputStream(bos.toByteArray());
+ ObjectInputStream ois = new ObjectInputStreamResolveClass(baos);
+ Object unzippedState = ois.readObject();
+ ois.close();
+ return unzippedState;
+ }
+ catch(ClassNotFoundException cnfe)
+ {
+ throw new RuntimeException(_LOG.getMessage("UNZIP_STATE_FAILED"), cnfe);
+ }
+ catch(IOException ioe)
+ {
+ throw new RuntimeException(_LOG.getMessage("UNZIP_STATE_FAILED"), ioe);
+ }
+ finally
+ {
+ //Reset and put back
+ if(decompressor != null)
+ {
+ decompressor.reset();
+ TransientHolder<Inflater> th = TransientHolder.newTransientHolder(decompressor);
+ sessionMap.put("PAGE_STATE_INFLATER", th);
+ }
+ }
+ }
+
+ private void _zipToBytes(Object state, Object structure)
+ {
+ Deflater compresser = null;
+ ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
+ Map<String,Object> sessionMap = externalContext.getSessionMap();
+
+ try
+ {
+ //Get deflater from session cope
+ TransientHolder<Deflater> th =
+ (TransientHolder<Deflater>)sessionMap.remove("PAGE_STATE_DEFLATER");
+
+ if (th != null)
+ {
+ compresser = th.getValue();
+ }
+
+ if(compresser == null)
+ {
+ compresser = new Deflater(Deflater.BEST_SPEED);
+ }
+
+ //Serialize state
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ ObjectOutputStream oos = new ObjectOutputStream(baos);
+
+ oos.writeObject(state);
+ oos.flush();
+ oos.close();
+
+ byte[] ret = baos.toByteArray();
+ compresser.setInput(ret);
+ compresser.finish();
+
+ baos.reset();
+ byte[] buf = new byte[ret.length/5];
+
+ while (!compresser.finished())
+ {
+ int count = compresser.deflate(buf);
+ baos.write(buf, 0, count);
+ }
+
+ _state = baos.toByteArray();
+
+ //Serialize structure
+ baos.reset();
+ oos = new ObjectOutputStream(baos);
+ compresser.reset();
+
+ oos.writeObject(structure);
+ oos.flush();
+ oos.close();
+
+ ret = baos.toByteArray();
+ compresser.setInput(ret);
+ compresser.finish();
+
+ baos.reset();
+
+ while (!compresser.finished())
+ {
+ int count = compresser.deflate(buf);
+ baos.write(buf, 0, count);
+ }
+
+ _structure = baos.toByteArray();
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException(_LOG.getMessage("ZIP_STATE_FAILED"), e);
+ }
+ finally
+ {
+ //Reset and put back
+ if(compresser != null)
+ {
+ compresser.reset();
+ TransientHolder<Deflater> th = TransientHolder.newTransientHolder(compresser);
+ sessionMap.put("PAGE_STATE_DEFLATER", th);
+ }
+ }
+ }
}
/**
Modified: myfaces/trinidad/branches/trinidad-1.2.x/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/renderkit/core/CoreResponseStateManager.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/branches/trinidad-1.2.x/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/renderkit/core/CoreResponseStateManager.java?rev=987228&r1=987227&r2=987228&view=diff
==============================================================================
--- myfaces/trinidad/branches/trinidad-1.2.x/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/renderkit/core/CoreResponseStateManager.java (original)
+++ myfaces/trinidad/branches/trinidad-1.2.x/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/renderkit/core/CoreResponseStateManager.java Thu Aug 19 16:48:28 2010
@@ -46,6 +46,7 @@ import org.apache.myfaces.trinidad.loggi
import org.apache.myfaces.trinidad.util.Base64InputStream;
import org.apache.myfaces.trinidad.util.Base64OutputStream;
import org.apache.myfaces.trinidad.util.ClassLoaderUtils;
+import org.apache.myfaces.trinidadinternal.util.ObjectInputStreamResolveClass;
/**
* ResponseStateManager implementation for the Core RenderKit.
@@ -184,21 +185,7 @@ public class CoreResponseStateManager ex
try
{
ObjectInputStream ois;
- ois = new ObjectInputStream( new GZIPInputStream( b64_in,
- _BUFFER_SIZE ))
- {
- protected Class<?> resolveClass(ObjectStreamClass desc)
- throws IOException,
- ClassNotFoundException
- {
- // TRINIDAD-1062 It has been noticed that in OC4J and Weblogic that the
- // classes being resolved are having problems by not finding
- // them using the context class loader. Therefore, we are adding
- // this work-around until the problem with these application
- // servers can be better understood
- return ClassLoaderUtils.loadClass(desc.getName());
- }
- };
+ ois = new ObjectInputStreamResolveClass( new GZIPInputStream( b64_in, _BUFFER_SIZE ));
Object structure = ois.readObject();
Object state = ois.readObject();
Added: myfaces/trinidad/branches/trinidad-1.2.x/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/util/ObjectInputStreamResolveClass.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/branches/trinidad-1.2.x/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/util/ObjectInputStreamResolveClass.java?rev=987228&view=auto
==============================================================================
--- myfaces/trinidad/branches/trinidad-1.2.x/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/util/ObjectInputStreamResolveClass.java (added)
+++ myfaces/trinidad/branches/trinidad-1.2.x/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/util/ObjectInputStreamResolveClass.java Thu Aug 19 16:48:28 2010
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.myfaces.trinidadinternal.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectStreamClass;
+import java.util.HashMap;
+
+import org.apache.myfaces.trinidad.util.ClassLoaderUtils;
+import org.apache.myfaces.trinidadinternal.util.ObjectInputStreamResolveClass;
+
+public class ObjectInputStreamResolveClass extends ObjectInputStream
+{
+ public ObjectInputStreamResolveClass() throws IOException, SecurityException
+ {
+ super();
+ }
+
+ public ObjectInputStreamResolveClass(InputStream in) throws IOException
+ {
+ super(in);
+ }
+
+ protected Class<?> resolveClass(ObjectStreamClass desc)
+ throws IOException,
+ ClassNotFoundException
+ {
+ Class<?> cls = null;
+ String className = desc.getName();
+
+ // if it is primitive class, for example, long.class
+ // we resolve them by getting them from the hashMaps
+ cls = _PRIMITIVE_CLASSES.get(className);
+
+ if (null == cls)
+ {
+ // TRINIDAD-1062 It has been noticed that in OC4J and Weblogic that the
+ // classes being resolved are having problems by not finding
+ // them using the context class loader. Therefore, we are adding
+ // this work-around until the problem with these application
+ // servers can be better understood
+ cls = ClassLoaderUtils.loadClass(desc.getName());
+ }
+ return cls;
+ }
+
+ // HashMap to map primitives to their clazzes
+ private static final HashMap<String, Class<?>> _PRIMITIVE_CLASSES = new HashMap<String, Class<?>>();
+
+ static
+ {
+ _PRIMITIVE_CLASSES.put("byte", byte.class);
+ _PRIMITIVE_CLASSES.put("short", short.class);
+ _PRIMITIVE_CLASSES.put("int", int.class);
+ _PRIMITIVE_CLASSES.put("long", long.class);
+ _PRIMITIVE_CLASSES.put("boolean", boolean.class);
+ _PRIMITIVE_CLASSES.put("char", char.class);
+ _PRIMITIVE_CLASSES.put("float", float.class);
+ _PRIMITIVE_CLASSES.put("double", double.class);
+ _PRIMITIVE_CLASSES.put("void", void.class);
+ }
+}