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);
+  }
+}