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

com.github.bedrin.jdbc.sniffer.servlet.BufferedServletOutputStream Maven / Gradle / Ivy

package com.github.bedrin.jdbc.sniffer.servlet;

import javax.servlet.ServletOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URLConnection;
import java.util.Arrays;

class BufferedServletOutputStream extends ServletOutputStream {

    private final BufferedServletResponseWrapper responseWrapper;

    private final ServletOutputStream target;
    private final Buffer buffer = new Buffer();

    private boolean closed;
    private boolean flushed;

    protected BufferedServletOutputStream(BufferedServletResponseWrapper responseWrapper, ServletOutputStream target) {
        this.responseWrapper = responseWrapper;
        this.target = target;
    }

    @Override
    public void flush() throws IOException {
        if (!flushed) {
            // analyze first chunk of data
            String mimeTypeMagic =
                    URLConnection.guessContentTypeFromStream(new ByteArrayInputStream(buffer.toByteArray(16)));
            responseWrapper.notifyBeforeFlush(mimeTypeMagic);
        }

        buffer.writeTo(target);
        target.flush();

        buffer.reset();
        responseWrapper.setCommitted();

        flushed = true;
    }

    public void closeTarget() throws IOException {
        responseWrapper.notifyBeforeClose();
        if (closed) target.close();
    }

    public void reset() {
        checkNotFlushed();
        buffer.reset();
    }

    protected void setBufferSize(int size) {
        checkNotFlushed();
        buffer.ensureCapacity(size);
    }

    protected int getBufferSize() {
        return buffer.getCapacity();
    }

    @Override
    public void close() throws IOException {
        flush();
        responseWrapper.notifyBeforeClose();
        closed = true;
    }

    protected void checkOpen() throws IOException {
        if (closed) throw new IOException("Output Stream is closed");
    }

    protected void checkNotFlushed() throws IllegalStateException {
        if (flushed) throw new IllegalStateException("Output Stream was already sent to client");
    }

    // delegate all calls to buffer

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

    private int maximumBufferSize = 200 * 1024;

    private void flushIfOverflow(int newBytes) throws IOException {
        if (buffer.size() + newBytes > maximumBufferSize) {
            flush();
        }
    }

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

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

    // TODO: flush buffer automatically after some threshold (say 100 kilobytes for start?) or analyze content-length headedr
    private static class Buffer extends ByteArrayOutputStream {

        public synchronized byte[] toByteArray(int maxSize) {
            return Arrays.copyOf(buf, Math.max(count, maxSize));
        }

        public int getCapacity() {
            return null == buf ? 0 : buf.length;
        }

        /**
         * Increases the capacity if necessary to ensure that it can hold
         * at least the number of elements specified by the minimum
         * capacity argument.
         *
         * @param minCapacity the desired minimum capacity
         * @throws OutOfMemoryError if {@code minCapacity < 0}.  This is
         * interpreted as a request for the unsatisfiably large capacity
         * {@code (long) Integer.MAX_VALUE + (minCapacity - Integer.MAX_VALUE)}.
         */
        private void ensureCapacity(int minCapacity) {
            // overflow-conscious code
            if (minCapacity - buf.length > 0)
                grow(minCapacity);
        }

        /**
         * Increases the capacity to ensure that it can hold at least the
         * number of elements specified by the minimum capacity argument.
         *
         * @param minCapacity the desired minimum capacity
         */
        private void grow(int minCapacity) {
            // overflow-conscious code
            int oldCapacity = buf.length;
            int newCapacity = oldCapacity << 1;
            if (newCapacity - minCapacity < 0)
                newCapacity = minCapacity;
            if (newCapacity < 0) {
                if (minCapacity < 0) // overflow
                    throw new OutOfMemoryError();
                newCapacity = Integer.MAX_VALUE;
            }
            buf = Arrays.copyOf(buf, newCapacity);
        }

    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy