dev@javaserverfaces.java.net

[REVIEW] Port of JSF HEAD response/state interweaving change

From: Ryan Lubke <Ryan.Lubke_at_Sun.COM>
Date: Tue, 13 Jun 2006 17:51:50 -0700

Port of JSF HEAD response/state interweaving change.
This change was localized to the ViewHandlerImpl in the
HEAD, but in JSF_1_1_ROLLING the proper location
is in the ViewTag. In the end, the result is the same.

SECTION: Modified Files
----------------------------
M src/com/sun/faces/RIConstants.java
 - Added delimiter to mark the beginning
   and end of the state marker
M src/com/sun/faces/taglib/jsf_core/ViewTag.java
 - removed substring logic that would
   replace the state markers with the
   actual state. Instead, use a custom
   writer to which the BodyContent is flushed.
   This writer will then write the contents
   to the current response writer by manipulating
   the char[] provided by the BodyContent.


SECTION: Diffs
----------------------------
Index: src/com/sun/faces/RIConstants.java
===================================================================
RCS file:
/cvs/javaserverfaces-sources/jsf-ri/src/com/sun/faces/RIConstants.java,v
retrieving revision 1.67.16.2
diff -u -r1.67.16.2 RIConstants.java
--- src/com/sun/faces/RIConstants.java 12 Apr 2006 18:33:00 -0000
 1.67.16.2
+++ src/com/sun/faces/RIConstants.java 14 Jun 2006 00:47:40 -0000
@@ -68,8 +68,12 @@
 
     public final static String IMPL_MESSAGES = FACES_PREFIX +
"IMPL_MESSAGES";
 
- public static final String SAVESTATE_FIELD_MARKER = FACES_PREFIX +
- "saveStateFieldMarker";
+ public static final char SAVESTATE_FIELD_DELIMITER = '~';
+ public static final String SAVESTATE_FIELD_MARKER =
+ SAVESTATE_FIELD_DELIMITER
+ + FACES_PREFIX
+ + "saveStateFieldMarker"
+ + SAVESTATE_FIELD_DELIMITER ;
 
     /**
      * <p>Parser implementation for processing JSF reference
expressions.</p>
Index: src/com/sun/faces/taglib/jsf_core/ViewTag.java
===================================================================
RCS file:
/cvs/javaserverfaces-sources/jsf-ri/src/com/sun/faces/taglib/jsf_core/ViewTag.java,v
retrieving revision 1.26.8.3
diff -u -r1.26.8.3 ViewTag.java
--- src/com/sun/faces/taglib/jsf_core/ViewTag.java 12 Apr 2006
18:33:27 -0000 1.26.8.3
+++ src/com/sun/faces/taglib/jsf_core/ViewTag.java 14 Jun 2006
00:47:40 -0000
@@ -29,11 +29,6 @@
 
 package com.sun.faces.taglib.jsf_core;
 
-import com.sun.faces.RIConstants;
-import com.sun.faces.util.Util;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
 import javax.faces.application.StateManager;
 import javax.faces.application.StateManager.SerializedView;
 import javax.faces.application.ViewHandler;
@@ -43,8 +38,6 @@
 import javax.faces.context.ResponseWriter;
 import javax.faces.el.ValueBinding;
 import javax.faces.webapp.UIComponentBodyTag;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
 import javax.servlet.http.HttpSession;
 import javax.servlet.jsp.JspException;
 import javax.servlet.jsp.jstl.core.Config;
@@ -52,8 +45,15 @@
 import javax.servlet.jsp.tagext.BodyTag;
 
 import java.io.IOException;
+import java.io.Writer;
 import java.util.Locale;
 
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import com.sun.faces.RIConstants;
+import com.sun.faces.util.Util;
+
 /**
  * All JSF component tags must be nested within UseFacesTag. This tag
  * corresponds to the root of the UIComponent tree. It does not have
@@ -162,12 +162,7 @@
         ResponseWriter responseWriter = context.getResponseWriter();
         StateManager stateManager = Util.getStateManager(context);
         SerializedView view = null;
- int
- beginIndex = 0,
- markerIndex = 0,
- markerLen = RIConstants.SAVESTATE_FIELD_MARKER.length(),
- contentLen = 0;
-
+
         // get a writer that sends to the client
         responseWriter = responseWriter.cloneWithWriter(getPreviousOut());
 
@@ -183,8 +178,7 @@
             throw new JspException(Util.getExceptionMessageString(
                 Util.NULL_BODY_CONTENT_ERROR_MESSAGE_ID, params));
         }
- content = bodyContent.getString();
-
+
         try {
             view = stateManager.saveSerializedView(context);
         } catch (IllegalStateException ise) {
@@ -195,29 +189,20 @@
             throw new JspException(Util.getExceptionMessageString(
                 Util.SAVING_STATE_ERROR_MESSAGE_ID, params), ie);
         }
+
+ WriteBehindWriter writeBehind = new WriteBehindWriter(context,
+ stateManager,
+ view);
         try {
- contentLen = content.length();
- do {
- // if we have no more markers
- if (-1 == (markerIndex =
- content.indexOf(RIConstants.SAVESTATE_FIELD_MARKER,
- beginIndex))) {
- // write out the rest of the content
- responseWriter.write(content.substring(beginIndex));
- } else {
- // we have more markers, write out the current chunk
- responseWriter.write(content.substring(beginIndex,
- markerIndex));
- stateManager.writeState(context, view);
- beginIndex = markerIndex + markerLen;
- }
- } while (-1 != markerIndex && beginIndex < contentLen);
- } catch (IOException iox) {
- // catch any thrown while saving state in response.
- Object[] params = {"client", iox.getMessage()};
- throw new JspException(Util.getExceptionMessageString(
- Util.SAVING_STATE_ERROR_MESSAGE_ID, params), iox);
+ bodyContent.writeOut(writeBehind);
+ writeBehind.flushToWriter(responseWriter);
+ } catch (IOException ioe) {
+ throw new JspException(ioe);
+ // Object[] params = {"client", iox.getMessage()};
+ //throw new JspException(Util.getExceptionMessageString(
+ // Util.SAVING_STATE_ERROR_MESSAGE_ID, params), iox);
         }
+
         return EVAL_PAGE;
     }
 
@@ -312,7 +297,7 @@
      */
     protected Locale getLocaleFromString(String localeExpr) {
         Locale result = Locale.getDefault();
- if (localeExpr.indexOf("_") == -1 && localeExpr.indexOf("-") ==
-1) {
+ if (localeExpr.indexOf('_') == -1 && localeExpr.indexOf('-') ==
-1) {
             // expression has just language code in it. make sure the
             // expression contains exactly 2 characters.
             if (localeExpr.length() == 2) {
@@ -329,6 +314,112 @@
             }
         }
         return result;
+ }
+
+
+ // -----------------------------------------------------------
Inner Classes
+
+ private static final class WriteBehindWriter extends Writer {
+
+ private static final int STATE_MARKER_LEN =
+ RIConstants.SAVESTATE_FIELD_MARKER.length();
+
+ private final FacesContext context;
+ private final SerializedView view;
+ private final StateManager manager;
+ private char[] input;
+ private int inputOff;
+ private int inputLen;
+
+ // --------------------------------------------------------
Constructors
+
+ public WriteBehindWriter(FacesContext context,
+ StateManager manager,
+ SerializedView view) {
+ this.context = context;
+ this.manager = manager;
+ this.view = view;
+ }
+
+
+ // ------------------------------------------------- Methods
from Writer
+
+
+ public void write(char cbuf[], int off, int len) throws
IOException {
+ input = cbuf;
+ inputOff = off;
+ inputLen = len;
+ }
+
+ public void flush() throws IOException {
+ // NO-OP
+ }
+
+ public void close() throws IOException {
+ // NO-OP
+ }
+
+
+ // ------------------------------------------------------
Public Methods
+
+
+ public void flushToWriter(Writer writer) throws IOException
{
+ int totalLen = inputLen;
+ int pos = inputOff;
+ int tildeIdx = getNextDelimiterIndex(pos);
+ while (pos < totalLen) {
+ if (tildeIdx != -1) {
+
+ // write all content up to the first
'~'
+ int len = (tildeIdx - pos);
+ writer.write(input, pos, len);
+ // now check to see if the state saving string is
+ // at the begining of pos, if so, write our
+ // state out.
+ if (input[tildeIdx + STATE_MARKER_LEN - 1]
+ == RIConstants
+ .SAVESTATE_FIELD_DELIMITER) {
+ manager.writeState(context, view);
+ }
+
+ // push us past the last '~' at the end of the marker
+ pos += (len + STATE_MARKER_LEN);
+ tildeIdx = getNextDelimiterIndex(pos);
+
+ } else {
+
+ // we're near the end of the
response
+ int len = (totalLen - pos);
+ writer.write(input, pos, len);
+ pos += (len + 1);
+
+ }
+ }
+ }
+
+ // -----------------------------------------------------
Private Methods
+
+ private int getNextDelimiterIndex(int fromIndex) {
+ return indexOf(RIConstants.SAVESTATE_FIELD_DELIMITER,
fromIndex);
+ }
+
+ private int indexOf(char c, int fromIndex) {
+ int max = inputOff + inputLen;
+
+ if (fromIndex < 0) {
+ fromIndex = 0;
+ } else if (fromIndex >= inputLen) {
+ return -1;
+ }
+ for (int i = inputOff + fromIndex; i < max; i++) {
+ if (input[i] == c) {
+ return i - inputOff;
+ }
+ }
+ return -1;
+ }
+
+
     }
 
 } // end of class ViewTag