001: //
002: // Copyright 1998 CDS Networks, Inc., Medford Oregon
003: //
004: // All rights reserved.
005: //
006: // Redistribution and use in source and binary forms, with or without
007: // modification, are permitted provided that the following conditions are met:
008: // 1. Redistributions of source code must retain the above copyright
009: // notice, this list of conditions and the following disclaimer.
010: // 2. Redistributions in binary form must reproduce the above copyright
011: // notice, this list of conditions and the following disclaimer in the
012: // documentation and/or other materials provided with the distribution.
013: // 3. All advertising materials mentioning features or use of this software
014: // must display the following acknowledgement:
015: // This product includes software developed by CDS Networks, Inc.
016: // 4. The name of CDS Networks, Inc. may not be used to endorse or promote
017: // products derived from this software without specific prior
018: // written permission.
019: //
020: // THIS SOFTWARE IS PROVIDED BY CDS NETWORKS, INC. ``AS IS'' AND
021: // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
022: // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
023: // ARE DISCLAIMED. IN NO EVENT SHALL CDS NETWORKS, INC. BE LIABLE
024: // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
025: // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
026: // OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
027: // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
028: // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
029: // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
030: // SUCH DAMAGE.
031: //
032:
033: package com.internetcds.jdbc.tds;
034:
035: import java.io.*;
036: import java.net.*;
037: import com.internetcds.util.HexDump;
038: import com.internetcds.util.Logger;
039: import java.sql.Timestamp;
040:
041: /**
042: * Handle the communications for a Tds instance.
043: *
044: * @version $Id: TdsComm.java,v 1.2 2007-10-19 13:21:40 sinisa Exp $
045: * @author Craig Spannring
046: * @author Igor Petrovski
047: */
048: public class TdsComm implements TdsDefinitions {
049: public static final String cvsVersion = "$Id: TdsComm.java,v 1.2 2007-10-19 13:21:40 sinisa Exp $";
050: //Dusan
051: static int z = 0;
052:
053: static final int headerLength = 8;
054:
055: //
056: // The following constants are the packet types.
057: //
058: // They are the first databayte in the packet and
059: // define the type of data in that packet.
060: public static final byte QUERY = 1;
061: public static final byte LOGON = 2;
062: public static final byte PROC = 3;
063: public static final byte REPLY = 4;
064: public static final byte CANCEL = 6;
065: public static final byte LOGON70 = 16; // Added 2000-06-05
066:
067: // The minimum packet length that a TDS implementation can support
068: // is 512 bytes. This implementation will not support packets longer
069: // than 512. This will simplify the connection negotiation.
070: //
071: // XXX Some future release of this driver should be modified to
072: // negotiate longer packet sizes with the DB server.
073: private static final int maxPacketLength = 4096;
074:
075: // in and out are the sockets used for all communication with the
076: // server.
077: private DataOutputStream out = null;
078: private DataInputStream in = null;
079:
080: // outBuffer is used to construct the physical packets that will
081: // be sent to the database server.
082: byte outBuffer[];
083:
084: // nextOutBufferIndex is an index into outBuffer where the next
085: // byte of data will be stored while constructing a packet.
086: int nextOutBufferIndex = 0;
087:
088: // The type of the TDS packet that is being constructed
089: // in outBuffer.
090: int packetType = 0;
091:
092: // Place to store the incoming data from the DB server.
093: byte inBuffer[];
094:
095: // index of next byte that will be 'read' from inBuffer
096: int inBufferIndex = 0;
097:
098: // Total Number of bytes stored in inBuffer. (The number includes bytes
099: // that have been 'read' as well as bytes that still need to be 'read'.
100: int inBufferLen = 0;
101:
102: // Track how many packets we have sent and received
103: int packetsSent = 0;
104: int packetsReceived = 0;
105:
106: // For debuging purposes it would be nice to uniquely identify each Tds
107: // stream. id will be a unique value for each instance of this class.
108: static int id = 0;
109:
110: // Added 2000-06-07. Used to control TDS version-specific behavior.
111:
112: private int tdsVer = TDS42;
113:
114: public TdsComm(Socket sock, int tdsVer_) throws java.io.IOException {
115: out = new DataOutputStream(sock.getOutputStream());
116: in = new DataInputStream(sock.getInputStream());
117: outBuffer = new byte[4096];
118: inBuffer = new byte[4096];
119: // Added 2000-06-07
120: tdsVer = tdsVer_;
121:
122: id++;
123: }
124:
125: public void close() {
126: // nop for now.
127: }
128:
129: /**
130: * start a TDS packet.
131: *
132: * <br>
133: * This method should be called to start a logical TDS packet.
134: *
135: * @param type Type of the packet. Can be QUERY, LOGON, PROC,
136: * REPLY, or CANCEL.
137: */
138: public synchronized void startPacket(int type) {
139: // Only one thread at a time can be building an outboudn packet.
140: // This is primarily a concern with building cancel packets.
141: while (someThreadIsBuildingPacket()) {
142: try {
143: wait();
144: } catch (java.lang.InterruptedException e) {
145: // nop
146: }
147: }
148:
149: packetType = type;
150: nextOutBufferIndex = headerLength;
151: }
152:
153: /**
154: * Is some thread currently building a logical TDS packet?
155: *
156: * @return true iff a packet is being built.
157: */
158: public boolean someThreadIsBuildingPacket() {
159: return packetType != 0;
160: }
161:
162: /**
163: * append a byte onto the end of the logical TDS packet.
164: *
165: * <p>
166: * Append a byte onto the end of the logical TDS packet. When a
167: * physical packet is full send it to the server.
168: *
169: * @param b byte to add to the TDS packet
170: */
171: public void appendByte(byte b) throws java.io.IOException {
172: if (nextOutBufferIndex == maxPacketLength) {
173: // If we have a full physical packet then ship it out to the
174: // network.
175: sendPhysicalPacket(false);
176: nextOutBufferIndex = headerLength;
177: }
178:
179: storeByte(nextOutBufferIndex, b);
180: nextOutBufferIndex++;
181:
182: } // appendByte()
183:
184: /**
185: * append an array of bytes onto the end of the logical TDS packet.
186: *
187: * @param b bytes to add to the TDS packet
188: */
189: public void appendBytes(byte[] b) throws java.io.IOException {
190: appendBytes(b, b.length, (byte) 0);
191: } // appendBytes()
192:
193: /**
194: * append an array of bytes onto the end of the logical TDS packet.
195: *
196: * @param b bytes to add to the TDS packet
197: * @param len maximum number of bytes to transmit
198: * @param pad fill with this byte until len is reached
199: */
200: public void appendBytes(byte[] b, int len, byte pad)
201: throws java.io.IOException {
202: int i = 0;
203: for (; i < b.length && i < len; i++) {
204: appendByte(b[i]);
205: }
206: for (; i < len; i++) {
207: appendByte(pad);
208: }
209: }
210:
211: /**
212: * append a short int onto the end of the logical TDS packet.
213: * <p>
214: * @param s short int to add to the TDS packet
215: */
216: public void appendShort(short s) throws java.io.IOException {
217: appendByte((byte) ((s >> 8) & 0xff));
218: appendByte((byte) ((s >> 0) & 0xff));
219: }
220:
221: /**
222: * Appends a short int onto the end of the logical TDS packet.
223: * <p>
224: * @param s short int to add to the TDS packet
225: */
226: public void appendTdsShort(short s) throws java.io.IOException {
227: appendByte((byte) ((s >> 0) & 0xff));
228: appendByte((byte) ((s >> 8) & 0xff));
229: }
230:
231: /**
232: * append a Double onto the end of the logical TDS packet.
233: * <p>
234: * Append the Double value onto the end of the TDS packet as a
235: * SYBFLT8.
236: *
237: * @param value Double to add to the TDS packet
238: */
239: public void appendFlt8(Double value) throws java.io.IOException {
240: long l = Double.doubleToLongBits(value.doubleValue());
241:
242: appendByte((byte) ((l >> 0) & 0xff));
243: appendByte((byte) ((l >> 8) & 0xff));
244: appendByte((byte) ((l >> 16) & 0xff));
245: appendByte((byte) ((l >> 24) & 0xff));
246: appendByte((byte) ((l >> 32) & 0xff));
247: appendByte((byte) ((l >> 40) & 0xff));
248: appendByte((byte) ((l >> 48) & 0xff));
249: appendByte((byte) ((l >> 56) & 0xff));
250: }
251:
252: public void appendInt(int i) throws java.io.IOException {
253: appendByte((byte) ((i >> 24) & 0xff));
254: appendByte((byte) ((i >> 16) & 0xff));
255: appendByte((byte) ((i >> 8) & 0xff));
256: appendByte((byte) ((i >> 0) & 0xff));
257: }
258:
259: public void appendTdsInt(int i) throws java.io.IOException {
260: appendByte((byte) ((i >> 0) & 0xff));
261: appendByte((byte) ((i >> 8) & 0xff));
262: appendByte((byte) ((i >> 16) & 0xff));
263: appendByte((byte) ((i >> 24) & 0xff));
264: }
265:
266: public void appendInt64(long i) throws java.io.IOException {
267: appendByte((byte) ((i >> 56) & 0xff));
268: appendByte((byte) ((i >> 48) & 0xff));
269: appendByte((byte) ((i >> 40) & 0xff));
270: appendByte((byte) ((i >> 32) & 0xff));
271: appendByte((byte) ((i >> 24) & 0xff));
272: appendByte((byte) ((i >> 16) & 0xff));
273: appendByte((byte) ((i >> 8) & 0xff));
274: appendByte((byte) ((i >> 0) & 0xff));
275: }
276:
277: /**
278: * Appends the 16-bit characters from the caller's string, without
279: * narrowing the characters.
280: *
281: * Sybase let's the client decide what byte order to use but it \
282: * appears that SQLServer 7.0 little-endian byte order.
283: *
284: * Added 2000-06-05
285: */
286: public void appendChars(String s) throws java.io.IOException {
287:
288: for (int i = 0; i < s.length(); ++i) {
289: int c = s.charAt(i);
290: byte b1 = (byte) (c & 0xFF);
291: byte b2 = (byte) ((c >> 8) & 0xFF);
292: appendByte(b1);
293: appendByte(b2);
294: }
295: }
296:
297: /*
298: * Stefan Bodewig 2000-06-21
299: *
300: * removed appendString() to keep the encoding to and from the
301: * server charset in on place - i.e. Tds.
302: *
303: * It had to be Tds as we need to specify the length for the
304: * String as well, sometimes before we send the actual data,
305: * sometimes after we've sent them.
306: *
307: * If we need to know the length beforehand in Tds, we'd have to
308: * convert the data twice, once to get the length and once to send
309: * them.
310: */
311: // public void appendString(
312: // String s,
313: // int length,
314: // byte pad)
315: // throws java.io.IOException
316: // {
317: // int i;
318: // byte dst[];
319: //
320: //
321: // dst = encoder.getBytes(s.substring(0, (length<=s.length() ? length
322: // : s.length())));
323: //
324: // for(i=0; i<dst.length; i++)
325: // {
326: // appendByte(dst[i]);
327: // }
328: //
329: // for(; i<length; i++)
330: // {
331: // appendByte(pad);
332: // }
333: // }
334:
335: /**
336: * Send the logical packet.
337: * <p>
338: * Send the logical packet the has been constructed. */
339: public synchronized void sendPacket() throws java.io.IOException {
340: sendPhysicalPacket(true);
341: nextOutBufferIndex = 0;
342: packetType = 0;
343: notify();
344: }
345:
346: /**
347: * store a byte of data at a particular location in the outBuffer.
348: *
349: * @param index position in outBuffer to store data
350: * @param value value to store in the outBuffer.
351: */
352: private void storeByte(int index, byte value) {
353: outBuffer[index] = value;
354:
355: }
356:
357: /**
358: * store a short integer of data at a particular location in the outBuffer.
359: *
360: * @param index position in outBuffer to store data
361: * @param value value to store in the outBuffer.
362: */
363: private void storeShort(int index, short s) {
364: outBuffer[index] = (byte) ((s >> 8) & 0xff);
365: outBuffer[index + 1] = (byte) ((s >> 0) & 0xff);
366: }
367:
368: /**
369: * send the data in the outBuffer.
370: * <p>
371: * Fill in the TDS packet header data and send the data in outBuffer
372: * to the DB server.
373: *
374: * @param isLastSegment is this the last physical packet that makes
375: * up the physical packet?
376: */
377: private void sendPhysicalPacket(boolean isLastSegment)
378: throws java.io.IOException {
379: if (nextOutBufferIndex > headerLength || packetType == CANCEL) {
380: // packet type
381: storeByte(0, (byte) (packetType & 0xff));
382: storeByte(1, isLastSegment ? (byte) 1 : (byte) 0);
383: storeShort(2, (short) nextOutBufferIndex);
384: storeByte(4, (byte) 0);
385: storeByte(5, (byte) 0);
386: storeByte(6, (byte) (tdsVer == TDS70 ? 1 : 0));
387: storeByte(7, (byte) 0);
388: //Dusan
389: z++;
390: /* if (z==7) {
391:
392: storeByte(18, "1".getBytes()[0]);
393: storeByte(20, (byte)0);
394: storeByte(21, "#".getBytes()[0]);
395: storeByte(22, "1".getBytes()[0]);
396: storeByte(23, "(".getBytes()[0]);
397: storeByte(24, "@".getBytes()[0]);
398: storeByte(25, "P".getBytes()[0]);
399: storeByte(26, "1".getBytes()[0]);
400: storeByte(31, (byte)0);
401:
402: }
403: */
404: out.write(outBuffer, 0, nextOutBufferIndex);
405: packetsSent++;
406: for (int j = 0; j < outBuffer.length; j++)
407: storeByte(j, (byte) 0);
408:
409: if (Logger.isActive()) {
410: String dump = HexDump.hexDump(outBuffer,
411: nextOutBufferIndex);
412: String t = (new Timestamp(System.currentTimeMillis()))
413: .toString();
414: Logger.println("Instance " + id + " @ " + t
415: + " sent packet #" + packetsSent + "\n" + dump);
416: }
417: }
418: }
419:
420: /**
421: * peek at the next byte of data.
422: * <p>
423: * This returns the next byte of data that would be returned
424: * by getByte(), but does not actually consume the data.
425: *
426: * <b>Note-</b> We can't synchronize this method (or most of the other
427: * methods in this class) because of the way cancels are handled.
428: * If a thread is waiting for a response from the server the
429: * cancelController class must be able to call sendPacket() to
430: * cancel the request.
431: *
432: * @return The next byte of data that will be returned by getByte()
433: *
434: * @exception com.internetcds.jdbc.tds.TdsException
435: * @exception java.io.IOException
436: */
437: public byte peek() throws com.internetcds.jdbc.tds.TdsException,
438: java.io.IOException {
439:
440: // XXX RACE CONDITION- It is possible that two threads
441: // could be modifying inBuffer at the same time. We need
442: // to synchronized based on inBuffer itself.
443: byte result = getByte();
444: backup();
445: return result;
446: }
447:
448: /**
449: * put the most recently read byte of data back in the inBuffer.
450: * <p>
451: * This function effectivly ungets the last byte read.
452: * It is guaranteed to be able to unget the last byte read.
453: * Trying to unget multiple bytes is not recomended and will
454: * only work so long as all the bytes were in the same
455: * physical TDS network packet.
456: *
457: * @author Craig Spannring
458: */
459: public void backup() {
460: inBufferIndex--;
461:
462: // make sure we have fallen of the beginning of the buffer
463: // throw an array out of bounds error if we've gone back too far.
464: byte b = inBuffer[inBufferIndex];
465: }
466:
467: /**
468: * read a byte of data from the DB server.
469: * <p>
470: * This will return the next byte of data from the DB server.
471: * <p>
472: * <B>Warning</B> If there is not data available this method
473: * will block.
474: */
475: public byte getByte() throws com.internetcds.jdbc.tds.TdsException,
476: java.io.IOException {
477: byte result;
478:
479: if (inBufferIndex >= inBufferLen) {
480: // out of data, read another physical packet.
481: getPhysicalPacket();
482: }
483:
484: result = inBuffer[inBufferIndex++];
485: return result;
486: }
487:
488: public byte[] getBytes(int len)
489: throws com.internetcds.jdbc.tds.TdsException,
490: java.io.IOException {
491: byte result[] = new byte[len];
492: int i;
493:
494: for (i = 0; i < len; i++) {
495: result[i] = getByte();
496: }
497:
498: return result;
499: }
500:
501: /**
502: * Reads bytes or characters (depending on TDS version) and constructs a
503: * string with them.
504: *
505: * Sybase will let the client choose byte ordering, but SQLServer 7.0
506: * wants little endian only. In the interest of simplicity, just use
507: * little endian regardless of the type of server.
508: *
509: * Added 2000-06-05.
510: */
511: public String getString(int len)
512: throws com.internetcds.jdbc.tds.TdsException,
513: java.io.IOException {
514: if (tdsVer == TDS70) {
515: char[] chars = new char[len];
516: for (int i = 0; i < len; ++i) {
517: int lo = getByte() & 0xFF;
518: int hi = getByte() & 0xFF;
519: chars[i] = (char) (lo | (hi << 8));
520: }
521: return new String(chars);
522: } else
523: return new String(getBytes(len));
524: }
525:
526: public void skip(int i)
527: throws com.internetcds.jdbc.tds.TdsException,
528: java.io.IOException {
529: for (; i > 0; i--) {
530: getByte();
531: }
532: } // skip()
533:
534: public int getNetShort() throws TdsException, java.io.IOException {
535: byte tmp[] = new byte[2];
536: tmp[0] = getByte();
537: tmp[1] = getByte();
538: return ntohs(tmp, 0);
539: }
540:
541: public int getTdsShort()
542: throws com.internetcds.jdbc.tds.TdsException,
543: java.io.IOException {
544: int lo = ((int) getByte() & 0xff);
545: int hi = ((int) getByte() & 0xff) << 8;
546: return lo | hi;
547: }
548:
549: public int getTdsInt()
550: throws com.internetcds.jdbc.tds.TdsException,
551: java.io.IOException {
552: int result;
553:
554: int b1 = ((int) getByte() & 0xff);
555: int b2 = ((int) getByte() & 0xff) << 8;
556: int b3 = ((int) getByte() & 0xff) << 16;
557: int b4 = ((int) getByte() & 0xff) << 24;
558:
559: result = b4 | b3 | b2 | b1;
560:
561: return result;
562: }
563:
564: public long getTdsInt64()
565: throws com.internetcds.jdbc.tds.TdsException,
566: java.io.IOException {
567: long b1 = ((long) getByte() & 0xff);
568: long b2 = ((long) getByte() & 0xff) << 8;
569: long b3 = ((long) getByte() & 0xff) << 16;
570: long b4 = ((long) getByte() & 0xff) << 24;
571: long b5 = ((long) getByte() & 0xff) << 32;
572: long b6 = ((long) getByte() & 0xff) << 40;
573: long b7 = ((long) getByte() & 0xff) << 48;
574: long b8 = ((long) getByte() & 0xff) << 56;
575: return b1 | b2 | b3 | b4 | b5 | b6 | b7 | b8;
576: }
577:
578: /**
579: * convert two bytes in a byte array into a Java short integer.
580: *
581: * @param buf array of data
582: * @param offset index into the buf array where the short integer
583: * is stored.
584: */
585: private static int ntohs(byte buf[], int offset) {
586: int lo = ((int) buf[offset + 1] & 0xff);
587: int hi = (((int) buf[offset] & 0xff) << 8);
588:
589: return hi | lo; // return an int since we really want an _unsigned_
590: }
591:
592: /**
593: * Read a physical packet.
594: * <p>
595: * <B>Warning</B> This method will block until it gets all of the input.
596: */
597: private void getPhysicalPacket() throws TdsException,
598: java.io.IOException {
599: byte tmpBuf[] = new byte[8];
600:
601: // read the header
602: for (int nread = 0; nread < 8;) {
603: nread += in.read(tmpBuf, nread, 8 - nread);
604: }
605: if (Logger.isActive()) {
606: String dump = com.internetcds.util.HexDump.hexDump(tmpBuf,
607: 8);
608: String t = (new Timestamp(System.currentTimeMillis()))
609: .toString();
610:
611: Logger.println("Instance " + id + " @ " + t
612: + " recevied header #" + (packetsReceived + 1)
613: + "\n" + dump);
614: }
615: byte packetType = tmpBuf[0];
616: if (packetType != LOGON && packetType != QUERY
617: && packetType != REPLY) {
618: throw new TdsUnknownPacketType(packetType, tmpBuf);
619: }
620: // figure out how many bytes are remaining in this packet.
621: int len = ntohs(tmpBuf, 2) - 8;
622: // Added 2000-06-05
623: if (len >= inBuffer.length) {
624: inBuffer = new byte[len];
625: }
626: if (len < 0) {
627: throw new TdsException("Confused by a length of " + len);
628: }
629:
630: // now get the data
631: for (int nread = 0; nread < len;) {
632: //Dusan1
633:
634: nread += in.read(inBuffer, nread, len - nread);
635: }
636: packetsReceived++;
637:
638: // adjust the bookkeeping info about the incoming buffer
639: inBufferLen = len;
640: inBufferIndex = 0;
641:
642: if (Logger.isActive()) {
643: String dump = com.internetcds.util.HexDump.hexDump(
644: inBuffer, len);
645: String t = (new Timestamp(System.currentTimeMillis()))
646: .toString();
647:
648: Logger.println("Instance " + id + " @ " + t
649: + " recevied data #" + (packetsReceived) + "\n"
650: + dump);
651: }
652: }
653: }
|