com.microsoft.sqlserver.jdbc.SimpleInputStream Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of mssql-jdbc Show documentation
Show all versions of mssql-jdbc Show documentation
Microsoft JDBC Driver for SQL Server.
//---------------------------------------------------------------------------------------------------------------------------------
// File: SimpleInputStream.java
//
//
// Microsoft JDBC Driver for SQL Server
// Copyright(c) Microsoft Corporation
// All rights reserved.
// MIT License
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the ""Software""),
// to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and / or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions :
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//---------------------------------------------------------------------------------------------------------------------------------
package com.microsoft.sqlserver.jdbc;
import java.io.*;
/**
* 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 int lastLoggingID = 0;
private synchronized static int nextLoggingID() { return ++lastLoggingID; }
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.
*/
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.
*/
private final 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=0;
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;
}
}