You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by lu...@apache.org on 2012/01/16 23:03:23 UTC

svn commit: r1232188 - in /myfaces/core/branches/2.0.x/impl/src/main/java/org/apache/myfaces: context/PartialResponseWriterImpl.java util/CDataEndEscapeFilterWriter.java

Author: lu4242
Date: Mon Jan 16 22:03:23 2012
New Revision: 1232188

URL: http://svn.apache.org/viewvc?rev=1232188&view=rev
Log:
MYFACES-3449 [perf] replace buffer in PartialResponseWriter implementation with a fast filter

Added:
    myfaces/core/branches/2.0.x/impl/src/main/java/org/apache/myfaces/util/CDataEndEscapeFilterWriter.java   (with props)
Modified:
    myfaces/core/branches/2.0.x/impl/src/main/java/org/apache/myfaces/context/PartialResponseWriterImpl.java

Modified: myfaces/core/branches/2.0.x/impl/src/main/java/org/apache/myfaces/context/PartialResponseWriterImpl.java
URL: http://svn.apache.org/viewvc/myfaces/core/branches/2.0.x/impl/src/main/java/org/apache/myfaces/context/PartialResponseWriterImpl.java?rev=1232188&r1=1232187&r2=1232188&view=diff
==============================================================================
--- myfaces/core/branches/2.0.x/impl/src/main/java/org/apache/myfaces/context/PartialResponseWriterImpl.java (original)
+++ myfaces/core/branches/2.0.x/impl/src/main/java/org/apache/myfaces/context/PartialResponseWriterImpl.java Mon Jan 16 22:03:23 2012
@@ -19,15 +19,16 @@
 
 package org.apache.myfaces.context;
 
+import java.io.FilterWriter;
 import java.io.IOException;
-import java.io.StringWriter;
 import java.io.Writer;
-import java.util.LinkedList;
+import java.util.ArrayList;
 import java.util.List;
 
 import javax.faces.component.UIComponent;
 import javax.faces.context.PartialResponseWriter;
 import javax.faces.context.ResponseWriter;
+import org.apache.myfaces.util.CDataEndEscapeFilterWriter;
 
 /**
  * <p/>
@@ -67,55 +68,68 @@ import javax.faces.context.ResponseWrite
  * @version $Revision$ $Date$
  */
 
-public class PartialResponseWriterImpl extends PartialResponseWriter {
+public class PartialResponseWriterImpl extends PartialResponseWriter
+{
 
-    class StackEntry {
+    class StackEntry
+    {
         ResponseWriter writer;
-        StringWriter _doubleBuffer;
+        Writer _doubleBuffer;
 
-        StackEntry(ResponseWriter writer, StringWriter doubleBuffer) {
+        StackEntry(ResponseWriter writer, Writer doubleBuffer)
+        {
             this.writer = writer;
             _doubleBuffer = doubleBuffer;
         }
 
-        public ResponseWriter getWriter() {
+        public ResponseWriter getWriter()
+        {
             return writer;
         }
 
-        public void setWriter(ResponseWriter writer) {
+        public void setWriter(ResponseWriter writer)
+        {
             this.writer = writer;
         }
 
-        public StringWriter getDoubleBuffer() {
+        public Writer getDoubleBuffer()
+        {
             return _doubleBuffer;
         }
 
-        public void setDoubleBuffer(StringWriter doubleBuffer) {
+        public void setDoubleBuffer(Writer doubleBuffer)
+        {
             _doubleBuffer = doubleBuffer;
         }
     }
 
     ResponseWriter _cdataDoubleBufferWriter = null;
-    StringWriter _doubleBuffer = null;
-    List<StackEntry> _nestingStack = new LinkedList<StackEntry>();
+    Writer _doubleBuffer = null;
+    List<StackEntry> _nestingStack = new ArrayList<StackEntry>(4);
 
-    public PartialResponseWriterImpl(ResponseWriter writer) {
+    public PartialResponseWriterImpl(ResponseWriter writer)
+    {
         super(writer);
     }
 
     @Override
-    public void startCDATA() throws IOException {
-        if (!isDoubleBufferEnabled()) {
+    public void startCDATA() throws IOException
+    {
+        if (!isDoubleBufferEnabled())
+        {
             super.startCDATA();
-        } else {
+        }
+        else
+        {
             _cdataDoubleBufferWriter.write("<![CDATA[");
         }
         openDoubleBuffer();
     }
 
-    private void openDoubleBuffer() {
-        _doubleBuffer = new StringWriter();
-        //_cdataDoubleBufferWriter = new HtmlResponseWriterImpl(_doubleBuffer, super.getContentType(), super.getCharacterEncoding());
+    private void openDoubleBuffer()
+    {
+        _doubleBuffer = new CDataEndEscapeFilterWriter(_cdataDoubleBufferWriter == null ? 
+                this.getWrapped() : _cdataDoubleBufferWriter );
         _cdataDoubleBufferWriter = getWrapped().cloneWithWriter(_doubleBuffer);
 
         StackEntry entry = new StackEntry(_cdataDoubleBufferWriter, _doubleBuffer);
@@ -124,11 +138,15 @@ public class PartialResponseWriterImpl e
     }
 
     @Override
-    public void endCDATA() throws IOException {
+    public void endCDATA() throws IOException
+    {
         closeDoubleBuffer(false);
-        if (isDoubleBufferEnabled()) {
+        if (isDoubleBufferEnabled())
+        {
             _cdataDoubleBufferWriter.write("]]>");
-        } else {
+        }
+        else
+        {
             super.endCDATA();
         }
     }
@@ -146,97 +164,52 @@ public class PartialResponseWriterImpl e
      *              internal CDATA nesting counter is at the nesting depth 1
      * @throws IOException
      */
-    private void closeDoubleBuffer(boolean force) throws IOException {
-        if (!isDoubleBufferEnabled()) return;
+    private void closeDoubleBuffer(boolean force) throws IOException
+    {
+        if (!isDoubleBufferEnabled())
+        {
+            return;
+        }
         /*
         * if a force close is issued we reset the condition
         * to 1 to reach the underlying closing block
         */
 
-        if (force) {
-            while (!_nestingStack.isEmpty()) {
+        if (force)
+        {
+            while (!_nestingStack.isEmpty())
+            {
                 popAndEncodeCurrentStackEntry();
 
             }
-        } else {
+        }
+        else
+        {
             popAndEncodeCurrentStackEntry();
         }
     }
 
-    private void popAndEncodeCurrentStackEntry() throws IOException {
+    private void popAndEncodeCurrentStackEntry() throws IOException
+    {
         StackEntry elem = _nestingStack.remove(0);
         StackEntry parent = (_nestingStack.isEmpty()) ? null : _nestingStack.get(0);
-        String result = postProcess(elem);
-        if (parent != null) {
+        if (parent != null)
+        {
             _cdataDoubleBufferWriter = parent.getWriter();
             _doubleBuffer = parent.getDoubleBuffer();
-
-            _cdataDoubleBufferWriter.write(result);
-        } else {
-            _cdataDoubleBufferWriter = null;
-            _doubleBuffer = null;
-
-            //todo write it in a blocks of strings which have
-            //the inherent string length included
-            super.write(result);
         }
-        elem.getDoubleBuffer().close();
-        elem.getWriter().close();
-    }
-
-    /**
-     * string post processing
-     *
-     * @param currentElement the current writer element
-     * @return the post processed string
-     * @throws IOException in case of an error
-     */
-    private String postProcess(StackEntry currentElement) throws IOException {
-
-        currentElement.getWriter().flush();
-        StringBuffer buffer = currentElement.getDoubleBuffer().getBuffer();
-
-        int i = buffer.indexOf("]]>");
-        if (i >= 0)
+        else
         {
-            do
-            {
-                buffer.replace(i, i+3, "]]><![CDATA[]]]]><![CDATA[>");
-                i=i+27; //27 is "]]><![CDATA[]]]]><![CDATA[>".length();
-            }
-            while ( (i = buffer.indexOf("]]>",i)) >= 0 );
-        }
-        return buffer.toString();
-        /*
-        String resultString = buffer.toString();
-        //section http://www.w3.org/TR/REC-xml/#sec-cdata-sect everything is parsed
-        //until it hits a ]]> hence we need to do some mapping here
-
-        //ok since our maximum string size is Integer.MAX_VALUE (most JVMs use char [] as
-        //representations
-        //we can work on strings directly, instead of having to go through streams
-        //it is absolutely unlikely that we will ever have a buffered stream bigger than that
-        //for our writer!
-        if (resultString.contains("]]>")) {
-
-            //we now first remove pending javascript CDATA blocks
-            //the reason is if we leave them the ppr chokes on them
-            //the syntax from the auto generated CDATA usually is //\s+<![CDATA[
-            // -= Leonardo Uribe =- Do this cause a bug on the client side, because
-            // scripts containing '&' will be considered invalid xml. 
-            //resultString = resultString.replaceAll("//\\s*((\\<\\!\\[CDATA\\[)|(\\]\\]\\>))", "");
-
-            //now to fullfill the xml spec we have to replace all ]] with blocks of cdata
-            resultString = resultString.replaceAll("\\]\\]\\>", "]]><![CDATA[]]]]><![CDATA[>");
+            _cdataDoubleBufferWriter = null;
+            _doubleBuffer = null;
         }
-        return resultString;
-        */
     }
 
     //--- we need to override ppr specifics to cover the case
 
     @Override
-    public void endInsert() throws IOException {
+    public void endInsert() throws IOException
+    {
         //we use a force close here to fix possible user CDATA corrections
         //under normal conditions the force close just processes the same
         //the underlying close cdata does, but nevertheless
@@ -246,7 +219,8 @@ public class PartialResponseWriterImpl e
     }
 
     @Override
-    public void endUpdate() throws IOException {
+    public void endUpdate() throws IOException
+    {
         //we use a force close here to fix possible user CDATA corrections
         //under normal conditions the force close just processes the same
         //the underlying close cdata does, but nevertheless
@@ -256,7 +230,8 @@ public class PartialResponseWriterImpl e
     }
 
     @Override
-    public void endExtension() throws IOException {
+    public void endExtension() throws IOException
+    {
         //we use a force close here to fix possible user CDATA corrections
         //under normal conditions the force close just processes the same
         //the underlying close cdata does, but nevertheless
@@ -266,7 +241,8 @@ public class PartialResponseWriterImpl e
     }
 
     @Override
-    public void endEval() throws IOException {
+    public void endEval() throws IOException
+    {
         //we use a force close here to fix possible user CDATA corrections
         //under normal conditions the force close just processes the same
         //the underlying close cdata does, but nevertheless
@@ -276,7 +252,8 @@ public class PartialResponseWriterImpl e
     }
 
     @Override
-    public void endError() throws IOException {
+    public void endError() throws IOException
+    {
         //we use a force close here to fix possible user CDATA corrections
         //under normal conditions the force close just processes the same
         //the underlying close cdata does, but nevertheless
@@ -288,85 +265,117 @@ public class PartialResponseWriterImpl e
     //--- optional delegation method ---
 
     @Override
-    public void endElement(String name) throws IOException {
-        if (isDoubleBufferEnabled()) {
+    public void endElement(String name) throws IOException
+    {
+        if (isDoubleBufferEnabled())
+        {
             _cdataDoubleBufferWriter.endElement(name);
-        } else {
+        }
+        else
+        {
             super.endElement(name);
         }
     }
 
     @Override
-    public void writeComment(Object comment) throws IOException {
-        if (isDoubleBufferEnabled()) {
+    public void writeComment(Object comment) throws IOException
+    {
+        if (isDoubleBufferEnabled())
+        {
             _cdataDoubleBufferWriter.writeComment(comment);
-        } else {
+        }
+        else
+        {
             super.writeComment(comment);
         }
     }
 
-    private boolean isDoubleBufferEnabled() {
+    private boolean isDoubleBufferEnabled()
+    {
         return !_nestingStack.isEmpty();
     }
 
     @Override
-    public void startElement(String name, UIComponent component) throws IOException {
-        if (isDoubleBufferEnabled()) {
+    public void startElement(String name, UIComponent component) throws IOException
+    {
+        if (isDoubleBufferEnabled())
+        {
             _cdataDoubleBufferWriter.startElement(name, component);
-        } else {
+        }
+        else
+        {
             super.startElement(name, component);
         }
     }
 
     @Override
-    public void writeText(Object text, String property) throws IOException {
-        if (isDoubleBufferEnabled()) {
+    public void writeText(Object text, String property) throws IOException
+    {
+        if (isDoubleBufferEnabled())
+        {
             _cdataDoubleBufferWriter.writeText(text, property);
-        } else {
+        }
+        else
+        {
             super.writeText(text, property);
         }
     }
 
     @Override
-    public void writeText(char[] text, int off, int len) throws IOException {
-        if (isDoubleBufferEnabled()) {
+    public void writeText(char[] text, int off, int len) throws IOException
+    {
+        if (isDoubleBufferEnabled())
+        {
             _cdataDoubleBufferWriter.writeText(text, off, len);
-        } else {
+        }
+        else
+        {
             super.writeText(text, off, len);
         }
     }
 
     @Override
-    public void write(char[] cbuf, int off, int len) throws IOException {
-        if (isDoubleBufferEnabled()) {
+    public void write(char[] cbuf, int off, int len) throws IOException
+    {
+        if (isDoubleBufferEnabled())
+        {
             _cdataDoubleBufferWriter.write(cbuf, off, len);
-        } else {
+        }
+        else
+        {
             super.write(cbuf, off, len);
         }
     }
 
     @Override
-    public ResponseWriter cloneWithWriter(Writer writer) {
+    public ResponseWriter cloneWithWriter(Writer writer)
+    {
         return super.cloneWithWriter(writer);
     }
 
     @Override
-    public void writeURIAttribute(String name, Object value, String property) throws IOException {
-        if (isDoubleBufferEnabled()) {
+    public void writeURIAttribute(String name, Object value, String property) throws IOException
+    {
+        if (isDoubleBufferEnabled())
+        {
             _cdataDoubleBufferWriter.writeURIAttribute(name, value, property);
-        } else {
+        }
+        else
+        {
             super.writeURIAttribute(name, value, property);
         }
     }
 
     @Override
-    public void close() throws IOException {
+    public void close() throws IOException
+    {
         //in case of a close
         //we have a user error of a final CDATA block
         //we do some error correction here
         //since a close is issued we do not care about
         //a proper closure of the cdata block here anymore
-        if (isDoubleBufferEnabled()) {
+        if (isDoubleBufferEnabled())
+        {
             //we have to properly close all nested cdata stacks
             //end end our cdata block if open
             closeDoubleBuffer(true);
@@ -377,93 +386,131 @@ public class PartialResponseWriterImpl e
     }
 
     @Override
-    public void flush() throws IOException {
-        if (isDoubleBufferEnabled()) {
+    public void flush() throws IOException
+    {
+        if (isDoubleBufferEnabled())
+        {
             _cdataDoubleBufferWriter.flush();
         }
         super.flush();
     }
 
     @Override
-    public void writeAttribute(String name, Object value, String property) throws IOException {
-        if (isDoubleBufferEnabled()) {
+    public void writeAttribute(String name, Object value, String property) throws IOException
+    {
+        if (isDoubleBufferEnabled())
+        {
             _cdataDoubleBufferWriter.writeAttribute(name, value, property);
-        } else {
+        }
+        else
+        {
             super.writeAttribute(name, value, property);
         }
     }
 
     @Override
-    public void writeText(Object object, UIComponent component, String string) throws IOException {
-        if (isDoubleBufferEnabled()) {
+    public void writeText(Object object, UIComponent component, String string) throws IOException
+    {
+        if (isDoubleBufferEnabled())
+        {
             _cdataDoubleBufferWriter.writeText(object, component, string);
-        } else {
+        }
+        else
+        {
             super.writeText(object, component, string);
         }
     }
 
     @Override
-    public Writer append(char c) throws IOException {
-        if (isDoubleBufferEnabled()) {
+    public Writer append(char c) throws IOException
+    {
+        if (isDoubleBufferEnabled())
+        {
             _cdataDoubleBufferWriter.append(c);
             return this;
-        } else {
+        }
+        else
+        {
             return super.append(c);
         }
     }
 
     @Override
-    public Writer append(CharSequence csq, int start, int end) throws IOException {
-        if (isDoubleBufferEnabled()) {
+    public Writer append(CharSequence csq, int start, int end) throws IOException
+    {
+        if (isDoubleBufferEnabled())
+        {
             _cdataDoubleBufferWriter.append(csq, start, end);
             return this;
-        } else {
+        }
+        else
+        {
             return super.append(csq, start, end);
         }
     }
 
     @Override
-    public Writer append(CharSequence csq) throws IOException {
-        if (isDoubleBufferEnabled()) {
+    public Writer append(CharSequence csq) throws IOException
+    {
+        if (isDoubleBufferEnabled())
+        {
             _cdataDoubleBufferWriter.append(csq);
             return this;
-        } else {
+        }
+        else
+        {
             return super.append(csq);
         }
     }
 
     @Override
-    public void write(char[] cbuf) throws IOException {
-        if (isDoubleBufferEnabled()) {
+    public void write(char[] cbuf) throws IOException
+    {
+        if (isDoubleBufferEnabled())
+        {
             _cdataDoubleBufferWriter.write(cbuf);
-        } else {
+        }
+        else
+        {
             super.write(cbuf);
         }
     }
 
     @Override
-    public void write(int c) throws IOException {
-        if (isDoubleBufferEnabled()) {
+    public void write(int c) throws IOException
+    {
+        if (isDoubleBufferEnabled())
+        {
             _cdataDoubleBufferWriter.write(c);
-        } else {
+        }
+        else
+        {
             super.write(c);
         }
     }
 
     @Override
-    public void write(String str, int off, int len) throws IOException {
-        if (isDoubleBufferEnabled()) {
+    public void write(String str, int off, int len) throws IOException
+    {
+        if (isDoubleBufferEnabled())
+        {
             _cdataDoubleBufferWriter.write(str, off, len);
-        } else {
+        }
+        else
+        {
             super.write(str, off, len);
         }
     }
 
     @Override
-    public void write(String str) throws IOException {
-        if (isDoubleBufferEnabled()) {
+    public void write(String str) throws IOException
+    {
+        if (isDoubleBufferEnabled())
+        {
             _cdataDoubleBufferWriter.write(str);
-        } else {
+        }
+        else
+        {
             super.write(str);
         }
     }

Added: myfaces/core/branches/2.0.x/impl/src/main/java/org/apache/myfaces/util/CDataEndEscapeFilterWriter.java
URL: http://svn.apache.org/viewvc/myfaces/core/branches/2.0.x/impl/src/main/java/org/apache/myfaces/util/CDataEndEscapeFilterWriter.java?rev=1232188&view=auto
==============================================================================
--- myfaces/core/branches/2.0.x/impl/src/main/java/org/apache/myfaces/util/CDataEndEscapeFilterWriter.java (added)
+++ myfaces/core/branches/2.0.x/impl/src/main/java/org/apache/myfaces/util/CDataEndEscapeFilterWriter.java Mon Jan 16 22:03:23 2012
@@ -0,0 +1,103 @@
+/*
+ * 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.util;
+
+import java.io.FilterWriter;
+import java.io.IOException;
+import java.io.Writer;
+
+/**
+ *
+ * @author Leonardo Uribe
+ */
+public class CDataEndEscapeFilterWriter extends FilterWriter
+{
+    private char c1;
+    private char c2;
+    private int pos;
+
+    public CDataEndEscapeFilterWriter(Writer out)
+    {
+        super(out);
+        pos = 0;
+    }
+
+    @Override
+    public void write(int c) throws IOException
+    {
+        super.write(c);
+        c1 = c2;
+        c2 = (char) c;
+        pos ++;
+        if (pos > 2)
+        {
+            if (c1 == ']' && c2 == ']' && c == '>')
+            {
+                //"]]><![CDATA[]]]]><![CDATA[>"
+                out.write("<![CDATA[]]]]><![CDATA[>");
+            }
+        }
+    }
+
+    @Override
+    public void write(char[] cbuf, int off, int len) throws IOException
+    {
+        int index = off;
+        for (int i = 0; i < len; i++)
+        {
+            char c = cbuf[off+i];
+            if (c1 == ']' && c2 == ']' && c == '>')
+            {
+                super.write(cbuf, index, i+1);
+                index = i+1;
+                out.write("<![CDATA[]]]]><![CDATA[>");
+            }
+            c1 = c2;
+            c2 = (char) cbuf[off+i];
+            pos++;
+        }
+        if (index < off+len)
+        {
+            super.write(cbuf, index, off+len-index);
+        }
+    }
+
+    @Override
+    public void write(String str, int off, int len) throws IOException
+    {
+        int index = off;
+        for (int i = 0; i < len; i++)
+        {
+            char c = str.charAt(off+i);
+            if (c1 == ']' && c2 == ']' && c == '>')
+            {
+                super.write(str, index, i+1);
+                index = i+1;
+                out.write("<![CDATA[]]]]><![CDATA[>");
+            }
+            c1 = c2;
+            c2 = (char) str.charAt(off+i);
+            pos++;
+        }
+        if (index < off+len)
+        {
+            super.write(str, index, off+len-index);
+        }
+    }
+}

Propchange: myfaces/core/branches/2.0.x/impl/src/main/java/org/apache/myfaces/util/CDataEndEscapeFilterWriter.java
------------------------------------------------------------------------------
    svn:eol-style = native