Package com.sun.faces.application.view

Source Code of com.sun.faces.application.view.WriteBehindStateWriter

/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License").  You
* may not use this file except in compliance with the License.  You can
* obtain a copy of the License at
* https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt.  See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license."  If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above.  However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/

package com.sun.faces.application.view;

import java.io.Writer;
import java.io.IOException;

import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.application.StateManager;

import com.sun.faces.RIConstants;
import com.sun.faces.util.Util;
import com.sun.faces.io.FastStringWriter;

/**
* Custom {@link Writer} to efficiently handle the state manager replacement
* marker written out by {@link MultiViewHandler#writeState(javax.faces.context.FacesContext)}.
*/
final class WriteBehindStateWriter extends Writer {

    // length of the state marker
    private static final int STATE_MARKER_LEN =
          RIConstants.SAVESTATE_FIELD_MARKER.length();

    private static final ThreadLocal<WriteBehindStateWriter> CUR_WRITER =
          new ThreadLocal<WriteBehindStateWriter>();
    private Writer out;
    private Writer orig;
    private FastStringWriter fWriter;
    private boolean stateWritten;
    private int bufSize;
    private char[] buf;
    private FacesContext context;


    // -------------------------------------------------------- Constructors


    /**
     * Constructs a new <code>WriteBehindStateWriter</code> instance.
     *
     * @param out the writer we write non-buffered content to
     * @param context the {@link FacesContext} for the current request
     * @param bufSize the buffer size for post-processing buffered content
     */
    public WriteBehindStateWriter(Writer out,
                                  FacesContext context,
                                  int bufSize) {
        this.out = out;
        this.orig = out;
        this.context = context;
        this.bufSize = bufSize;
        this.buf = new char[bufSize];
        CUR_WRITER.set(this);
       
    }


    // ------------------------------------------------- Methods from Writer


    /**
     * Writes directly to the current <code>out</code>.
     *
     * @see Writer#write(int)
     */
    public void write(int c) throws IOException {
        out.write(c);
    }


    /**
     * Writes directly to the current <code>out</code>.
     *
     * @see Writer#write(char[])
     */
    public void write(char cbuf[]) throws IOException {
        out.write(cbuf);
    }


    /**
     * Writes directly to the current <code>out</code>.
     *
     * @see Writer#write(String)
     */
    public void write(String str) throws IOException {
        out.write(str);
    }


    /**
     * Writes directly to the current <code>out</code>.
     *
     * @see Writer#write(String, int, int)
     */
    public void write(String str, int off, int len) throws IOException {
        out.write(str, off, len);
    }


    /**
     * Writes directly to the current <code>out</code>.
     *
     * @see Writer#write(char[], int, int)
     */
    public void write(char cbuf[], int off, int len) throws IOException {
        out.write(cbuf, off, len);
    }


    /**
     * This is a no-op.
     */
    public void flush() throws IOException {
        // no-op
    }


    /**
     * This is a no-op.
     */
    public void close() throws IOException {
        // no-op
    }


    // ------------------------------------------------------ Public Methods


    /**
     * @return the <code>WriteBehindStateWriter</code> being used for processing
     *  this request
     */
    public static WriteBehindStateWriter getCurrentInstance() {
        return CUR_WRITER.get();
    }


    /**
     * Clear the ThreadLocal state.
     */
    public void release() {
        CUR_WRITER.remove();
    }


    /**
     * When called, the original writer is backed up and replaced
     * with a new FastStringWriter.  All content written after this method
     * is called will then be buffered and written out later after the
     * entire view has been rendered.
     */
    public void writingState() {
        if (!stateWritten) {
            this.stateWritten = true;
            out = fWriter = new FastStringWriter(1024);
        }
    }

    /**
     * @return <code>true</code> if {@link #writingState()} has been called,
     *  otherwise returns <code>false</code>
     */
    public boolean stateWritten() {
        return stateWritten;
    }


    /**
     * <p> Write directly from our FastStringWriter to the provided writer.</p>
     *
     * @throws IOException if an error occurs
     */
    public void flushToWriter() throws IOException {

        // Save the state to a new instance of StringWriter to
        // avoid multiple serialization steps if the view contains
        // multiple forms.
        StateManager stateManager = Util.getStateManager(context);
        ResponseWriter origWriter = context.getResponseWriter();
        StringBuilder stateBuilder = getState(stateManager, origWriter);
        StringBuilder builder = fWriter.getBuffer();
        // begin writing...
        int totalLen = builder.length();
        int stateLen = stateBuilder.length();
        int pos = 0;
        int tildeIdx = getNextDelimiterIndex(builder, pos);
        while (pos < totalLen) {
            if (tildeIdx != -1) {
                if (tildeIdx > pos && (tildeIdx - pos) > bufSize) {
                    // there's enough content before the first ~
                    // to fill the entire buffer
                    builder.getChars(pos, (pos + bufSize), buf, 0);
                    orig.write(buf);
                    pos += bufSize;
                } else {
                    // write all content up to the first '~'
                    builder.getChars(pos, tildeIdx, buf, 0);
                    int len = (tildeIdx - pos);
                    orig.write(buf, 0, len);
                    // now check to see if the state saving string is
                    // at the begining of pos, if so, write our
                    // state out.
                    if (builder.indexOf(
                          RIConstants.SAVESTATE_FIELD_MARKER,
                          pos) == tildeIdx) {
                        // buf is effectively zero'd out at this point
                        int statePos = 0;
                        while (statePos < stateLen) {
                            if ((stateLen - statePos) > bufSize) {
                                // enough state to fill the buffer
                                stateBuilder.getChars(statePos,
                                                      (statePos + bufSize),
                                                      buf,
                                                      0);
                                orig.write(buf);
                                statePos += bufSize;
                            } else {
                                int slen = (stateLen - statePos);
                                stateBuilder.getChars(statePos,
                                                      stateLen,
                                                      buf,
                                                      0);
                                orig.write(buf, 0, slen);
                                statePos += slen;
                            }

                        }
                        // push us past the last '~' at the end of the marker
                        pos += (len + STATE_MARKER_LEN);
                        tildeIdx = getNextDelimiterIndex(builder, pos);
                    } else {
                        pos = tildeIdx;
                        tildeIdx = getNextDelimiterIndex(builder,
                                                         tildeIdx + 1);

                    }
                }
               
                stateBuilder = getState(stateManager, origWriter);
                stateLen = stateBuilder.length();
               
            } else {
                // we've written all of the state field markers.
                // finish writing content
                if (totalLen - pos > bufSize) {
                    // there's enough content to fill the buffer
                    builder.getChars(pos, (pos + bufSize), buf, 0);
                    orig.write(buf);
                    pos += bufSize;
                } else {
                    // we're near the end of the response
                    builder.getChars(pos, totalLen, buf, 0);
                    int len = (totalLen - pos);
                    orig.write(buf, 0, len);
                    pos += (len + 1);
                }
            }
        }

        // all state has been written.  Have 'out' point to the
        // response so that all subsequent writes will make it to the
        // browser.
        out = orig;

    }
   
    /**
     * Get the state.
     *
     * <p>
     *  In JSF 2.2 it is required by the specification that the view state in
     *  each h:form has a unique view state id. So we have to call this method
     *  multiple times as each h:form needs to generate the view state string
     *  for itself.
     * </p>
     *
     * @param stateManager the state manager.
     * @param origWriter the original response writer.
     * @return the state.
     * @throws IOException when an I/O error occurs.
     */
    private StringBuilder getState(StateManager stateManager, ResponseWriter origWriter) throws IOException {
        FastStringWriter state =
                new FastStringWriter((stateManager.isSavingStateInClient(
                        context)) ? bufSize : 128);
        context.setResponseWriter(origWriter.cloneWithWriter(state));
        stateManager.writeState(context, stateManager.saveView(context));
        context.setResponseWriter(origWriter);
        StringBuilder stateBuilder = state.getBuffer();
        return stateBuilder;
    }


    /**
     * @param builder buffered content
     * @param offset the offset to start the search from
     * @return the index of the next delimiter, if any
     */
    private static int getNextDelimiterIndex(StringBuilder builder,
                                             int offset) {

        return builder.indexOf(RIConstants.SAVESTATE_FIELD_DELIMITER,
                               offset);

    }

}
TOP

Related Classes of com.sun.faces.application.view.WriteBehindStateWriter

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.