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

com.microsoft.sqlserver.jdbc.SimpleInputStream Maven / Gradle / Ivy

Go to download

Microsoft JDBC Driver for SQL Server. The Azure Key Vault feature in Microsoft JDBC Driver for SQL Server depends on Azure SDK for JAVA and Azure Active Directory Library For Java.

There is a newer version: 12.7.0.jre11-preview
Show newest version
/*
 * Microsoft JDBC Driver for SQL Server
 * 
 * Copyright(c) Microsoft Corporation All rights reserved.
 * 
 * This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
 */

package com.microsoft.sqlserver.jdbc;

import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * SimpleInputStream is an InputStream implementation that reads from TDS.
 * 
 * This class is to support adaptive streaming of non plp aka simple byte types char, byte etc.
 * 
 */
abstract class BaseInputStream extends InputStream {
    abstract byte[] getBytes() throws SQLServerException;

    // Flag indicating whether the stream conforms to adaptive response buffering API restrictions
    final boolean isAdaptive;

    // Flag indicating whether the stream consumes and discards data as it reads it
    final boolean isStreaming;

    /** Generate the logging ID */
    private String parentLoggingInfo = "";
    private static final AtomicInteger lastLoggingID = new AtomicInteger(0);

    private static int nextLoggingID() {
        return lastLoggingID.incrementAndGet();
    }

    static final java.util.logging.Logger logger = java.util.logging.Logger.getLogger("com.microsoft.sqlserver.jdbc.internals.InputStream");;
    private String traceID;

    final public String toString() {
        if (traceID == null)
            traceID = getClass().getName() + "ID:" + nextLoggingID();
        return traceID;
    }

    final void setLoggingInfo(String info) {
        parentLoggingInfo = info;
        if (logger.isLoggable(java.util.logging.Level.FINER))
            logger.finer(toString());
    }

    int streamPos = 0;
    int markedStreamPos = 0;
    TDSReaderMark currentMark;
    private ServerDTVImpl dtv;
    TDSReader tdsReader;
    int readLimit = 0;
    boolean isReadLimitSet = false;

    BaseInputStream(TDSReader tdsReader,
            boolean isAdaptive,
            boolean isStreaming,
            ServerDTVImpl dtv) {
        this.tdsReader = tdsReader;
        this.isAdaptive = isAdaptive;
        this.isStreaming = isStreaming;

        if (isAdaptive)
            clearCurrentMark();
        else
            currentMark = tdsReader.mark();
        this.dtv = dtv;
    }

    final void clearCurrentMark() {
        currentMark = null;
        isReadLimitSet = false;
        if (isAdaptive && isStreaming)
            tdsReader.stream();
    }

    void closeHelper() throws IOException {
        if (isAdaptive && null != dtv) {
            if (logger.isLoggable(java.util.logging.Level.FINER))
                logger.finer(toString() + " closing the adaptive stream.");
            dtv.setPositionAfterStreamed(tdsReader);
        }
        currentMark = null;
        tdsReader = null;
        dtv = null;
    }

    /**
     * Verifies stream is open and throws IOException if otherwise.
     */
    final void checkClosed() throws IOException {
        if (null == tdsReader)
            throw new IOException(SQLServerException.getErrString("R_streamIsClosed"));
    }

    /**
     * Tests if this input stream supports the mark and reset methods.
     * 
     * @return true if mark and reset are supported.
     */
    public boolean markSupported() {
        return true;
    }

    void setReadLimit(int readLimit) {
        // we buffer the whole stream in the full case so readlimit is meaningless.
        // spec does not say what to do with -ve values.
        if (isAdaptive && readLimit > 0) {
            this.readLimit = readLimit;
            isReadLimitSet = true;
        }
    }

    /**
     * Resets stream to saved mark position.
     * 
     * @exception IOException
     *                if an I/O error occurs.
     */
    void resetHelper() throws IOException {
        checkClosed();
        // if no mark set already throw
        if (null == currentMark)
            throw new IOException(SQLServerException.getErrString("R_streamWasNotMarkedBefore"));
        tdsReader.reset(currentMark);
    }
}

final class SimpleInputStream extends BaseInputStream {

    // Stated length of the payload
    private final int payloadLength;

    /**
     * Initializes the input stream.
     */
    SimpleInputStream(TDSReader tdsReader,
            int payLoadLength,
            InputStreamGetterArgs getterArgs,
            ServerDTVImpl dtv) throws SQLServerException {
        super(tdsReader, getterArgs.isAdaptive, getterArgs.isStreaming, dtv);
        setLoggingInfo(getterArgs.logContext);
        this.payloadLength = payLoadLength;
    }

    /**
     * Closes the stream releasing all resources held.
     * 
     * @exception IOException
     *                if an I/O error occurs.
     */
    public void close() throws IOException {
        if (null == tdsReader)
            return;
        if (logger.isLoggable(java.util.logging.Level.FINER))
            logger.finer(toString() + "Enter Closing SimpleInputStream.");

        // Discard the remainder of the stream, positioning the TDSReader
        // at the next item in the TDS response. Once the stream is closed,
        // it can no longer access the discarded response data.
        skip(payloadLength - streamPos);

        closeHelper();
        if (logger.isLoggable(java.util.logging.Level.FINER))
            logger.finer(toString() + "Exit Closing SimpleInputStream.");
    }

    /**
     * Checks if we have EOS state.
     * 
     * @exception IOException
     *                if an I/O error occurs.
     */
    private boolean isEOS() throws IOException {
        assert streamPos <= payloadLength;
        return (streamPos == payloadLength);
    }

    // java.io.InputStream interface methods.

    /**
     * Skips over and discards n bytes of data from this input stream.
     * 
     * @param n
     *            the number of bytes to be skipped.
     * @return the actual number of bytes skipped.
     * @exception IOException
     *                if an I/O error occurs.
     */
    public long skip(long n) throws IOException {
        checkClosed();
        if (logger.isLoggable(java.util.logging.Level.FINER))
            logger.finer(toString() + " Skipping :" + n);
        if (n < 0)
            return 0L;
        if (isEOS())
            return 0;

        int skipAmount;
        if (streamPos + n > payloadLength) {
            skipAmount = payloadLength - streamPos;
        }
        else {
            skipAmount = (int) n;
        }
        try {
            tdsReader.skip(skipAmount);
        }
        catch (SQLServerException e) {
            throw new IOException(e.getMessage());
        }
        streamPos += skipAmount;
        if (isReadLimitSet && ((streamPos - markedStreamPos) > readLimit))
            clearCurrentMark();

        return skipAmount;

    }

    /**
     * Returns the number of bytes that can be read (or skipped over) from this input stream without blocking by the next caller of a method for this
     * input stream.
     * 
     * @return the actual number of bytes available.
     * @exception IOException
     *                if an I/O error occurs.
     */
    public int available() throws IOException {
        checkClosed();
        assert streamPos <= payloadLength;

        int available = payloadLength - streamPos;
        if (tdsReader.available() < available)
            available = tdsReader.available();
        return available;
    }

    private byte[] bSingleByte;

    /**
     * Reads the next byte of data from the input stream.
     * 
     * @return the byte read or -1 meaning no more bytes.
     * @exception IOException
     *                if an I/O error occurs.
     */
    public int read() throws IOException {
        checkClosed();
        if (null == bSingleByte)
            bSingleByte = new byte[1];
        if (isEOS())
            return -1;
        int bytesRead = read(bSingleByte, 0, 1);
        return (0 == bytesRead) ? -1 : (bSingleByte[0] & 0xFF);
    }

    /**
     * Reads available data into supplied byte array.
     * 
     * @param b
     *            array of bytes to fill.
     * @return the number of bytes read or -1 meaning no bytes read.
     * @exception IOException
     *                if an I/O error occurs.
     */
    public int read(byte[] b) throws IOException {
        checkClosed();
        return read(b, 0, b.length);
    }

    /**
     * Reads available data into supplied byte array.
     * 
     * @param b
     *            array of bytes to fill.
     * @param offset
     *            the offset into array b where to start writing.
     * @param maxBytes
     *            the max number of bytes to write into b.
     * @return the number of bytes read or -1 meaning no bytes read.
     * @exception IOException
     *                if an I/O error occurs.
     */
    public int read(byte b[],
            int offset,
            int maxBytes) throws IOException {
        checkClosed();
        if (logger.isLoggable(java.util.logging.Level.FINER))
            logger.finer(toString() + " Reading " + maxBytes + " from stream offset " + streamPos + " payload length " + payloadLength);

        if (offset < 0 || maxBytes < 0 || offset + maxBytes > b.length)
            throw new IndexOutOfBoundsException();

        if (0 == maxBytes)
            return 0;
        if (isEOS())
            return -1;

        int readAmount;
        if (streamPos + maxBytes > payloadLength) {
            readAmount = payloadLength - streamPos;
        }
        else {
            readAmount = maxBytes;
        }

        try {
            tdsReader.readBytes(b, offset, readAmount);
        }
        catch (SQLServerException e) {
            throw new IOException(e.getMessage());
        }
        streamPos += readAmount;

        if (isReadLimitSet && ((streamPos - markedStreamPos) > readLimit))
            clearCurrentMark();

        return readAmount;
    }

    /**
     * Marks the current position in this input stream.
     * 
     * @param readLimit
     *            the number of bytes to hold
     */
    public void mark(int readLimit) {
        if (null != tdsReader && readLimit > 0) {
            currentMark = tdsReader.mark();
            markedStreamPos = streamPos;
            setReadLimit(readLimit);
        }
    }

    /**
     * Resets stream to saved mark position.
     * 
     * @exception IOException
     *                if an I/O error occurs.
     */
    public void reset() throws IOException {
        resetHelper();
        streamPos = markedStreamPos;
    }

    /**
     * Helper function to convert the entire PLP stream into a contiguous byte array. This call is inefficient (in terms of memory usage and run time)
     * for very large PLPs. Use it only if a contiguous byte array is required.
     */
    final byte[] getBytes() throws SQLServerException {
        // We should always retrieve the entire stream, and only once.
        assert 0 == streamPos;

        byte[] value = new byte[payloadLength];
        try {
            read(value);
            close();
        }
        catch (IOException e) {
            SQLServerException.makeFromDriverError(null, null, e.getMessage(), null, true);
        }

        return value;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy