net.sourceforge.jtds.jdbc.ResponseStream Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jtds Show documentation
Show all versions of jtds Show documentation
jTDS is an open source 100% pure Java (type 4) JDBC 3.0 driver
for Microsoft SQL Server (6.5, 7, 2000, 2005, 2008, 2012) and Sybase ASE
(10, 11, 12, 15). jTDS is based on FreeTDS and is currently the fastest
production-ready JDBC driver for SQL Server and Sybase. jTDS is 100% JDBC
3.0 compatible, supporting forward-only and scrollable/updateable ResultSets
and implementing all the DatabaseMetaData and ResultSetMetaData methods.
// jTDS JDBC Driver for Microsoft SQL Server and Sybase
// Copyright (C) 2004 The jTDS Project
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
package net.sourceforge.jtds.jdbc;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.io.UnsupportedEncodingException;
import net.sourceforge.jtds.util.*;
/**
* Implements an input stream for the server response.
*
* Implementation note:
*
* - This class contains methods to read different types of data from the
* server response stream in TDS format.
*
- Character translation of String items is carried out.
*
*
* @author Mike Hutchinson.
* @version $Id: ResponseStream.java,v 1.20 2005/10/27 13:22:33 alin_sinpalean Exp $
*/
public class ResponseStream {
/** The shared network socket. */
private final SharedSocket socket;
/** The Input packet buffer. */
private byte[] buffer;
/** The offset of the next byte to read. */
private int bufferPtr;
/** The length of current input packet. */
private int bufferLen;
/** The unique stream id. */
private final int streamId;
/** True if stream is closed. */
private boolean isClosed;
/** A shared byte buffer. */
private final byte[] byteBuffer = new byte[255];
/** A shared char buffer. */
private final char[] charBuffer = new char[255];
/**
* Constructs a RequestStream
object.
*
* @param socket the shared socket object to write to
* @param streamId the unique id for this stream (from ResponseStream)
* @param bufferSize the initial buffer size
*/
ResponseStream(SharedSocket socket, int streamId, int bufferSize){
this.streamId = streamId;
this.socket = socket;
this.buffer = new byte[bufferSize];
this.bufferLen = bufferSize;
this.bufferPtr = bufferSize;
}
/**
* Retrieves the unique stream id.
*
* @return the unique stream id as an int
*/
int getStreamId() {
return this.streamId;
}
/**
* Retrieves the next input byte without reading forward.
*
* @return the next byte in the input stream as an int
* @throws IOException if an I/O error occurs
*/
int peek() throws IOException {
int b = read();
bufferPtr--; // Backup one
return b;
}
/**
* Reads the next input byte from the server response stream.
*
* @return the next byte in the input stream as an int
* @throws IOException if an I/O error occurs
*/
int read() throws IOException {
if (bufferPtr >= bufferLen) {
getPacket();
}
return (int) buffer[bufferPtr++] & 0xFF;
}
/**
* Reads a byte array from the server response stream.
*
* @param b the byte array to read into
* @return the number of bytes read as an int
* @throws IOException if an I/O error occurs
*/
int read(byte[] b) throws IOException {
return read(b, 0, b.length);
}
/**
* Reads a byte array from the server response stream, specifying a start
* offset and length.
*
* @param b the byte array
* @param off the starting offset in the array
* @param len the number of bytes to read
* @return the number of bytes read as an int
* @throws IOException if an I/O error occurs
*/
int read(byte[] b, int off, int len) throws IOException {
int bytesToRead = len;
while (bytesToRead > 0) {
if (bufferPtr >= bufferLen) {
getPacket();
}
int available = bufferLen - bufferPtr;
int bc = (available > bytesToRead) ? bytesToRead : available;
System.arraycopy(buffer, bufferPtr, b, off, bc);
off += bc;
bytesToRead -= bc;
bufferPtr += bc;
}
return len;
}
/**
* Reads a char array from the server response stream.
*
* @param c the char array
* @return the byte array as a byte[]
* @throws IOException if an I/O error occurs
*/
int read(char[] c) throws IOException {
for (int i = 0; i < c.length; i++) {
if (bufferPtr >= bufferLen) {
getPacket();
}
int b1 = buffer[bufferPtr++] & 0xFF;
if (bufferPtr >= bufferLen) {
getPacket();
}
int b2 = buffer[bufferPtr++] << 8;
c[i] = (char) (b2 | b1);
}
return c.length;
}
/**
* Reads a String
object from the server response stream. If
* the TDS protocol version is 4.2 or 5.0 decode the string use the default
* server charset, otherwise use UCS2-LE (Unicode).
*
* @param len the length of the string to read in bytes in the case
* of TDS 4.2/5.0 and in characters for TDS 7.0+
* (UCS2-LE encoded strings)
* @return the result as a String
* @throws IOException if an I/O error occurs
*/
String readString(int len) throws IOException {
if (socket.getTdsVersion() >= Driver.TDS70) {
return readUnicodeString(len);
}
return readNonUnicodeString(len);
}
/**
* Skips a String
from the server response stream. If the TDS
* protocol version is 4.2 or 5.0 len
is the length in bytes,
* otherwise it's the length in UCS2-LE characters (length in bytes == 2 *
* len
).
*
* @param len the length of the string to skip in bytes in the case
* of TDS 4.2/5.0 and in characters for TDS 7.0+
* (UCS2-LE encoded strings)
* @throws IOException if an I/O error occurs
*/
void skipString(int len) throws IOException {
if (len <= 0) {
return;
}
if (socket.getTdsVersion() >= Driver.TDS70) {
skip(len * 2);
} else {
skip(len);
}
}
/**
* Reads a UCS2-LE (Unicode) encoded String object from the server response
* stream.
*
* @param len the length of the string to read in characters
* @return the result as a String
* @throws IOException if an I/O error occurs
*/
String readUnicodeString(int len) throws IOException {
char[] chars = (len > charBuffer.length) ? new char[len] : charBuffer;
for (int i = 0; i < len; i++) {
if (bufferPtr >= bufferLen) {
getPacket();
}
int b1 = buffer[bufferPtr++] & 0xFF;
if (bufferPtr >= bufferLen) {
getPacket();
}
int b2 = buffer[bufferPtr++] << 8;
chars[i] = (char) (b2 | b1);
}
return new String(chars, 0, len);
}
/**
* Reads a non Unicode String
from the server response stream,
* creating the String
from a translated byte
* array.
*
* @param len the length of the string to read in bytes
* @return the result as a String
* @throws IOException if an I/O error occurs
*/
String readNonUnicodeString(int len) throws IOException {
CharsetInfo info = socket.getCharsetInfo();
return readString(len, info);
}
/**
* Reads a String
from the server response stream, translating
* it from a byte
array using the specified character set.
*
* @param len the length of the string to read in bytes
* @return the result as a String
* @throws IOException if an I/O error occurs
*/
String readNonUnicodeString(int len, CharsetInfo charsetInfo)
throws IOException {
return readString(len, charsetInfo);
}
/**
* Reads a String
from the server response stream, creating
* it from a translated byte
array.
*
* @param len the length of the string to read in bytes
* @param info descriptor of the charset to use
* @return the result as a String
* @throws IOException if an I/O error occurs
*/
String readString(int len, CharsetInfo info) throws IOException {
String charsetName = info.getCharset();
byte[] bytes = (len > byteBuffer.length) ? new byte[len] : byteBuffer;
read(bytes, 0, len);
try {
return new String(bytes, 0, len, charsetName);
} catch (UnsupportedEncodingException e) {
return new String(bytes, 0, len);
}
}
/**
* Reads a short
value from the server response stream.
*
* @return the result as a short
* @throws IOException if an I/O error occurs
*/
short readShort() throws IOException {
int b1 = read();
return (short) (b1 | (read() << 8));
}
/**
* Reads an int
value from the server response stream.
*
* @return the result as a int
* @throws IOException if an I/O error occurs
*/
int readInt() throws IOException {
int b1 = read();
int b2 = read() << 8;
int b3 = read() << 16;
int b4 = read() << 24;
return b4 | b3 | b2 | b1;
}
/**
* Reads a long
value from the server response stream.
*
* @return the result as a long
* @throws IOException if an I/O error occurs
*/
long readLong() throws IOException {
long b1 = ((long) read());
long b2 = ((long) read()) << 8;
long b3 = ((long) read()) << 16;
long b4 = ((long) read()) << 24;
long b5 = ((long) read()) << 32;
long b6 = ((long) read()) << 40;
long b7 = ((long) read()) << 48;
long b8 = ((long) read()) << 56;
return b1 | b2 | b3 | b4 | b5 | b6 | b7 | b8;
}
/**
* Reads an unsigned long
value from the server response stream.
*
* @return the result as a BigDecimal
* @throws IOException if an I/O error occurs
*/
BigDecimal readUnsignedLong() throws IOException {
int b1 = ((int) read() & 0xFF);
long b2 = ((long) read());
long b3 = ((long) read()) << 8;
long b4 = ((long) read()) << 16;
long b5 = ((long) read()) << 24;
long b6 = ((long) read()) << 32;
long b7 = ((long) read()) << 40;
long b8 = ((long) read()) << 48;
// Convert via String as BigDecimal(long) is actually BigDecimal(double)
// on older versions of java
return new BigDecimal(Long.toString(b2 | b3 | b4 | b5 | b6 | b7 | b8))
.multiply(new BigDecimal(256))
.add(new BigDecimal(b1));
}
/**
* Discards bytes from the server response stream.
*
* @param skip the number of bytes to discard
* @return the number of bytes skipped
*/
int skip(int skip) throws IOException {
int tmp = skip;
while (skip > 0) {
if (bufferPtr >= bufferLen) {
getPacket();
}
int available = bufferLen - bufferPtr;
if (skip > available) {
skip -= available;
bufferPtr = bufferLen;
} else {
bufferPtr += skip;
skip = 0;
}
}
return tmp;
}
/**
* Consumes the rest of the server response, without parsing it.
*
* Note: Use only in extreme cases, packets will not be parsed and
* could leave the connection in an inconsistent state.
*/
void skipToEnd() {
try {
// No more data to read.
bufferPtr = bufferLen;
// Now consume all data until we get an exception.
while (true) {
buffer = socket.getNetPacket(streamId, buffer);
}
} catch (IOException ex) {
// Ignore it. Probably no more packets.
}
}
/**
* Closes this response stream. The stream id is unlinked from the
* underlying shared socket as well.
*/
void close() {
isClosed = true;
socket.closeStream(streamId);
}
/**
* Retrieves the TDS version number.
*
* @return the TDS version as an int
*/
int getTdsVersion() {
return socket.getTdsVersion();
}
/**
* Retrieves the server type.
*
* @return the server type as an int
*/
int getServerType() {
return socket.serverType;
}
/**
* Creates a simple InputStream
over the server response.
*
* This method can be used to obtain a stream which can be passed to
* InputStreamReader
s to assist in reading multi byte
* character sets.
*
* @param len the number of bytes available in the server response
* @return the InputStream
built over the server response
*/
InputStream getInputStream(int len) {
return new TdsInputStream(this, len);
}
/**
* Read the next TDS packet from the network.
*
* @throws IOException if an I/O error occurs
*/
private void getPacket() throws IOException {
while (bufferPtr >= bufferLen) {
if (isClosed) {
throw new IOException("ResponseStream is closed");
}
buffer = socket.getNetPacket(streamId, buffer);
bufferLen = (((int) buffer[2] & 0xFF) << 8) | ((int) buffer[3] & 0xFF);
bufferPtr = TdsCore.PKT_HDR_LEN;
if (Logger.isActive()) {
Logger.logPacket(streamId, true, buffer);
}
}
}
/**
* Simple inner class implementing an InputStream
over the
* server response.
*/
private static class TdsInputStream extends InputStream {
/** The underlying ResponseStream
. */
ResponseStream tds;
/** The maximum amount of data to make available. */
int maxLen;
/**
* Creates a TdsInputStream
instance.
*
* @param tds the underlying ResponseStream
* @param maxLen the maximum amount of data that will be available
*/
public TdsInputStream(ResponseStream tds, int maxLen) {
this.tds = tds;
this.maxLen = maxLen;
}
public int read() throws IOException {
return (maxLen-- > 0)? tds.read(): -1;
}
public int read(byte[] bytes, int offset, int len) throws IOException {
if (maxLen < 1) {
return -1;
} else {
int bc = Math.min(maxLen, len);
if (bc > 0) {
bc = tds.read(bytes, offset, bc);
maxLen -= (bc == -1) ? 0 : bc;
}
return bc;
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy