All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sun.faces.application.view.WriteBehindStateWriter Maven / Gradle / Ivy

/*
 * 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 CUR_WRITER =
          new ThreadLocal();
    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 WriteBehindStateWriter 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 out.
     *
     * @see Writer#write(int)
     */
    public void write(int c) throws IOException {
        out.write(c);
    }


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


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


    /**
     * Writes directly to the current out.
     *
     * @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 out.
     *
     * @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 WriteBehindStateWriter 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 true if {@link #writingState()} has been called,
     *  otherwise returns false
     */
    public boolean stateWritten() {
        return stateWritten;
    }


    /**
     * 

Write directly from our FastStringWriter to the provided writer.

* * @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(); FastStringWriter state = new FastStringWriter((stateManager.isSavingStateInClient( context)) ? bufSize : 128); context.setResponseWriter(origWriter.cloneWithWriter(state)); stateManager.writeState(context, stateManager.saveView(context)); context.setResponseWriter(origWriter); StringBuilder builder = fWriter.getBuffer(); // begin writing... int totalLen = builder.length(); StringBuilder stateBuilder = state.getBuffer(); 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); } } } 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; } /** * @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); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy