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

org.postgresql.util.StreamWrapper Maven / Gradle / Ivy

/*-------------------------------------------------------------------------
*
* Copyright (c) 2004-2014, PostgreSQL Global Development Group
* Copyright (c) 2004, Open Cloud Limited.
*
*
*-------------------------------------------------------------------------
*/
package org.postgresql.util;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

/**
 * Wrapper around a length-limited InputStream.
 *
 * @author Oliver Jowett ([email protected])
 */
public class StreamWrapper {

    private static final int MAX_MEMORY_BUFFER_BYTES = 51200;
    
    private static final String TEMP_FILE_PREFIX = "postgres-pgjdbc-stream";

    public StreamWrapper(byte[] data, int offset, int length) {
        this.stream = null;
        this.rawData = data;
        this.offset = offset;
        this.length = length;
    }

    public StreamWrapper(InputStream stream, int length) {
        this.stream = stream;
        this.rawData = null;
        this.offset = 0;
        this.length = length;
    }

    public StreamWrapper(InputStream stream) throws PSQLException
    {
        try
        {
            ByteArrayOutputStream memoryOutputStream = new ByteArrayOutputStream();
            final int memoryLength = copyStream(stream, memoryOutputStream, MAX_MEMORY_BUFFER_BYTES);
            byte[] rawData = memoryOutputStream.toByteArray();

            if (memoryLength == -1)
            {
                final int diskLength;
                final File tempFile = File.createTempFile(TEMP_FILE_PREFIX, null);
                FileOutputStream diskOutputStream = new FileOutputStream(tempFile);
                diskOutputStream.write(rawData);
                try
                {
                    diskLength = copyStream(stream, diskOutputStream, Integer.MAX_VALUE - rawData.length);
                    if (diskLength == -1)
                    {
                        throw new PSQLException(GT.tr("Object is too large to send over the protocol."), PSQLState.NUMERIC_CONSTANT_OUT_OF_RANGE);
                    }
                    diskOutputStream.flush();
                }
                finally
                {
                    diskOutputStream.close();
                }
                this.offset = 0;
                this.length = rawData.length + diskLength;
                this.rawData = null;
                this.stream = new FileInputStream(tempFile)
                {
                    /* Usually, closing stream should be done by pgjdbc clients.
                     * Here it's an internally managed stream so we need to auto-close
                     * it and be sure to delete the temporary file when doing so.
                     * Auto-closing will be done when the first occur: reaching EOF or
                     * Garbage Collection
                     */
                    private boolean _closed = false;
                    private int _position = 0;

                    /**
                     * Check if we should auto-close this stream
                     */
                    private void checkShouldClose(int readResult) throws IOException
                    {
                        if (readResult == -1)
                        {
                            close();
                        }
                        else
                        {
                            _position += readResult;
                            if (_position >= length)
                            {
                                close();
                            }
                        }
                    }

                    public int read(byte[] b) throws IOException
                    {
                        if (_closed)
                        {
                            return -1;
                        }
                        int result = super.read(b);
                        checkShouldClose(result);
                        return result;
                    }

                    public int read(byte[] b, int off, int len) throws IOException
                    {
                        if (_closed)
                        {
                            return -1;
                        }
                        int result = super.read(b, off, len);
                        checkShouldClose(result);
                        return result;
                    }

                    public void close() throws IOException
                    {
                        if (!_closed)
                        {
                            super.close();
                            tempFile.delete();
                            _closed = true;
                        }
                    }

                    protected void finalize() throws IOException
                    {
                        // forcibly close it because super.finalize() may keep the FD open, which may prevent file deletion
                        close();
                        super.finalize();
                    }
                };
            }
            else
            {
                this.rawData = rawData;
                this.stream = null;
                this.offset = 0;
                this.length = rawData.length;
            }
        }
        catch (IOException e)
        {
            throw new PSQLException(GT.tr("An I/O error occurred while sending to the backend."), PSQLState.IO_ERROR, e);
        }
    }

    public InputStream getStream() {
        if (stream != null)
            return stream;

        return new java.io.ByteArrayInputStream(rawData, offset, length);
    }

    public int getLength() {
        return length;
    }

    public int getOffset() {
        return offset;
    }

    public byte[] getBytes() {
        return rawData;
    }

    public String toString() {
        return "";
    }

    private int copyStream(InputStream inputStream, OutputStream outputStream, int limit)
        throws IOException
    {
        int totalLength = 0;
        byte[] buffer = new byte[2048];
        int readLength = inputStream.read(buffer);
        while (readLength > 0)
        {
            totalLength += readLength;
            outputStream.write(buffer, 0, readLength);
            if (totalLength >= limit)
            {
                return -1;
            }
            readLength = inputStream.read(buffer);
        }
        return totalLength;
    }

    private final InputStream stream;
    private final byte[] rawData;
    private final int offset;
    private final int length;
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy