From: Mark Slee Date: Wed, 7 Jun 2006 06:51:18 +0000 (+0000) Subject: Java libraries for Thrift X-Git-Tag: 0.2.0~1757 X-Git-Url: https://source.supwisdom.com/gerrit/gitweb?a=commitdiff_plain;h=83c52a8d7ab0be9215a70351b2a4d27938092d72;p=common%2Fthrift.git Java libraries for Thrift Summary: The basic Thrift stack implemented in Java, still in need of a lot of work but fully functional. Reviewed By: aditya Test Plan: Unit tests are the NEXT checkin, I swear Notes: Perf on the Java stuff actually isn't that bad, and it's far from optimized at the moment. Barely any tweaking has been done. Testing shows that a Java server with the C++ client has RPC performance within 2x of the pure C++ implementations. This is pretty sweet, since this cost will be eclipsed by the cost of whatever processing is being done on an actual server doing real work. git-svn-id: https://svn.apache.org/repos/asf/incubator/thrift/trunk@664715 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/lib/java/build.xml b/lib/java/build.xml new file mode 100644 index 00000000..d2a4df6f --- /dev/null +++ b/lib/java/build.xml @@ -0,0 +1,32 @@ + + + Thrift Build File + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/java/src/TException.java b/lib/java/src/TException.java new file mode 100644 index 00000000..2dbcf391 --- /dev/null +++ b/lib/java/src/TException.java @@ -0,0 +1,24 @@ +package com.facebook.thrift; + +/** + * Generic exception class for Thrift. + * + * @author Mark Slee + */ +public class TException extends Exception { + public TException() { + super(); + } + + public TException(String message) { + super(message); + } + + public TException(Throwable cause) { + super(cause); + } + + public TException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/lib/java/src/TProcessor.java b/lib/java/src/TProcessor.java new file mode 100644 index 00000000..dd6ae858 --- /dev/null +++ b/lib/java/src/TProcessor.java @@ -0,0 +1,14 @@ +package com.facebook.thrift; + +import com.facebook.thrift.transport.TTransport; + +/** + * A processor is a generic object which operates upon an input stream and + * writes to some output stream. + * + * @author Mark Slee + */ +public interface TProcessor { + public boolean process(TTransport in, TTransport out) + throws TException; +} diff --git a/lib/java/src/protocol/TBinaryProtocol.java b/lib/java/src/protocol/TBinaryProtocol.java new file mode 100644 index 00000000..3aaa06bd --- /dev/null +++ b/lib/java/src/protocol/TBinaryProtocol.java @@ -0,0 +1,236 @@ +package com.facebook.thrift.protocol; + +import com.facebook.thrift.TException; +import com.facebook.thrift.transport.TTransport; +import com.facebook.thrift.types.*; + +/** + * Binary protocol implementation for thrift. + * + * @author Mark Slee + */ +public class TBinaryProtocol implements TProtocol { + public int writeStructBegin (TTransport out, + TStruct struct) throws TException { + return 0; + } + + public int writeStructEnd (TTransport out) throws TException { + return 0; + } + + public int writeFieldBegin (TTransport out, + TField field) throws TException { + return + writeByte(out, field.type.getCode()) + + writeU32(out, field.id); + } + + public int writeFieldEnd (TTransport out) throws TException { + return 0; + } + + public int writeFieldStop (TTransport out) throws TException { + return + writeByte(out, TType.STOP.getCode()); + } + + public int writeMapBegin (TTransport out, + TMap map) throws TException { + return + writeByte(out, map.keyType.getCode()) + + writeByte(out, map.valueType.getCode()) + + writeU32(out, map.size); + } + + public int writeMapEnd (TTransport out) throws TException { + return 0; + } + + public int writeListBegin (TTransport out, + TList list) throws TException { + return + writeByte(out, list.elemType.getCode()) + + writeU32(out, list.size); + } + + public int writeListEnd (TTransport out) throws TException { + return 0; + } + + public int writeSetBegin (TTransport out, + TSet set) throws TException { + return + writeByte(out, set.elemType.getCode()) + + writeU32(out, set.size); + } + + public int writeSetEnd (TTransport out) throws TException { + return 0; + } + + public int writeByte (TTransport out, + UInt8 b) throws TException { + out.write(b.data(), 0, 1); + return 1; + } + + public int writeU32 (TTransport out, + UInt32 u32) throws TException { + out.write(u32.data(), 0, 4); + return 4; + } + + public int writeI32 (TTransport out, + Int32 i32) throws TException { + out.write(i32.data(), 0, 4); + return 4; + } + + public int writeU64 (TTransport out, + UInt64 u64) throws TException { + out.write(u64.data(), 0, 8); + return 8; + } + + public int writeI64 (TTransport out, + Int64 i64) throws TException { + out.write(i64.data(), 0, 8); + return 8; + } + + public int writeString (TTransport out, + TString str) throws TException { + byte[] dat = str.value.getBytes(); + int sent = writeU32(out, new UInt32(dat.length)); + out.write(dat, 0, dat.length); + return sent + dat.length; + } + + /** + * Reading methods. + */ + + public int readStructBegin (TTransport in, + TStruct struct) throws TException { + struct.name = ""; + return 0; + } + + public int readStructEnd (TTransport in) throws TException { + return 0; + } + + public int readFieldBegin (TTransport in, + TField field) throws TException { + int recv = 0; + UInt8 t = new UInt8(); + + recv += readByte(in, t); + field.type = TType.getType(t); + if (field.type.equals(TType.STOP)) { + field.id = new UInt32(0); + return recv; + } + recv += readU32(in, field.id); + return recv; + } + + public int readFieldEnd (TTransport in) throws TException { + return 0; + } + + public int readMapBegin (TTransport in, + TMap map) throws TException { + int recv = 0; + UInt8 t = new UInt8(); + recv += readByte(in, t); + map.keyType = TType.getType(t); + recv += readByte(in, t); + map.valueType = TType.getType(t); + recv += readU32(in, map.size); + return recv; + } + + public int readMapEnd (TTransport in) throws TException { + return 0; + } + + public int readListBegin (TTransport in, + TList list) throws TException { + int recv = 0; + UInt8 t = new UInt8(); + recv += readByte(in, t); + list.elemType = TType.getType(t); + recv += readU32(in, list.size); + return recv; + } + + public int readListEnd (TTransport in) throws TException { + return 0; + } + + public int readSetBegin (TTransport in, + TSet set) throws TException { + int recv = 0; + UInt8 t = new UInt8(); + recv += readByte(in, t); + set.elemType = TType.getType(t); + recv += readU32(in, set.size); + return recv; + } + + public int readSetEnd (TTransport in) throws TException { + return 0; + } + + public int readByte (TTransport in, + UInt8 b) throws TException { + byte[] buf = new byte[1]; + in.readAll(buf, 0, 1); + b.read(buf, 0); + return 1; + } + + public int readU32 (TTransport in, + UInt32 u32) throws TException { + byte[] buf = new byte[4]; + in.readAll(buf, 0, 4); + u32.read(buf, 0); + return 4; + } + + public int readI32 (TTransport in, + Int32 i32) throws TException { + byte[] buf = new byte[4]; + in.readAll(buf, 0, 4); + i32.read(buf, 0); + return 4; + } + + public int readU64 (TTransport in, + UInt64 u64) throws TException { + byte[] buf = new byte[8]; + in.readAll(buf, 0, 8); + u64.read(buf, 0); + return 8; + } + + public int readI64 (TTransport in, + Int64 i64) throws TException { + byte[] buf = new byte[8]; + in.readAll(buf, 0, 8); + i64.read(buf, 0); + return 8; + } + + public int readString (TTransport in, + TString s) throws TException { + UInt32 size = new UInt32(); + int recv = readU32(in, size); + byte[] buf = new byte[size.toInt()]; + in.readAll(buf, 0, size.toInt()); + s.value = new String(buf); + return recv + size.toInt(); + } +} diff --git a/lib/java/src/protocol/TField.java b/lib/java/src/protocol/TField.java new file mode 100644 index 00000000..83f1fc35 --- /dev/null +++ b/lib/java/src/protocol/TField.java @@ -0,0 +1,26 @@ +package com.facebook.thrift.protocol; + +import com.facebook.thrift.types.*; + +/** + * Helper class that encapsulates field metadata. + * + * @author Mark Slee + */ +public class TField { + public TField() {} + + public TField(String n, TType t, int i) { + this(n, t, new UInt32(i)); + } + + public TField(String n, TType t, UInt32 i) { + name = n; + type = t; + id = i; + } + + public String name = ""; + public TType type = TType.STOP; + public UInt32 id = new UInt32(); +} diff --git a/lib/java/src/protocol/TList.java b/lib/java/src/protocol/TList.java new file mode 100644 index 00000000..6eac06b1 --- /dev/null +++ b/lib/java/src/protocol/TList.java @@ -0,0 +1,24 @@ +package com.facebook.thrift.protocol; + +import com.facebook.thrift.types.*; + +/** + * Helper class that encapsulates list metadata. + * + * @author Mark Slee + */ +public class TList { + public TList() {} + + public TList(TType t, int s) { + this(t, new UInt32(s)); + } + + public TList(TType t, UInt32 s) { + elemType = t; + size = s; + } + + public TType elemType = TType.STOP; + public UInt32 size = new UInt32(); +} diff --git a/lib/java/src/protocol/TMap.java b/lib/java/src/protocol/TMap.java new file mode 100644 index 00000000..84eb4689 --- /dev/null +++ b/lib/java/src/protocol/TMap.java @@ -0,0 +1,26 @@ +package com.facebook.thrift.protocol; + +import com.facebook.thrift.types.*; + +/** + * Helper class that encapsulates map metadata. + * + * @author Mark Slee + */ +public class TMap { + public TMap() {} + + public TMap(TType k, TType v, int s) { + this(k, v, new UInt32(s)); + } + + public TMap(TType k, TType v, UInt32 s) { + keyType = k; + valueType = v; + size = s; + } + + public TType keyType = TType.STOP; + public TType valueType = TType.STOP; + public UInt32 size = new UInt32();; +} diff --git a/lib/java/src/protocol/TProtocol.java b/lib/java/src/protocol/TProtocol.java new file mode 100644 index 00000000..f44eb9ee --- /dev/null +++ b/lib/java/src/protocol/TProtocol.java @@ -0,0 +1,109 @@ +package com.facebook.thrift.protocol; + +import com.facebook.thrift.types.*; +import com.facebook.thrift.TException; +import com.facebook.thrift.transport.TTransport; + +/** + * Protocol interface definition. + * + * @author Mark Slee + */ +public interface TProtocol { + + /** + * Writing methods. + */ + + public int writeStructBegin (TTransport out, + TStruct struct) throws TException; + public int writeStructEnd (TTransport out) throws TException; + + public int writeFieldBegin (TTransport out, + TField field) throws TException; + + public int writeFieldEnd (TTransport out) throws TException; + + public int writeFieldStop (TTransport out) throws TException; + + public int writeMapBegin (TTransport out, + TMap map) throws TException; + + public int writeMapEnd (TTransport out) throws TException; + + public int writeListBegin (TTransport out, + TList list) throws TException; + + public int writeListEnd (TTransport out) throws TException; + + public int writeSetBegin (TTransport out, + TSet set) throws TException; + + public int writeSetEnd (TTransport out) throws TException; + + public int writeByte (TTransport out, + UInt8 b) throws TException; + + public int writeU32 (TTransport out, + UInt32 u32) throws TException; + + public int writeI32 (TTransport out, + Int32 i32) throws TException; + + public int writeU64 (TTransport out, + UInt64 u64) throws TException; + + public int writeI64 (TTransport out, + Int64 i64) throws TException; + + public int writeString (TTransport out, + TString str) throws TException; + + /** + * Reading methods. + */ + + public int readStructBegin (TTransport in, + TStruct struct) throws TException; + + public int readStructEnd (TTransport in) throws TException; + + public int readFieldBegin (TTransport in, + TField field) throws TException; + + public int readFieldEnd (TTransport in) throws TException; + + public int readMapBegin (TTransport in, + TMap map) throws TException; + + public int readMapEnd (TTransport in) throws TException; + + public int readListBegin (TTransport in, + TList list) throws TException; + + public int readListEnd (TTransport in) throws TException; + + public int readSetBegin (TTransport in, + TSet set) throws TException; + + public int readSetEnd (TTransport in) throws TException; + + public int readByte (TTransport in, + UInt8 b) throws TException; + + public int readU32 (TTransport in, + UInt32 u32) throws TException; + + public int readI32 (TTransport in, + Int32 i32) throws TException; + + public int readU64 (TTransport in, + UInt64 u64) throws TException; + + public int readI64 (TTransport in, + Int64 i64) throws TException; + + public int readString (TTransport in, + TString s) throws TException; + +} diff --git a/lib/java/src/protocol/TProtocolUtil.java b/lib/java/src/protocol/TProtocolUtil.java new file mode 100644 index 00000000..b182139b --- /dev/null +++ b/lib/java/src/protocol/TProtocolUtil.java @@ -0,0 +1,104 @@ +package com.facebook.thrift.protocol; + +import com.facebook.thrift.types.*; +import com.facebook.thrift.TException; +import com.facebook.thrift.transport.TTransport; + +/** + * Utility class with static methods for interacting with protocol data + * streams. + * + * @author Mark Slee + */ +public class TProtocolUtil { + public static int skip(TProtocol prot, TTransport in, TType type) + throws TException { + + switch (type) { + case BYTE: + { + UInt8 b = new UInt8(); + return prot.readByte(in, b); + } + case U32: + { + UInt32 u32 = new UInt32(); + return prot.readU32(in, u32); + } + case I32: + { + Int32 i32 = new Int32(); + return prot.readI32(in, i32); + } + case U64: + { + UInt64 u64 = new UInt64(); + return prot.readU64(in, u64); + } + case I64: + { + Int64 i64 = new Int64(); + return prot.readI64(in, i64); + } + case STRING: + { + TString s = new TString(); + return prot.readString(in, s); + } + case STRUCT: + { + int result = 0; + TString name = new TString(); + TStruct struct = new TStruct(); + TField field = new TField(); + result += prot.readStructBegin(in, struct); + while (true) { + result += prot.readFieldBegin(in, field); + if (field.type.equals(TType.STOP)) { + break; + } + result += skip(prot, in, field.type); + result += prot.readFieldEnd(in); + } + result += prot.readStructEnd(in); + return result; + } + case MAP: + { + int result = 0; + TMap map = new TMap(); + result += prot.readMapBegin(in, map); + for (int i = 0; i < map.size.get(); i++) { + result += skip(prot, in, map.keyType); + result += skip(prot, in, map.valueType); + } + result += prot.readMapEnd(in); + return result; + } + case SET: + { + int result = 0; + TSet set = new TSet(); + result += prot.readSetBegin(in, set); + for (int i = 0; i < set.size.get(); i++) { + result += skip(prot, in, set.elemType); + } + result += prot.readSetEnd(in); + return result; + } + case LIST: + { + int result = 0; + TList list = new TList(); + result += prot.readListBegin(in, list); + for (int i = 0; i < list.size.get(); i++) { + result += skip(prot, in, list.elemType); + } + result += prot.readListEnd(in); + return result; + } + default: + return 0; + } + } +} diff --git a/lib/java/src/protocol/TSet.java b/lib/java/src/protocol/TSet.java new file mode 100644 index 00000000..e0dcf769 --- /dev/null +++ b/lib/java/src/protocol/TSet.java @@ -0,0 +1,24 @@ +package com.facebook.thrift.protocol; + +import com.facebook.thrift.types.*; + +/** + * Helper class that encapsulates set metadata. + * + * @author Mark Slee + */ +public class TSet { + public TSet() {} + + public TSet(TType t, int s) { + this(t, new UInt32(s)); + } + + public TSet(TType t, UInt32 s) { + elemType = t; + size = s; + } + + public TType elemType = TType.STOP; + public UInt32 size = new UInt32(); +} diff --git a/lib/java/src/protocol/TString.java b/lib/java/src/protocol/TString.java new file mode 100644 index 00000000..04fcc1dd --- /dev/null +++ b/lib/java/src/protocol/TString.java @@ -0,0 +1,17 @@ +package com.facebook.thrift.protocol; + +/** + * Wrapper around String so that you can pass this object to a function and + * have it set the internal string value. + * + * @author Mark Slee + */ +public class TString { + public TString() {} + + public TString(String v) { + value = v; + } + + public String value = ""; +} diff --git a/lib/java/src/protocol/TStruct.java b/lib/java/src/protocol/TStruct.java new file mode 100644 index 00000000..2fbcb8f5 --- /dev/null +++ b/lib/java/src/protocol/TStruct.java @@ -0,0 +1,18 @@ +package com.facebook.thrift.protocol; + +import com.facebook.thrift.types.*; + +/** + * Helper class that encapsulates struct metadata. + * + * @author Mark Slee + */ +public class TStruct { + public TStruct() {} + + public TStruct(String n) { + name = n; + } + + public String name = ""; +} diff --git a/lib/java/src/protocol/TType.java b/lib/java/src/protocol/TType.java new file mode 100644 index 00000000..21b7914a --- /dev/null +++ b/lib/java/src/protocol/TType.java @@ -0,0 +1,82 @@ +package com.facebook.thrift.protocol; + +import com.facebook.thrift.types.UInt8; + +/** + * Type constants in the Thrift protocol. + * + * @author Mark Slee + */ +public enum TType { + STOP (1), + BYTE (2), + U16 (3), + I16 (4), + U32 (5), + I32 (6), + U64 (7), + I64 (8), + STRING (9), + STRUCT (10), + MAP (11), + SET (12), + LIST (13); + + /** U8 identifier */ + private UInt8 code_; + + /** + * Constructor to create a TType object from its code. + * + * @param code The identifier code for this type + */ + private TType(int code) { + code_ = new UInt8((byte)code); + } + + /** + * Accessor for the code. + */ + public UInt8 getCode() { + return code_; + } + + /** + * Static method to get a type object from a byte. + * + * @param code The type code + */ + public static TType getType(UInt8 code) { + switch (code.get()) { + case 1: + return STOP; + case 2: + return BYTE; + case 3: + return U16; + case 4: + return I16; + case 5: + return U32; + case 6: + return I32; + case 7: + return U64; + case 8: + return I64; + case 9: + return STRING; + case 10: + return STRUCT; + case 11: + return MAP; + case 12: + return SET; + case 13: + return LIST; + default: + System.err.println("WARNING: Unidentified type code: " + code.get()); + return STOP; + } + } +} diff --git a/lib/java/src/server/TServer.java b/lib/java/src/server/TServer.java new file mode 100644 index 00000000..38ef81fa --- /dev/null +++ b/lib/java/src/server/TServer.java @@ -0,0 +1,38 @@ +package com.facebook.thrift.server; + +import com.facebook.thrift.TProcessor; + +/** + * Generic interface for a Thrift server. + * + * @author Mark Slee + */ +public abstract class TServer { + + /** + * The options class should be subclassed by particular servers which have + * specific options needs, while the general options should live here. + */ + public static class Options { + public Options() {} + } + + /** Core processor */ + protected TProcessor processor_; + + /** Server options */ + protected Options options_; + + /** + * Default constructor, all servers take a processor and some options. + */ + protected TServer(TProcessor processor, Options options) { + processor_ = processor; + options_ = options; + } + + /** + * The run method fires up the server and gets things going. + */ + public abstract void run(); +} diff --git a/lib/java/src/server/TSimpleServer.java b/lib/java/src/server/TSimpleServer.java new file mode 100644 index 00000000..352a6de9 --- /dev/null +++ b/lib/java/src/server/TSimpleServer.java @@ -0,0 +1,50 @@ +package com.facebook.thrift.server; + +import com.facebook.thrift.TException; +import com.facebook.thrift.TProcessor; +import com.facebook.thrift.transport.TServerTransport; +import com.facebook.thrift.transport.TTransport; +import com.facebook.thrift.transport.TTransportException; + +/** + * Simple singlethreaded server for testing. + * + * @author Mark Slee + */ +public class TSimpleServer extends TServer { + + private TServerTransport serverTransport_; + + public TSimpleServer(TProcessor processor, + TServer.Options options, + TServerTransport serverTransport) { + super(processor, options); + serverTransport_ = serverTransport; + } + + public void run() { + try { + serverTransport_.listen(); + } catch (TTransportException ttx) { + ttx.printStackTrace(); + return; + } + + while (true) { + TTransport client = null; + try { + client = serverTransport_.accept(); + if (client != null) { + while (processor_.process(client, client)); + } + } catch (TException tx) { + tx.printStackTrace(); + } + + if (client != null) { + client.close(); + client = null; + } + } + } +} diff --git a/lib/java/src/transport/TIOStreamTransport.java b/lib/java/src/transport/TIOStreamTransport.java new file mode 100644 index 00000000..aa4adf1d --- /dev/null +++ b/lib/java/src/transport/TIOStreamTransport.java @@ -0,0 +1,138 @@ +package com.facebook.thrift.transport; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * This is the most commonly used base transport. It takes an InputStream + * and an OutputStream and uses those to perform all transport operations. + * This allows for compatibility with all the nice constructs Java already + * has to provide a variety of types of streams. + * + * @author Mark Slee + */ +public class TIOStreamTransport extends TTransport { + + /** Underlying inputStream */ + protected InputStream inputStream_ = null; + + /** Underlying outputStream */ + protected OutputStream outputStream_ = null; + + /** + * Subclasses can invoke the default constructor and then assign the input + * streams in the open method. + */ + protected TIOStreamTransport() {} + + /** + * Input stream constructor. + * + * @param is Input stream to read from + */ + public TIOStreamTransport(InputStream is) { + inputStream_ = is; + } + + /** + * Output stream constructor. + * + * @param os Output stream to read from + */ + public TIOStreamTransport(OutputStream os) { + outputStream_ = os; + } + + /** + * Two-way stream constructor. + * + * @param is Input stream to read from + * @param os Output stream to read from + */ + public TIOStreamTransport(InputStream is, OutputStream os) { + inputStream_ = is; + outputStream_ = os; + } + + /** + * The streams must already be open at construction time, so this should + * always return true. + * + * @return true + */ + public boolean isOpen() { + return true; + } + + /** + * The streams must already be open. This method does nothing. + */ + public void open() throws TTransportException {} + + /** + * Closes both the input and output streams. + */ + public void close() { + if (inputStream_ != null) { + try { + inputStream_.close(); + } catch (IOException iox) { + System.err.println("WARNING: Error closing input stream: " + + iox.getMessage()); + } + inputStream_ = null; + } + if (outputStream_ != null) { + try { + outputStream_.close(); + } catch (IOException iox) { + System.err.println("WARNING: Error closing output stream: " + + iox.getMessage()); + } + outputStream_ = null; + } + } + + /** + * Reads from the underlying input stream if not null. + */ + public int read(byte[] buf, int off, int len) throws TTransportException { + if (inputStream_ == null) { + throw new TTransportException("Cannot read from null inputStream"); + } + try { + return inputStream_.read(buf, off, len); + } catch (IOException iox) { + throw new TTransportException(iox); + } + } + + /** + * Writes to the underlying output stream if not null. + */ + public void write(byte[] buf, int off, int len) throws TTransportException { + if (outputStream_ == null) { + throw new TTransportException("Cannot write to null outputStream"); + } + try { + outputStream_.write(buf, off, len); + } catch (IOException iox) { + throw new TTransportException(iox); + } + } + + /** + * Flushes the underlying output stream if not null. + */ + public void flush() throws TTransportException { + if (outputStream_ == null) { + throw new TTransportException("Cannot flush null outputStream"); + } + try { + outputStream_.flush(); + } catch (IOException iox) { + throw new TTransportException(iox); + } + } +} diff --git a/lib/java/src/transport/TServerSocket.java b/lib/java/src/transport/TServerSocket.java new file mode 100644 index 00000000..a885fa14 --- /dev/null +++ b/lib/java/src/transport/TServerSocket.java @@ -0,0 +1,46 @@ +package com.facebook.thrift.transport; + +import java.io.IOException; +import java.net.ServerSocket; +import java.net.Socket; + +/** + * Wrapper around ServerSocket for Thrift. + * + * @author Mark Slee + */ +public class TServerSocket extends TServerTransport { + + private ServerSocket serverSocket_; + + public TServerSocket(ServerSocket serverSocket) { + serverSocket_ = serverSocket; + } + + public void listen() throws TTransportException {} + + protected TSocket acceptImpl() throws TTransportException { + if (serverSocket_ == null) { + throw new TTransportException("No underlying server socket."); + } + try { + Socket result = serverSocket_.accept(); + return new TSocket(result); + } catch (IOException iox) { + throw new TTransportException(iox); + } + } + + public void close() { + if (serverSocket_ != null) { + try { + serverSocket_.close(); + } catch (IOException iox) { + System.err.println("WARNING: Could not close server socket: " + + iox.getMessage()); + } + serverSocket_ = null; + } + } + +} diff --git a/lib/java/src/transport/TServerTransport.java b/lib/java/src/transport/TServerTransport.java new file mode 100644 index 00000000..6faeaabe --- /dev/null +++ b/lib/java/src/transport/TServerTransport.java @@ -0,0 +1,23 @@ +package com.facebook.thrift.transport; + +/** + * Server transport. Object which provides client transports. + * + * @author Mark Slee + */ +public abstract class TServerTransport { + + public abstract void listen() throws TTransportException; + + public final TTransport accept() throws TTransportException { + TTransport transport = acceptImpl(); + if (transport == null) { + throw new TTransportException("accept() may not return NULL"); + } + return transport; + } + + public abstract void close(); + + protected abstract TTransport acceptImpl() throws TTransportException; +} diff --git a/lib/java/src/transport/TSocket.java b/lib/java/src/transport/TSocket.java new file mode 100644 index 00000000..2092f119 --- /dev/null +++ b/lib/java/src/transport/TSocket.java @@ -0,0 +1,126 @@ +package com.facebook.thrift.transport; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.Socket; + +/** + * Socket implementation of the TTransport interface. To be commented soon! + * + * @author Mark Slee + */ +public class TSocket extends TIOStreamTransport { + + /** Wrapped Socket object */ + private Socket socket_ = null; + + /** Remote host */ + private String host_ = null; + + /** Remote port */ + private int port_ = 0; + + /** + * Constructor that takes an already created socket. + * + * @param socket Already created socket object + * @throws TTransportException if there is an error setting up the streams + */ + public TSocket(Socket socket) throws TTransportException { + socket_ = socket; + if (isOpen()) { + try { + inputStream_ = new BufferedInputStream(socket_.getInputStream()); + outputStream_ = new BufferedOutputStream(socket_.getOutputStream()); + } catch (IOException iox) { + close(); + throw new TTransportException(iox); + } + } + } + + /** + * Creates a new unconnected socket that will connect to the given host + * on the given port. + * + * @param host Remote host + * @param port Remote port + */ + public TSocket(String host, int port) { + socket_ = new Socket(); + host_ = host; + port_ = port; + } + + /** + * Returns a reference to the underlying socket. Can be used to set + * socket options, etc. If an underlying socket does not exist yet, this + * will create one. + */ + public Socket getSocket() { + if (socket_ == null) { + socket_ = new Socket(); + } + return socket_; + } + + /** + * Checks whether the socket is connected. + */ + public boolean isOpen() { + if (socket_ == null) { + return false; + } + return socket_.isConnected(); + } + + /** + * Connects the socket, creating a new socket object if necessary. + */ + public void open() throws TTransportException { + if (socket_ == null) { + if (host_.length() == 0) { + throw new TTransportException("Cannot open null host."); + } + if (port_ <= 0) { + throw new TTransportException("Cannot open without port."); + } + socket_ = new Socket(); + } + + if (isOpen()) { + throw new TTransportException("Socket already connected."); + } + + try { + socket_.connect(new InetSocketAddress(host_, port_)); + inputStream_ = new BufferedInputStream(socket_.getInputStream()); + outputStream_ = new BufferedOutputStream(socket_.getOutputStream()); + } catch (IOException iox) { + close(); + throw new TTransportException(iox); + } + } + + /** + * Closes the socket. + */ + public void close() { + // Close the underlying streams + super.close(); + + // Close the socket + if (socket_ != null) { + try { + socket_.close(); + } catch (IOException iox) { + System.err.println("WARNING: exception closing socket: " + + iox.getMessage()); + } + socket_ = null; + } + } + +} diff --git a/lib/java/src/transport/TTransport.java b/lib/java/src/transport/TTransport.java new file mode 100644 index 00000000..00610df4 --- /dev/null +++ b/lib/java/src/transport/TTransport.java @@ -0,0 +1,84 @@ +package com.facebook.thrift.transport; + +/** + * Generic class that encapsulates the I/O layer. This is basically a thin + * wrapper around the combined functionality of Java input/output streams. + * + * @author Mark Slee + */ +public abstract class TTransport { + + /** + * Queries whether the transport is open. + * + * @return True if the transport is open. + */ + public abstract boolean isOpen(); + + /** + * Opens the transport for reading/writing. + * + * @throws TTransportException if the transport could not be opened + */ + public abstract void open() + throws TTransportException; + + /** + * Closes the transport. + */ + public abstract void close(); + + /** + * Reads up to len bytes into buffer buf, starting att offset off. + * + * @param buf Array to read into + * @param off Index to start reading at + * @param len Maximum number of bytes to read + * @return The number of bytes actually read + * @throws TTransportException if there was an error reading data + */ + public abstract int read(byte[] buf, int off, int len) + throws TTransportException; + + /** + * Guarantees that all of len bytes are + * + * @param buf Array to read into + * @param off Index to start reading at + * @param len Maximum number of bytes to read + * @return The number of bytes actually read, which must be equal to len + * @throws TTransportException if there was an error reading data + */ + public int readAll(byte[] buf, int off, int len) + throws TTransportException { + int got = 0; + int ret = 0; + while (got < len) { + ret = read(buf, off+got, len-got); + if (ret == -1) { + throw new TTransportException("Cannot read. Remote side has closed."); + } + got += ret; + } + return got; + } + + /** + * Writes up to len bytes from the buffer. + * + * @param buf The output data buffer + * @param off The offset to start writing from + * @param len The number of bytes to write + * @throws TTransportException if there was an error writing data + */ + public abstract void write(byte[] buf, int off, int len) + throws TTransportException; + + /** + * Flush any pending data out of a transport buffer. + * + * @throws TTransportException if there was an error writing out data. + */ + public void flush() + throws TTransportException {} +} diff --git a/lib/java/src/transport/TTransportException.java b/lib/java/src/transport/TTransportException.java new file mode 100644 index 00000000..a67b6edf --- /dev/null +++ b/lib/java/src/transport/TTransportException.java @@ -0,0 +1,26 @@ +package com.facebook.thrift.transport; + +import com.facebook.thrift.TException; + +/** + * Transport exceptions. + * + * @author Mark Slee + */ +public class TTransportException extends TException { + public TTransportException() { + super(); + } + + public TTransportException(String message) { + super(message); + } + + public TTransportException(Throwable cause) { + super(cause); + } + + public TTransportException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/lib/java/src/types/Int32.java b/lib/java/src/types/Int32.java new file mode 100644 index 00000000..e6a8cf2a --- /dev/null +++ b/lib/java/src/types/Int32.java @@ -0,0 +1,75 @@ +package com.facebook.thrift.types; + +import java.io.ByteArrayOutputStream; + +/** + * Wrapper for Pillar TyInt32. We have to flip the byte order in here + * because Pillar is little endian. + * + * @author Mark Slee (mcslee@facebook.com) + * + * See: http://darksleep.com/player/JavaAndUnsignedTypes.html + * + */ +public class Int32 { + private int intValue_ = 0; + private byte[] data_ = new byte[4]; + + public int get() { + return intValue_; + } + + public byte[] data() { + return data_; + } + + public Int32() { + for (int i = 0; i < 4; i++) { + data_[i] = 0; + } + } + + public Int32(byte[] buf, int offset) { + read(buf, offset); + } + + public Int32(int value) { + set(value); + } + + public void set(int value) { + intValue_ = value; + data_[0] = (byte)((intValue_ & 0xFF000000L) >> 24); + data_[1] = (byte)((intValue_ & 0x00FF0000L) >> 16); + data_[2] = (byte)((intValue_ & 0x0000FF00L) >> 8); + data_[3] = (byte)((intValue_ & 0x000000FFL)); + } + + public void read(byte[] buf, int offset) { + for (int i = 0; i < 4; i++) { + data_[i] = buf[offset+i]; + } + + int[] bytes = new int[4]; + bytes[0] = (0x000000FF & ((int)data_[0])); + bytes[1] = (0x000000FF & ((int)data_[1])); + bytes[2] = (0x000000FF & ((int)data_[2])); + bytes[3] = (0x000000FF & ((int)data_[3])); + intValue_ = ((int) (bytes[0] << 24 | bytes[1] << 16 | + bytes[2] << 8 | bytes[3])); + } + + public String toString() { + return String.valueOf(intValue_); + } + + public int hashCode() { + return intValue_; + } + + public boolean equals(Object that) { + return ((that instanceof Int32) && + (this.intValue_ == ((Int32) that).intValue_)); + } + +} diff --git a/lib/java/src/types/Int64.java b/lib/java/src/types/Int64.java new file mode 100644 index 00000000..12bef4bc --- /dev/null +++ b/lib/java/src/types/Int64.java @@ -0,0 +1,90 @@ +package com.facebook.thrift.types; + +import java.io.ByteArrayOutputStream; + +/** + * 64-bit integers, just a wrapper around a long, and a byte-order + * reverser. + * + * @author Mark Slee (mcslee@facebook.com) + * + */ +public class Int64 { + private long longValue_ = 0L; + private byte[] data_ = new byte[8]; + + public long get() { + return longValue_; + } + + public byte[] data() { + return data_; + } + + public Int64() { + for (int i = 0; i < 8; i++) data_[i] = (byte) 0; + } + + public Int64(long value) { + set(value); + } + + public Int64(byte[] buf, int offset) { + read(buf, offset); + } + + + /** + * Yes, this could be done in a loop, but written out this way makes + * it easier to see how the bytes are actually generated. + */ + public void set(long value) { + longValue_ = value; + data_[0] = (byte)((longValue_ & 0xFF00000000000000L) >> 56); + data_[1] = (byte)((longValue_ & 0x00FF000000000000L) >> 48); + data_[2] = (byte)((longValue_ & 0x0000FF0000000000L) >> 40); + data_[3] = (byte)((longValue_ & 0x000000FF00000000L) >> 32); + data_[4] = (byte)((longValue_ & 0x00000000FF000000L) >> 24); + data_[5] = (byte)((longValue_ & 0x0000000000FF0000L) >> 16); + data_[6] = (byte)((longValue_ & 0x000000000000FF00L) >> 8); + data_[7] = (byte)((longValue_ & 0x00000000000000FFL)); + } + + /** + * Reverse byte order to calculate the value. + */ + public void read(byte[] buf, int offset) { + for (int i = 0; i < 8; i++) { + data_[i] = buf[offset+i]; + } + + long[] bytes = new long[8]; + bytes[0] = (0xFF & ((long)data_[0])); + bytes[1] = (0xFF & ((long)data_[1])); + bytes[2] = (0xFF & ((long)data_[2])); + bytes[3] = (0xFF & ((long)data_[3])); + bytes[4] = (0xFF & ((long)data_[4])); + bytes[5] = (0xFF & ((long)data_[5])); + bytes[6] = (0xFF & ((long)data_[6])); + bytes[7] = (0xFF & ((long)data_[7])); + + longValue_ = ((long) ((bytes[0] << 56) | (bytes[1] << 48) | + (bytes[2] << 40) | (bytes[3] << 32) | + (bytes[4] << 24) | (bytes[5] << 16) | + (bytes[6] << 8) | (bytes[7]))); + } + + public String toString() { + return String.valueOf(longValue_); + } + + public int hashCode() { + return (int)longValue_; + } + + public boolean equals(Object that) { + return ((that instanceof Int64) && + (this.longValue_ == ((Int64) that).longValue_)); + } + +} diff --git a/lib/java/src/types/UInt32.java b/lib/java/src/types/UInt32.java new file mode 100644 index 00000000..52c6da06 --- /dev/null +++ b/lib/java/src/types/UInt32.java @@ -0,0 +1,89 @@ +package com.facebook.thrift.types; + +import java.io.ByteArrayOutputStream; + +/** + * Bit-twiddling bullshit because java doesn't have unsigned types. + * Also, the JVM is big-endian, but Pillar is written on the little + * endian architecture, and it doesn't translated numbers to network + * byte order (also big-endian) when it transmits them. + * + * So, a UInt32 received by pillar will come over the net in little + * endian byte order, which means we have to reverse it for Java. + * + * @author Mark Slee (mcslee@facebook.com) + * + * See: http://darksleep.com/player/JavaAndUnsignedTypes.html + * + */ +public class UInt32 { + private long longValue_ = 0; + private byte[] data_ = new byte[4]; + + public long get() { + return longValue_; + } + + public int toInt() { + return (int) longValue_; + } + + public byte[] data() { + return data_; + } + + public UInt32() { + for (int i = 0; i < 4; i++) data_[i] = (byte) 0; + } + + public UInt32(byte[] buf, int offset) { + read(buf, offset); + } + + public UInt32(long value) { + set(value); + } + + public UInt32(int value) { + this((long)value); + } + + public void set(long value) { + if (value < 0) { + throw new RuntimeException("Cannot assign negative value to UInt32."); + } + longValue_ = value; + data_[0] = (byte)((longValue_ & 0xFF000000L) >> 24); + data_[1] = (byte)((longValue_ & 0x00FF0000L) >> 16); + data_[2] = (byte)((longValue_ & 0x0000FF00L) >> 8); + data_[3] = (byte)((longValue_ & 0x000000FFL)); + } + + public void read(byte[] buf, int offset) { + for (int i = 0; i < 4; i++) { + data_[i] = buf[offset+i]; + } + + int[] bytes = new int[4]; + bytes[0] = (0x000000FF & ((int)data_[0])); + bytes[1] = (0x000000FF & ((int)data_[1])); + bytes[2] = (0x000000FF & ((int)data_[2])); + bytes[3] = (0x000000FF & ((int)data_[3])); + longValue_ = ((long) ((bytes[0] << 24) | (bytes[1] << 16) | + (bytes[2] << 8) | (bytes[3]))) & 0xFFFFFFFFL; + } + + public String toString() { + return String.valueOf(longValue_); + } + + public int hashCode() { + return toInt(); + } + + public boolean equals(Object that) { + return ((that instanceof UInt32) && + (this.longValue_ == ((UInt32) that).longValue_)); + } + +} diff --git a/lib/java/src/types/UInt64.java b/lib/java/src/types/UInt64.java new file mode 100644 index 00000000..18cf44ae --- /dev/null +++ b/lib/java/src/types/UInt64.java @@ -0,0 +1,126 @@ +package com.facebook.thrift.types; + +import java.io.ByteArrayOutputStream; + +/** + * More bit-twiddling bullshit. Take a look at UInt32 for details about + * the endian difference between Pillar and the JVM. + * + * Since we don't do arithmetic on unsigned longs, we just implement + * them here as a raw byte array. The only caveat is the constructor + * that takes an integer value. That value will be big-endian (JVM) + * but the byte array needs to be little-endian (Pillar) + * + * @author Mark Slee (mcslee@facebook.com) + * + */ +public class UInt64 { + private byte[] data_ = new byte[8]; + + public byte[] data() { + return data_; + } + + public UInt64() { + for (int i = 0; i < 8; i++) { + data_[i] = (byte) 0; + } + } + + public UInt64(long value) { + set(value); + } + + public UInt64(byte[] buf, int offset) { + read(buf, offset); + } + + public long toLong() { + long[] bytes = new long[8]; + bytes[0] = (0xFF & ((long)data_[0])); + bytes[1] = (0xFF & ((long)data_[1])); + bytes[2] = (0xFF & ((long)data_[2])); + bytes[3] = (0xFF & ((long)data_[3])); + bytes[4] = (0xFF & ((long)data_[4])); + bytes[5] = (0xFF & ((long)data_[5])); + bytes[6] = (0xFF & ((long)data_[6])); + bytes[7] = (0xFF & ((long)data_[7])); + + return ((long) ((bytes[0] << 56) | (bytes[1] << 48) | + (bytes[2] << 40) | (bytes[3] << 32) | + (bytes[4] << 24) | (bytes[5] << 16) | + (bytes[6] << 8) | (bytes[7]))); + } + + /** + * "HOLD IT! Pay close attention..." -Prodigy - Out of Space + * + * This is some wacky business. We want to take the integer value + * represented JVM style and put it into an 8-byte array that mirrors + * Intel/Pillar endianness (little). To do this, we graduate the + * integer to a long and then reverse the byte order, so bytes + * 5-8 in the JVM long become bytes 3-0 in the Pillar representation. + * + * NOTE: value MUST be positive, or else shit gets ill. + */ + public void set(long longValue) { + if (longValue < 0) { + throw new RuntimeException("Cannot make UInt64 from a negative value."); + } + data_[0] = (byte)((longValue & 0xFF00000000000000L) >> 56); + data_[1] = (byte)((longValue & 0x00FF000000000000L) >> 48); + data_[2] = (byte)((longValue & 0x0000FF0000000000L) >> 40); + data_[3] = (byte)((longValue & 0x000000FF00000000L) >> 32); + data_[4] = (byte)((longValue & 0x00000000FF000000L) >> 24); + data_[5] = (byte)((longValue & 0x0000000000FF0000L) >> 16); + data_[6] = (byte)((longValue & 0x000000000000FF00L) >> 8); + data_[7] = (byte)((longValue & 0x00000000000000FFL)); + } + + public void read(byte[] buf, int offset) { + for (int i = 0; i < 8; i++) { + data_[i] = buf[offset+i]; + } + } + + /** + * Equivalent to << 8, shifting left by a byte. + */ + public UInt64 lshift() { + for (int i = 7; i > 0; i--) { + data_[i] = data_[i-1]; + } + data_[0] = (byte) 0; + return this; + } + + /** + * Equivalent to |, logical or across all bytes + */ + public UInt64 lor(UInt64 that) { + for (int i = 0; i < 8; i++) { + this.data_[i] = (byte) (this.data_[i] | that.data_[i]); + } + return this; + } + + public int hashCode() { + return ((0xFF & (int)data_[3]) << 24) | + ((0xFF & (int)data_[2]) << 16) | + ((0xFF & (int)data_[1]) << 8) | + ((0xFF & (int)data_[0])); + } + + public boolean equals(Object that) { + if (that instanceof UInt64) { + for (int i = 0; i < 8; i++) { + if (this.data_[i] != ((UInt64)that).data_[i]) { + return false; + } + } + return true; + } + return false; + } + +} diff --git a/lib/java/src/types/UInt8.java b/lib/java/src/types/UInt8.java new file mode 100644 index 00000000..640a7e11 --- /dev/null +++ b/lib/java/src/types/UInt8.java @@ -0,0 +1,60 @@ +package com.facebook.thrift.types; + +import java.io.ByteArrayOutputStream; + +/** + * WILL IT EVER END? Even 'byte' is signed in Java, so we must use + * a short to represent bytes. + * + * @author Mark Slee (mcslee@facebook.com) + * + */ +public class UInt8 { + private short shortValue_; + private byte[] data_ = new byte[1]; + + public short get() { + return shortValue_; + } + + public byte[] data() { + return data_; + } + + public UInt8() { + data_[0] = (byte)0; + } + + public UInt8(byte[] buf, int offset) { + read(buf, offset); + } + + public UInt8(short value) { + set(value); + } + + public void set(short value) { + if (value < 0) throw new RuntimeException("Cannot apply negative value to UInt8"); + shortValue_ = value; + data_[0] = (byte)((shortValue_ & 0x00FF)); + } + + public void read(byte[] buf, int offset) { + data_[0] = buf[offset]; + shortValue_ = (short) (0x00FF & (short)data_[0]); + } + + public String toString() { + return String.valueOf(shortValue_); + } + + public int hashCode() { + return (int)shortValue_; + } + + public boolean equals(Object that) { + return ((that instanceof UInt8) && + (this.shortValue_ == ((UInt8) that).shortValue_)); + } + +}