Package org.apache.geronimo.console.filter

Source Code of org.apache.geronimo.console.filter.SubstituteResponseOutputStream

/*
* 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.geronimo.console.filter;

import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CoderResult;
import java.nio.charset.CodingErrorAction;
import java.nio.charset.MalformedInputException;

import javax.servlet.ServletOutputStream;

/**
* A output stream that will replace certain keyword (</body>) with a
* given string in the output.
* @version $Rev: 817570 $, $Date: 2009-09-22 05:27:20 -0400 (Tue, 22 Sep 2009) $
*/
public class SubstituteResponseOutputStream extends ServletOutputStream {

    private static final int BUFFER_SIZE = 4086;

    private static final int PROCESS_THRESHOLD = 128;

    // Console use UTF-8 encoding. In UTF-8, max length for one character is 4.
    private static final int MALFORMED_INPUT_MAX_LENGTH = 4;

    private String substitute = null;

    private String outputCharset = null;

    private OutputStream stream = null;

    private CharsetDecoder cd = null;

    private ByteBuffer bb = ByteBuffer.allocate(BUFFER_SIZE);

    private CharBuffer cb = CharBuffer.allocate(BUFFER_SIZE);

    private boolean found = false;

    /**
     * Construct a output stream which will scan the input and do substitution
     * for the keyword "</body>".
     *
     * @param substitute
     *            The text that will replace "</body>"
     * @param outputCharset
     *            The charset that will be used to encode the output
     * @param os
     *            The output stream
     */
    public SubstituteResponseOutputStream(String substitute, String outputCharset, OutputStream os) {
        if (substitute == null || outputCharset == null || os == null) {
            throw new NullPointerException();
        }
        this.substitute = substitute;
        this.outputCharset = outputCharset;
        this.stream = os;
        Charset cs = Charset.forName(outputCharset);
        cd = cs.newDecoder().onMalformedInput(CodingErrorAction.REPORT).onUnmappableCharacter(CodingErrorAction.REPLACE);
    }

    /*
     * Decode the bytes in the byte buffer and append to the char buffer.
     *
     * The byte & char buffer should be ready for "put" before entering this
     * method. They are still ready for "put" after exiting this method.
     *
     * @param flushBuffer Whether to flush out all the contents in the byte
     * buffer
     *
     * @throws IOException
     */
    private void decodeBuffer(boolean flushBuffer) throws IOException {
        if (!flushBuffer && bb.position() < PROCESS_THRESHOLD) {
            return;
        }
        bb.flip();
        cd.reset();
        if (bb.hasRemaining()) {
            CoderResult cr = cd.decode(bb, cb, true);
            cd.flush(cb);
            if (cr.isMalformed()) {
                // Move the tail bytes to the head
                int tailLength = bb.remaining();
                if (tailLength >= MALFORMED_INPUT_MAX_LENGTH) {
                    // We only expect the bytes of one character to be broken
                    throw new MalformedInputException(tailLength);
                }
            }
            bb.compact();
        } else {
            bb.clear();
        }
    }

    /*
     * Process the bytes in the buffer. This involves: 1. Decode the bytes and
     * put into the char buffer; 2. Scan the characters in the buffer and do
     * replacement.
     *
     * The byte & char buffer should be ready for "put" before entering this
     * method. They are still ready for "put" after exiting this method.
     *
     * @param flushBuffer Whether to flush out all the contents in the buffer
     * @param endOfInput Whether this is the end of input
     *
     * @throws IOException
     */
    private void processBuffer(boolean flushBuffer, boolean endOfInput) throws IOException {
        decodeBuffer(flushBuffer || endOfInput);
        if (!endOfInput && !flushBuffer && cb.position() < PROCESS_THRESHOLD) {
            return;
        }
        cb.flip();
        found = SubstituteUtil.processSubstitute(cb, substitute, endOfInput, outputCharset, stream);
        // Write the tail bytes in the byte buffer if required
        if (bb.position() > 0 && (found || endOfInput)) {
            bb.flip();
            while (bb.hasRemaining()) {
                stream.write(bb.get());
            }
            bb.clear();
        }
    }

    /*
     * (non-Javadoc)
     *
     * @see java.io.OutputStream#write(int)
     */
    @Override
    public void write(int b) throws IOException {
        if (found) {
            stream.write(b);
        } else {
            bb.put((byte) b);
            processBuffer(false, false);
        }
    }

    /*
     * (non-Javadoc)
     *
     * @see java.io.OutputStream#write(int)
     */
    @Override
    public void write(byte[] content, int offset, int length) throws IOException {
        if (found) {
            stream.write(content, offset, length);
            return;
        }
        int boundary = offset + length;
        int batchOffset = offset;
        int batchLength;
        while (batchOffset < boundary) {
            if (found) {
                stream.write(content, batchOffset, boundary - batchOffset);
                break;
            } else {
                if (boundary - batchOffset < bb.remaining()) {
                    batchLength = boundary - batchOffset;
                } else {
                    batchLength = bb.remaining();
                }
                bb.put(content, batchOffset, batchLength);
                processBuffer(false, false);
                batchOffset = batchOffset + batchLength;
            }
        }
    }

    /*
     * (non-Javadoc)
     *
     * @see java.io.OutputStream#print(String)
     */
    @Override
    public void print(String s) throws IOException {
        if (found) {
            super.print(s);
            return;
        }
        if (bb.position() > 0) {
            // Process the buffered bytes
            decodeBuffer(true);
            if (bb.position() > 0) {
                // There are still malformed input remaining, even though we
                // are going to write a string. This should not happen.
                throw new MalformedInputException(bb.position());
            }
        }
        // Process the string without encoding/decoding overhead
        char[] content = s.toCharArray();
        int offset = 0;
        int length;
        while (offset < content.length) {
            if (found) {
                super.print(s.substring(offset));
                break;
            } else {
                if (content.length - offset < cb.remaining()) {
                    length = content.length - offset;
                } else {
                    length = cb.remaining();
                }
                cb.put(content, offset, length);
                processBuffer(false, false);
                offset = offset + length;
            }
        }
    }

    /*
     * (non-Javadoc)
     *
     * @see java.io.OutputStream#close()
     */
    @Override
    public void close() throws IOException {
        if (!found) {
            processBuffer(true, true);
        }
        stream.flush();
        stream.close();
    }

    /*
     * (non-Javadoc)
     *
     * @see java.io.OutputStream#flush()
     */
    @Override
    public void flush() throws IOException {
        if (!found) {
            // Try to write out as much as possible, but some text might be
            // still buffered in order to handle broken keyword
            processBuffer(true, false);
        }
        stream.flush();
    }

    /**
     * Reset this output stream. Clear the buffers.
     */
    public void reset() {
        bb.clear();
        cb.clear();
        found = false;
    }
}
TOP

Related Classes of org.apache.geronimo.console.filter.SubstituteResponseOutputStream

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.