001: // jTDS JDBC Driver for Microsoft SQL Server and Sybase
002: // Copyright (C) 2004 The jTDS Project
003: //
004: // This library is free software; you can redistribute it and/or
005: // modify it under the terms of the GNU Lesser General Public
006: // License as published by the Free Software Foundation; either
007: // version 2.1 of the License, or (at your option) any later version.
008: //
009: // This library is distributed in the hope that it will be useful,
010: // but WITHOUT ANY WARRANTY; without even the implied warranty of
011: // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012: // Lesser General Public License for more details.
013: //
014: // You should have received a copy of the GNU Lesser General Public
015: // License along with this library; if not, write to the Free Software
016: // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
017: //
018: package net.sourceforge.jtds.jdbc;
019:
020: import java.io.IOException;
021: import java.io.InputStream;
022: import java.math.BigDecimal;
023: import java.io.UnsupportedEncodingException;
024: import net.sourceforge.jtds.util.*;
025:
026: /**
027: * Implements an input stream for the server response.
028: * <p/>
029: * Implementation note:
030: * <ol>
031: * <li>This class contains methods to read different types of data from the
032: * server response stream in TDS format.
033: * <li>Character translation of String items is carried out.
034: * </ol>
035: *
036: * @author Mike Hutchinson.
037: * @version $Id: ResponseStream.java,v 1.20 2005/10/27 13:22:33 alin_sinpalean Exp $
038: */
039: public class ResponseStream {
040: /** The shared network socket. */
041: private final SharedSocket socket;
042: /** The Input packet buffer. */
043: private byte[] buffer;
044: /** The offset of the next byte to read. */
045: private int bufferPtr;
046: /** The length of current input packet. */
047: private int bufferLen;
048: /** The unique stream id. */
049: private final int streamId;
050: /** True if stream is closed. */
051: private boolean isClosed;
052: /** A shared byte buffer. */
053: private final byte[] byteBuffer = new byte[255];
054: /** A shared char buffer. */
055: private final char[] charBuffer = new char[255];
056:
057: /**
058: * Constructs a <code>RequestStream</code> object.
059: *
060: * @param socket the shared socket object to write to
061: * @param streamId the unique id for this stream (from ResponseStream)
062: * @param bufferSize the initial buffer size
063: */
064: ResponseStream(SharedSocket socket, int streamId, int bufferSize) {
065: this .streamId = streamId;
066: this .socket = socket;
067: this .buffer = new byte[bufferSize];
068: this .bufferLen = bufferSize;
069: this .bufferPtr = bufferSize;
070: }
071:
072: /**
073: * Retrieves the unique stream id.
074: *
075: * @return the unique stream id as an <code>int</code>
076: */
077: int getStreamId() {
078: return this .streamId;
079: }
080:
081: /**
082: * Retrieves the next input byte without reading forward.
083: *
084: * @return the next byte in the input stream as an <code>int</code>
085: * @throws IOException if an I/O error occurs
086: */
087: int peek() throws IOException {
088: int b = read();
089:
090: bufferPtr--; // Backup one
091:
092: return b;
093: }
094:
095: /**
096: * Reads the next input byte from the server response stream.
097: *
098: * @return the next byte in the input stream as an <code>int</code>
099: * @throws IOException if an I/O error occurs
100: */
101: int read() throws IOException {
102: if (bufferPtr >= bufferLen) {
103: getPacket();
104: }
105:
106: return (int) buffer[bufferPtr++] & 0xFF;
107: }
108:
109: /**
110: * Reads a byte array from the server response stream.
111: *
112: * @param b the byte array to read into
113: * @return the number of bytes read as an <code>int</code>
114: * @throws IOException if an I/O error occurs
115: */
116: int read(byte[] b) throws IOException {
117: return read(b, 0, b.length);
118: }
119:
120: /**
121: * Reads a byte array from the server response stream, specifying a start
122: * offset and length.
123: *
124: * @param b the byte array
125: * @param off the starting offset in the array
126: * @param len the number of bytes to read
127: * @return the number of bytes read as an <code>int</code>
128: * @throws IOException if an I/O error occurs
129: */
130: int read(byte[] b, int off, int len) throws IOException {
131: int bytesToRead = len;
132:
133: while (bytesToRead > 0) {
134: if (bufferPtr >= bufferLen) {
135: getPacket();
136: }
137:
138: int available = bufferLen - bufferPtr;
139: int bc = (available > bytesToRead) ? bytesToRead
140: : available;
141:
142: System.arraycopy(buffer, bufferPtr, b, off, bc);
143: off += bc;
144: bytesToRead -= bc;
145: bufferPtr += bc;
146: }
147:
148: return len;
149: }
150:
151: /**
152: * Reads a char array from the server response stream.
153: *
154: * @param c the char array
155: * @return the byte array as a <code>byte[]</code>
156: * @throws IOException if an I/O error occurs
157: */
158: int read(char[] c) throws IOException {
159: for (int i = 0; i < c.length; i++) {
160: if (bufferPtr >= bufferLen) {
161: getPacket();
162: }
163:
164: int b1 = buffer[bufferPtr++] & 0xFF;
165:
166: if (bufferPtr >= bufferLen) {
167: getPacket();
168: }
169:
170: int b2 = buffer[bufferPtr++] << 8;
171:
172: c[i] = (char) (b2 | b1);
173: }
174:
175: return c.length;
176: }
177:
178: /**
179: * Reads a <code>String</code> object from the server response stream. If
180: * the TDS protocol version is 4.2 or 5.0 decode the string use the default
181: * server charset, otherwise use UCS2-LE (Unicode).
182: *
183: * @param len the length of the string to read <b>in bytes</b> in the case
184: * of TDS 4.2/5.0 and <b>in characters</b> for TDS 7.0+
185: * (UCS2-LE encoded strings)
186: * @return the result as a <code>String</code>
187: * @throws IOException if an I/O error occurs
188: */
189: String readString(int len) throws IOException {
190: if (socket.getTdsVersion() >= Driver.TDS70) {
191: return readUnicodeString(len);
192: }
193:
194: return readNonUnicodeString(len);
195: }
196:
197: /**
198: * Skips a <code>String</code> from the server response stream. If the TDS
199: * protocol version is 4.2 or 5.0 <code>len</code> is the length in bytes,
200: * otherwise it's the length in UCS2-LE characters (length in bytes == 2 *
201: * <code>len</code>).
202: *
203: * @param len the length of the string to skip <b>in bytes</b> in the case
204: * of TDS 4.2/5.0 and <b>in characters</b> for TDS 7.0+
205: * (UCS2-LE encoded strings)
206: * @throws IOException if an I/O error occurs
207: */
208: void skipString(int len) throws IOException {
209: if (len <= 0) {
210: return;
211: }
212:
213: if (socket.getTdsVersion() >= Driver.TDS70) {
214: skip(len * 2);
215: } else {
216: skip(len);
217: }
218: }
219:
220: /**
221: * Reads a UCS2-LE (Unicode) encoded String object from the server response
222: * stream.
223: *
224: * @param len the length of the string to read <b>in characters</b>
225: * @return the result as a <code>String</code>
226: * @throws IOException if an I/O error occurs
227: */
228: String readUnicodeString(int len) throws IOException {
229: char[] chars = (len > charBuffer.length) ? new char[len]
230: : charBuffer;
231:
232: for (int i = 0; i < len; i++) {
233: if (bufferPtr >= bufferLen) {
234: getPacket();
235: }
236:
237: int b1 = buffer[bufferPtr++] & 0xFF;
238:
239: if (bufferPtr >= bufferLen) {
240: getPacket();
241: }
242:
243: int b2 = buffer[bufferPtr++] << 8;
244:
245: chars[i] = (char) (b2 | b1);
246: }
247:
248: return new String(chars, 0, len);
249: }
250:
251: /**
252: * Reads a non Unicode <code>String</code> from the server response stream,
253: * creating the <code>String</code> from a translated <code>byte</code>
254: * array.
255: *
256: * @param len the length of the string to read <b>in bytes</b>
257: * @return the result as a <code>String</code>
258: * @throws IOException if an I/O error occurs
259: */
260: String readNonUnicodeString(int len) throws IOException {
261: CharsetInfo info = socket.getCharsetInfo();
262:
263: return readString(len, info);
264: }
265:
266: /**
267: * Reads a <code>String</code> from the server response stream, translating
268: * it from a <code>byte</code> array using the specified character set.
269: *
270: * @param len the length of the string to read <b>in bytes</b>
271: * @return the result as a <code>String</code>
272: * @throws IOException if an I/O error occurs
273: */
274: String readNonUnicodeString(int len, CharsetInfo charsetInfo)
275: throws IOException {
276: return readString(len, charsetInfo);
277: }
278:
279: /**
280: * Reads a <code>String</code> from the server response stream, creating
281: * it from a translated <code>byte</code> array.
282: *
283: * @param len the length of the string to read <b>in bytes</b>
284: * @param info descriptor of the charset to use
285: * @return the result as a <code>String</code>
286: * @throws IOException if an I/O error occurs
287: */
288: String readString(int len, CharsetInfo info) throws IOException {
289: String charsetName = info.getCharset();
290: byte[] bytes = (len > byteBuffer.length) ? new byte[len]
291: : byteBuffer;
292:
293: read(bytes, 0, len);
294:
295: try {
296: return new String(bytes, 0, len, charsetName);
297: } catch (UnsupportedEncodingException e) {
298: return new String(bytes, 0, len);
299: }
300: }
301:
302: /**
303: * Reads a <code>short</code> value from the server response stream.
304: *
305: * @return the result as a <code>short</code>
306: * @throws IOException if an I/O error occurs
307: */
308: short readShort() throws IOException {
309: int b1 = read();
310:
311: return (short) (b1 | (read() << 8));
312: }
313:
314: /**
315: * Reads an <code>int</code> value from the server response stream.
316: *
317: * @return the result as a <code>int</code>
318: * @throws IOException if an I/O error occurs
319: */
320: int readInt() throws IOException {
321: int b1 = read();
322: int b2 = read() << 8;
323: int b3 = read() << 16;
324: int b4 = read() << 24;
325:
326: return b4 | b3 | b2 | b1;
327: }
328:
329: /**
330: * Reads a <code>long</code> value from the server response stream.
331: *
332: * @return the result as a <code>long</code>
333: * @throws IOException if an I/O error occurs
334: */
335: long readLong() throws IOException {
336: long b1 = ((long) read());
337: long b2 = ((long) read()) << 8;
338: long b3 = ((long) read()) << 16;
339: long b4 = ((long) read()) << 24;
340: long b5 = ((long) read()) << 32;
341: long b6 = ((long) read()) << 40;
342: long b7 = ((long) read()) << 48;
343: long b8 = ((long) read()) << 56;
344:
345: return b1 | b2 | b3 | b4 | b5 | b6 | b7 | b8;
346: }
347:
348: /**
349: * Reads an <code>unsigned long</code> value from the server response stream.
350: *
351: * @return the result as a <code>BigDecimal</code>
352: * @throws IOException if an I/O error occurs
353: */
354: BigDecimal readUnsignedLong() throws IOException {
355: int b1 = ((int) read() & 0xFF);
356: long b2 = ((long) read());
357: long b3 = ((long) read()) << 8;
358: long b4 = ((long) read()) << 16;
359: long b5 = ((long) read()) << 24;
360: long b6 = ((long) read()) << 32;
361: long b7 = ((long) read()) << 40;
362: long b8 = ((long) read()) << 48;
363: // Convert via String as BigDecimal(long) is actually BigDecimal(double)
364: // on older versions of java
365: return new BigDecimal(Long.toString(b2 | b3 | b4 | b5 | b6 | b7
366: | b8)).multiply(new BigDecimal(256)).add(
367: new BigDecimal(b1));
368: }
369:
370: /**
371: * Discards bytes from the server response stream.
372: *
373: * @param skip the number of bytes to discard
374: * @return the number of bytes skipped
375: */
376: int skip(int skip) throws IOException {
377: int tmp = skip;
378:
379: while (skip > 0) {
380: if (bufferPtr >= bufferLen) {
381: getPacket();
382: }
383:
384: int available = bufferLen - bufferPtr;
385:
386: if (skip > available) {
387: skip -= available;
388: bufferPtr = bufferLen;
389: } else {
390: bufferPtr += skip;
391: skip = 0;
392: }
393: }
394:
395: return tmp;
396: }
397:
398: /**
399: * Consumes the rest of the server response, without parsing it.
400: * <p/>
401: * <b>Note:</b> Use only in extreme cases, packets will not be parsed and
402: * could leave the connection in an inconsistent state.
403: */
404: void skipToEnd() {
405: try {
406: // No more data to read.
407: bufferPtr = bufferLen;
408: // Now consume all data until we get an exception.
409: while (true) {
410: buffer = socket.getNetPacket(streamId, buffer);
411: }
412: } catch (IOException ex) {
413: // Ignore it. Probably no more packets.
414: }
415: }
416:
417: /**
418: * Closes this response stream. The stream id is unlinked from the
419: * underlying shared socket as well.
420: */
421: void close() {
422: isClosed = true;
423: socket.closeStream(streamId);
424: }
425:
426: /**
427: * Retrieves the TDS version number.
428: *
429: * @return the TDS version as an <code>int</code>
430: */
431: int getTdsVersion() {
432: return socket.getTdsVersion();
433: }
434:
435: /**
436: * Retrieves the server type.
437: *
438: * @return the server type as an <code>int</code>
439: */
440: int getServerType() {
441: return socket.serverType;
442: }
443:
444: /**
445: * Creates a simple <code>InputStream</code> over the server response.
446: * <p/>
447: * This method can be used to obtain a stream which can be passed to
448: * <code>InputStreamReader</code>s to assist in reading multi byte
449: * character sets.
450: *
451: * @param len the number of bytes available in the server response
452: * @return the <code>InputStream</code> built over the server response
453: */
454: InputStream getInputStream(int len) {
455: return new TdsInputStream(this , len);
456: }
457:
458: /**
459: * Read the next TDS packet from the network.
460: *
461: * @throws IOException if an I/O error occurs
462: */
463: private void getPacket() throws IOException {
464: while (bufferPtr >= bufferLen) {
465: if (isClosed) {
466: throw new IOException("ResponseStream is closed");
467: }
468:
469: buffer = socket.getNetPacket(streamId, buffer);
470: bufferLen = (((int) buffer[2] & 0xFF) << 8)
471: | ((int) buffer[3] & 0xFF);
472: bufferPtr = TdsCore.PKT_HDR_LEN;
473:
474: if (Logger.isActive()) {
475: Logger.logPacket(streamId, true, buffer);
476: }
477: }
478: }
479:
480: /**
481: * Simple inner class implementing an <code>InputStream</code> over the
482: * server response.
483: */
484: private static class TdsInputStream extends InputStream {
485: /** The underlying <code>ResponseStream</code>. */
486: ResponseStream tds;
487: /** The maximum amount of data to make available. */
488: int maxLen;
489:
490: /**
491: * Creates a <code>TdsInputStream</code> instance.
492: *
493: * @param tds the underlying <code>ResponseStream</code>
494: * @param maxLen the maximum amount of data that will be available
495: */
496: public TdsInputStream(ResponseStream tds, int maxLen) {
497: this .tds = tds;
498: this .maxLen = maxLen;
499: }
500:
501: public int read() throws IOException {
502: return (maxLen-- > 0) ? tds.read() : -1;
503: }
504:
505: public int read(byte[] bytes, int offset, int len)
506: throws IOException {
507: if (maxLen < 1) {
508: return -1;
509: } else {
510: int bc = Math.min(maxLen, len);
511: if (bc > 0) {
512: bc = tds.read(bytes, offset, bc);
513: maxLen -= (bc == -1) ? 0 : bc;
514: }
515: return bc;
516: }
517: }
518: }
519: }
|