blob: 688cd895b49f48e38197b8b86a6f870160deb328 [file] [log] [blame]
/* Copyright (c) 2001-2010, The HSQL Development Group
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the HSQL Development Group nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.hsqldb.server;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import org.hsqldb.HsqlException;
import org.hsqldb.types.BinaryData;
/**
* An atomic transfer packet received from a HyperSQL client.
*
* Since we read and cache all data for the packet upon instantiation, the
* available method is reliable and may be relied upon.
*
* @see #available()
*/
class OdbcPacketInputStream extends DataInputStream {
char packetType;
private InputStream bufferStream;
/**
* Instantiate a packet of the specified type and size.
*/
static OdbcPacketInputStream newOdbcPacketInputStream(
char cType, InputStream streamSource, int sizeInt) throws IOException {
return newOdbcPacketInputStream(
cType, streamSource, new Integer(sizeInt));
}
/**
* Instantiate a packet of the specified type, with size determined by
* the first int read from the given stream.
*/
static OdbcPacketInputStream newOdbcPacketInputStream(
char cType, InputStream streamSource) throws IOException {
return newOdbcPacketInputStream(cType, streamSource, null);
}
static private OdbcPacketInputStream newOdbcPacketInputStream(
char cType, InputStream streamSource, Integer packetSizeObj)
throws IOException {
int bytesRead, i;
int packetSize = 0;
if (packetSizeObj == null) {
byte[] fourBytes = new byte[4];
bytesRead = 0;
while ((i = streamSource.read(fourBytes, bytesRead,
fourBytes.length - bytesRead)) > 0) {
bytesRead += i;
}
if (bytesRead != fourBytes.length) {
throw new EOFException("Failed to read size header int");
}
packetSize =
((fourBytes[0] & 0xff) << 24) + ((fourBytes[1] & 0xff) <<16)
+ ((fourBytes[2] & 0xff) << 8) + (fourBytes[3] & 0xff) - 4;
// Minus 4 because this counts the size int itself.
} else {
packetSize = packetSizeObj.intValue();
}
byte[] xferBuffer = new byte[packetSize];
bytesRead = 0;
while ((i = streamSource.read(xferBuffer, bytesRead,
xferBuffer.length - bytesRead)) > 0) {
bytesRead += i;
}
if (bytesRead != xferBuffer.length) {
throw new EOFException (
"Failed to read packet contents from given stream");
}
return new OdbcPacketInputStream(
cType, new ByteArrayInputStream(xferBuffer));
}
private OdbcPacketInputStream(char packetType, InputStream bufferStream) {
super(bufferStream);
this.packetType = packetType;
}
/**
* Generate a String/String Map from null-terminated String pairs, until
* a '\0' character is read in place of the first key character.
*
* @return the generated Map
* @throws EOFException if the rest of packet does not contained the
* required, well-formed null-terminated string pairs.
*/
Map readStringPairs() throws IOException {
String key;
Map map = new HashMap();
while (true) {
key = readString();
if (key.length() < 1) {
break;
}
map.put(key, readString());
}
return map;
}
/**
* Reads a NULL-TERMINATED String.
*
* @throws IOException if attempt to read past end of packet.
*/
String readString() throws IOException {
/* Would be MUCH easier to do this with Java6's String
* encoding/decoding operations */
ByteArrayOutputStream baos = new ByteArrayOutputStream();
baos.write((byte) 'X');
baos.write((byte) 'X');
// Place-holders to be replaced with short length
int i;
while ((i = readByte()) > 0) {
baos.write((byte) i);
}
byte[] ba = baos.toByteArray();
baos.close();
int len = ba.length - 2;
ba[0] = (byte) (len >>> 8);
ba[1] = (byte) len;
DataInputStream dis = new DataInputStream(new ByteArrayInputStream(ba));
String s = dis.readUTF();
//String s = DataInputStream.readUTF(dis);
// TODO: Test the previous two to see if one works better for
// high-order characters.
dis.close();
return s;
}
BinaryData readSizedBinaryData() throws IOException {
int len = readInt();
try {
return (len < 0) ? null : new BinaryData((long) len, this);
} catch (HsqlException he) {
throw new IOException(he.getMessage());
}
}
String readSizedString() throws IOException {
int len = readInt();
return (len < 0) ? null : readString(len);
}
/**
* These Strings are not null-terminated.
*
* @param len Bytes to read (not necessarily characters to be returned!
* @throws IOException if attempt to read past end of packet.
*/
String readString(int len) throws IOException {
/* Would be MUCH easier to do this with Java6's String
* encoding/decoding operations */
int bytesRead = 0;
int i;
byte[] ba = new byte[len + 2];
ba[0] = (byte) (len >>> 8);
ba[1] = (byte) len;
while ((i = read(ba, 2 + bytesRead, len - bytesRead)) > -1
&& bytesRead < len) {
bytesRead += i;
}
if (bytesRead != len) {
throw new EOFException("Packet ran dry");
}
for (i = 2; i < ba.length - 1; i++) {
if (ba[i] == 0) {
throw new RuntimeException(
"Null internal to String at offset " + (i - 2));
}
}
DataInputStream dis = new DataInputStream(new ByteArrayInputStream(ba));
String s = dis.readUTF();
//String s = DataInputStream.readUTF(dis);
// TODO: Test the previous two to see if one works better for
// high-order characters.
dis.close();
return s;
}
public char readByteChar() throws IOException {
return (char) readByte();
}
}