Package com.alibaba.citrus.service.requestcontext.buffered.impl

Source Code of com.alibaba.citrus.service.requestcontext.buffered.impl.BufferedResponseImpl$WriterOutputStream

/*
* Copyright 2010 Alibaba Group Holding Limited.
* All rights reserved.
*
* Licensed 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 com.alibaba.citrus.service.requestcontext.buffered.impl;

import static com.alibaba.citrus.util.BasicConstant.*;
import static com.alibaba.citrus.util.CollectionUtil.*;
import static com.alibaba.citrus.util.ObjectUtil.*;

import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.util.EmptyStackException;
import java.util.LinkedList;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.citrus.service.requestcontext.RequestContext;
import com.alibaba.citrus.service.requestcontext.support.AbstractResponseWrapper;
import com.alibaba.citrus.util.io.ByteArray;
import com.alibaba.citrus.util.io.ByteArrayInputStream;
import com.alibaba.citrus.util.io.ByteArrayOutputStream;
import com.alibaba.citrus.util.io.StreamUtil;

/**
* ����<code>HttpServletResponse</code>��ʹ֮������ڴ��С�
*
* @author Michael Zhou
*/
public class BufferedResponseImpl extends AbstractResponseWrapper {
    private static final Logger log = LoggerFactory.getLogger(BufferedResponseImpl.class);
    private boolean buffering = true;
    private Stack<ByteArrayOutputStream> bytesStack;
    private Stack<StringWriter> charsStack;
    private ServletOutputStream stream;
    private PrintWriter streamAdapter;
    private PrintWriter writer;
    private ServletOutputStream writerAdapter;

    /**
     * ����һ��<code>BufferedResponseImpl</code>��
     *
     * @param requestContext response���ڵ�request context
     * @param response ԭʼ��response
     */
    public BufferedResponseImpl(RequestContext requestContext, HttpServletResponse response) {
        super(requestContext, response);
    }

    /**
     * ȡ���������
     *
     * @return response�������
     * @throws IOException �������ʧ��
     */
    @Override
    public ServletOutputStream getOutputStream() throws IOException {
        if (stream != null) {
            return stream;
        }

        if (writer != null) {
            // ���getWriter�����Ѿ������ã���writerת����OutputStream
            // ����������������������ڴ濪��������׼��servlet engine���������������Σ�
            // ֻ������servlet engine��Ҫ����������resin����
            if (writerAdapter != null) {
                return writerAdapter;
            } else {
                log.debug("Attampt to getOutputStream after calling getWriter.  This may cause unnecessary system cost.");
                writerAdapter = new WriterOutputStream(writer, getCharacterEncoding());
                return writerAdapter;
            }
        }

        if (buffering) {
            // ע�⣬servletStreamһ���������Ͳ��ı䣬
            // �����Ҫ�ı䣬ֻ��Ҫ�ı��������bytes�����ɡ�
            if (bytesStack == null) {
                bytesStack = new Stack<ByteArrayOutputStream>();
            }

            ByteArrayOutputStream bytes = new ByteArrayOutputStream();

            bytesStack.push(bytes);
            stream = new BufferedServletOutputStream(bytes);

            log.debug("Created new byte buffer");
        } else {
            stream = super.getOutputStream();
        }

        return stream;
    }

    /**
     * ȡ������ַ�����
     *
     * @return response������ַ���
     * @throws IOException �������ʧ��
     */
    @Override
    public PrintWriter getWriter() throws IOException {
        if (writer != null) {
            return writer;
        }

        if (stream != null) {
            // ���getOutputStream�����Ѿ������ã���streamת����PrintWriter��
            // ����������������������ڴ濪��������׼��servlet engine���������������Σ�
            // ֻ������servlet engine��Ҫ����������resin����
            if (streamAdapter != null) {
                return streamAdapter;
            } else {
                log.debug("Attampt to getWriter after calling getOutputStream.  This may cause unnecessary system cost.");
                streamAdapter = new PrintWriter(new OutputStreamWriter(stream, getCharacterEncoding()), true);
                return streamAdapter;
            }
        }

        if (buffering) {
            // ע�⣬servletWriterһ���������Ͳ��ı䣬
            // �����Ҫ�ı䣬ֻ��Ҫ�ı��������chars�����ɡ�
            if (charsStack == null) {
                charsStack = new Stack<StringWriter>();
            }

            StringWriter chars = new StringWriter();

            charsStack.push(chars);
            writer = new BufferedServletWriter(chars);

            log.debug("Created new character buffer");
        } else {
            writer = super.getWriter();
        }

        return writer;
    }

    /**
     * ����content���ȡ��õ���ֻ��<code>setBuffering(false)</code>ʱ��Ч��
     *
     * @param length content����
     */
    @Override
    public void setContentLength(int length) {
        if (!buffering) {
            super.setContentLength(length);
        }
    }

    /**
     * ��ϴbuffer��
     *
     * @throws IOException ���ʧ��
     */
    @Override
    public void flushBuffer() throws IOException {
        if (buffering) {
            flushBufferAdapter();

            if (writer != null) {
                writer.flush();
            } else if (stream != null) {
                stream.flush();
            }
        } else {
            super.flushBuffer();
        }
    }

    /**
     * �������buffers����������ʾ������Ϣ��
     *
     * @throws IllegalStateException ���response�Ѿ�commit
     */
    @Override
    public void resetBuffer() {
        if (buffering) {
            flushBufferAdapter();

            if (stream != null) {
                bytesStack.clear();
                bytesStack.add(new ByteArrayOutputStream());
                ((BufferedServletOutputStream) stream).updateOutputStream(bytesStack.peek());
            }

            if (writer != null) {
                charsStack.clear();
                charsStack.add(new StringWriter());
                ((BufferedServletWriter) writer).updateWriter(charsStack.peek());
            }
        }

        super.resetBuffer();
    }

    /**
     * �����Ƿ�������Ϣ�������ڴ��С�
     *
     * @return ����ǣ��򷵻�<code>true</code>
     */
    public boolean isBuffering() {
        return buffering;
    }

    /**
     * ����bufferģʽ��������ó�<code>true</code>����ʾ��������Ϣ�������ڴ��У�����ֱ�������ԭʼresponse�С�
     * <p>
     * �˷���������<code>getOutputStream</code>��<code>getWriter</code>����֮ǰִ�У������׳�
     * <code>IllegalStateException</code>��
     * </p>
     *
     * @param buffering �Ƿ�buffer����
     * @throws IllegalStateException <code>getOutputStream</code>��
     *             <code>getWriter</code>�����Ѿ���ִ��
     */
    public void setBuffering(boolean buffering) {
        if (stream == null && writer == null) {
            if (this.buffering != buffering) {
                this.buffering = buffering;
                log.debug("Set buffering " + (buffering ? "on" : "off"));
            }
        } else {
            if (this.buffering != buffering) {
                throw new IllegalStateException(
                        "Unable to change the buffering mode since the getOutputStream() or getWriter() method has been called");
            }
        }
    }

    /**
     * �����µ�buffer�������ϵ�buffer��
     *
     * @throws IllegalStateException �������bufferģʽ����<code>getWriter</code>
     *             �����������ã���<code>getOutputStream</code>������δ������
     */
    public void pushBuffer() {
        if (!buffering) {
            throw new IllegalStateException("Buffering mode is required to pushBuffer");
        }

        if (stream == null && writer == null) {
            throw new IllegalStateException("getOutputStream() or getWriter() method has not been called yet");
        }

        flushBufferAdapter();

        // ��stream��writer stack��ѹ���µ�buffer��
        if (stream != null) {
            ByteArrayOutputStream bytes = new ByteArrayOutputStream();

            bytesStack.push(bytes);

            ((BufferedServletOutputStream) stream).updateOutputStream(bytesStack.peek());

            log.debug("Pushed new byte buffer (stack size is " + bytesStack.size() + ")");
        } else {
            StringWriter chars = new StringWriter();

            charsStack.push(chars);

            ((BufferedServletWriter) writer).updateWriter(charsStack.peek());

            log.debug("Pushed new character buffer (stack size is " + charsStack.size() + ")");
        }
    }

    /**
     * ���������buffer�������ջ��ֻ��һ��buffer���򵯳����ٴ���һ���µġ�
     *
     * @return �����buffer���ݣ����<code>getOutputStream</code>������δ�����ã��򷵻ؿյ�byte array
     * @throws IllegalStateException �������bufferģʽ����<code>getWriter</code>������������
     */
    public ByteArray popByteBuffer() {
        if (!buffering) {
            throw new IllegalStateException("Buffering mode is required to popByteBuffer");
        }

        if (writer != null) {
            throw new IllegalStateException("Unable to popByteBuffer() since the getWriter() method has been called");
        }

        if (stream == null) {
            return new ByteArray(EMPTY_BYTE_ARRAY, 0, 0);
        } else {
            flushBufferAdapter();

            ByteArrayOutputStream block = bytesStack.pop();

            if (bytesStack.size() == 0) {
                bytesStack.push(new ByteArrayOutputStream());
            }

            ((BufferedServletOutputStream) stream).updateOutputStream(bytesStack.peek());

            log.debug("Popped the last byte buffer (stack size is " + bytesStack.size() + ")");

            return block.toByteArray();
        }
    }

    /**
     * ���������buffer�������ջ��ֻ��һ��buffer���򵯳����ٴ���һ���µġ�
     *
     * @return �����buffer���ݣ����<code>getWriter</code>������δ�����ã��򷵻ؿյ��ַ���
     * @throws IllegalStateException �������bufferģʽ����<code>getOutputStream</code>
     *             ������������
     */
    public String popCharBuffer() {
        if (!buffering) {
            throw new IllegalStateException("Buffering mode is required to popCharBuffer");
        }

        if (stream != null) {
            throw new IllegalStateException(
                    "Unable to popCharBuffer() since the getOutputStream() method has been called");
        }

        if (writer == null) {
            return EMPTY_STRING;
        } else {
            flushBufferAdapter();

            StringWriter block = charsStack.pop();

            if (charsStack.size() == 0) {
                charsStack.push(new StringWriter());
            }

            ((BufferedServletWriter) writer).updateWriter(charsStack.peek());

            log.debug("Popped the last character buffer (stack size is " + charsStack.size() + ")");

            return block.toString();
        }
    }

    /**
     * ��buffer�е������ύ��������servlet������С�
     * <p>
     * �������û��ִ�й�<code>getOutputStream</code>��<code>getWriter</code>
     * ��������÷��������κ����顣
     * </p>
     *
     * @throws IOException ����������ʧ��
     * @throws IllegalStateException ���������bufferģʽ����bufferջ�в�ֹһ��buffer
     */
    public void commitBuffer() throws IOException {
        if (stream == null && writer == null) {
            return;
        }

        if (!buffering) {
            throw new IllegalStateException("Buffering mode is required for commitBuffer");
        }

        // ���bytes
        if (stream != null) {
            if (bytesStack.size() > 1) {
                throw new IllegalStateException("More than 1 byte-buffers in the stack");
            }

            flushBufferAdapter();

            OutputStream ostream = super.getOutputStream();
            ByteArray bytes = popByteBuffer();

            bytes.writeTo(ostream);

            log.debug("Committed buffered bytes to the Servlet output stream");
        }

        // ���chars
        if (writer != null) {
            if (charsStack.size() > 1) {
                throw new IllegalStateException("More than 1 char-buffers in the stack");
            }

            flushBufferAdapter();

            PrintWriter writer = super.getWriter();
            String chars = popCharBuffer();

            writer.write(chars);

            log.debug("Committed buffered characters to the Servlet writer");
        }
    }

    /**
     * ��ϴbuffer adapter��ȷ��adapter�е���Ϣ��д��buffer�С�
     */
    private void flushBufferAdapter() {
        if (streamAdapter != null) {
            streamAdapter.flush();
        }

        if (writerAdapter != null) {
            try {
                writerAdapter.flush();
            } catch (IOException e) {
            }
        }
    }

    /**
     * ��<code>LinkedList</code>�̳е�stack������<code>java.util.Stack</code>
     * ��synchronized�Ĵ��ۡ�
     */
    private static class Stack<T> {
        private final LinkedList<T> list = createLinkedList();

        public T peek() {
            if (list.isEmpty()) {
                throw new EmptyStackException();
            }

            return list.getLast();
        }

        public void push(T object) {
            list.addLast(object);
        }

        public T pop() {
            if (list.isEmpty()) {
                throw new EmptyStackException();
            }

            return list.removeLast();
        }

        public int size() {
            return list.size();
        }

        public boolean add(T o) {
            return list.add(o);
        }

        public void clear() {
            list.clear();
        }
    }

    /**
     * ����һ�������ݱ������ڴ��е�<code>ServletOutputStream</code>��
     */
    private static class BufferedServletOutputStream extends ServletOutputStream {
        private ByteArrayOutputStream bytes;

        public BufferedServletOutputStream(ByteArrayOutputStream bytes) {
            this.bytes = bytes;
        }

        public void updateOutputStream(ByteArrayOutputStream bytes) {
            this.bytes = bytes;
        }

        @Override
        public void write(int b) throws IOException {
            bytes.write((byte) b);
        }

        @Override
        public void write(byte[] b) throws IOException {
            write(b, 0, b.length);
        }

        @Override
        public void write(byte[] b, int off, int len) throws IOException {
            bytes.write(b, off, len);
        }

        @Override
        public void flush() throws IOException {
            bytes.flush();
        }

        @Override
        public void close() throws IOException {
            bytes.flush();
            bytes.close();
        }
    }

    /**
     * ����һ�������ݱ������ڴ��е�<code>PrintWriter</code>��
     */
    private static class BufferedServletWriter extends PrintWriter {
        public BufferedServletWriter(StringWriter chars) {
            super(chars);
        }

        public void updateWriter(StringWriter chars) {
            this.out = chars;
        }
    }

    /**
     * ��<code>Writer</code>���䵽<code>ServletOutputStream</code>��
     */
    private static class WriterOutputStream extends ServletOutputStream {
        private ByteArrayOutputStream buffer = new ByteArrayOutputStream();
        private Writer writer;
        private String charset;

        public WriterOutputStream(Writer writer, String charset) {
            this.writer = writer;
            this.charset = defaultIfNull(charset, "ISO-8859-1");
        }

        @Override
        public void write(int b) throws IOException {
            buffer.write((byte) b);
        }

        @Override
        public void write(byte[] b) throws IOException {
            buffer.write(b, 0, b.length);
        }

        @Override
        public void write(byte[] b, int off, int len) throws IOException {
            buffer.write(b, off, len);
        }

        @Override
        public void flush() throws IOException {
            ByteArray bytes = buffer.toByteArray();

            if (bytes.getLength() > 0) {
                ByteArrayInputStream inputBytes = new ByteArrayInputStream(bytes.getRawBytes(), bytes.getOffset(),
                        bytes.getLength());
                InputStreamReader reader = new InputStreamReader(inputBytes, charset);

                StreamUtil.io(reader, writer, true, false);
                writer.flush();

                buffer.reset();
            }
        }

        @Override
        public void close() throws IOException {
            this.flush();
        }
    }
}
TOP

Related Classes of com.alibaba.citrus.service.requestcontext.buffered.impl.BufferedResponseImpl$WriterOutputStream

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.