Various Thrift fixes, including Application Exception support in Ruby, better errror messages across languages, etc.

Reviewed By: thrift


git-svn-id: https://svn.apache.org/repos/asf/incubator/thrift/trunk@665058 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/lib/php/src/transport/TPhpStream.php b/lib/php/src/transport/TPhpStream.php
index 8a4fb0a..03837a8 100644
--- a/lib/php/src/transport/TPhpStream.php
+++ b/lib/php/src/transport/TPhpStream.php
@@ -40,13 +40,13 @@
     if ($this->read_) {
       $this->inStream_ = @fopen('php://input', 'r');
       if (!is_resource($this->inStream_)) {
-        throw new Exception('TPhpStream: Could not open php://input');
+        throw new TException('TPhpStream: Could not open php://input');
       }
     }
     if ($this->write_) {
       $this->outStream_ = @fopen('php://output', 'w');
       if (!is_resource($this->outStream_)) {
-        throw new Exception('TPhpStream: Could not open php://output');
+        throw new TException('TPhpStream: Could not open php://output');
       }
     }
   }
@@ -71,7 +71,7 @@
   public function read($len) {
     $data = @fread($this->inStream_, $len);
     if (!$data) {
-      throw new Exception('TPhpStream: Could not read '.$len.' bytes');
+      throw new TException('TPhpStream: Could not read '.$len.' bytes');
     }
     return $data;
   }
@@ -80,7 +80,7 @@
     while (strlen($buf) > 0) {
       $got = @fwrite($this->outStream_, $buf);
       if ($got === 0 || $got === FALSE) {
-        throw new Exception('TPhpStream: Could not write '.strlen($buf).' bytes');
+        throw new TException('TPhpStream: Could not write '.strlen($buf).' bytes');
       }
       $buf = substr($buf, $got);
     }
diff --git a/lib/php/src/transport/TSocket.php b/lib/php/src/transport/TSocket.php
index 1600b6f..cce3a44 100644
--- a/lib/php/src/transport/TSocket.php
+++ b/lib/php/src/transport/TSocket.php
@@ -160,7 +160,7 @@
       if ($this->debug_) {
         call_user_func($this->debugHandler_, $error);
       }
-      throw new Exception($error);
+      throw new TException($error);
     }
     
     stream_set_timeout($this->handle_, 0, $this->sendTimeout_*1000);
@@ -197,17 +197,17 @@
       if ($buf === FALSE || $buf === '') {
         $md = stream_get_meta_data($this->handle_);
         if ($md['timed_out']) {
-          throw new Exception('TSocket: timed out reading '.$len.' bytes from '.
-                              $this->host_.':'.$this->port_);
+          throw new TException('TSocket: timed out reading '.$len.' bytes from '.
+                               $this->host_.':'.$this->port_);
         } else {
-          throw new Exception('TSocket: Could not read '.$len.' bytes from '.
-                              $this->host_.':'.$this->port_);
+          throw new TException('TSocket: Could not read '.$len.' bytes from '.
+                               $this->host_.':'.$this->port_);
         }
       } else if (($sz = strlen($buf)) < $len) {
         $md = stream_get_meta_data($this->handle_);
         if ($md['timed_out']) {
-          throw new Exception('TSocket: timed out reading '.$len.' bytes from '.
-                              $this->host_.':'.$this->port_);
+          throw new TException('TSocket: timed out reading '.$len.' bytes from '.
+                               $this->host_.':'.$this->port_);
         } else {
           $pre .= $buf;
           $len -= $sz;
@@ -233,11 +233,11 @@
     if ($data === FALSE || $data === '') {
       $md = stream_get_meta_data($this->handle_);
       if ($md['timed_out']) {
-        throw new Exception('TSocket: timed out reading '.$len.' bytes from '.
-                            $this->host_.':'.$this->port_);
+        throw new TException('TSocket: timed out reading '.$len.' bytes from '.
+                             $this->host_.':'.$this->port_);
       } else {
-        throw new Exception('TSocket: Could not read '.$len.' bytes from '.
-                            $this->host_.':'.$this->port_);
+        throw new TException('TSocket: Could not read '.$len.' bytes from '.
+                             $this->host_.':'.$this->port_);
       }
     }
     return $data;
@@ -258,11 +258,11 @@
       if ($got === 0 || $got === FALSE) {
         $md = stream_get_meta_data($this->handle_);
         if ($md['timed_out']) {
-          throw new Exception('TSocket: timed out writing '.$len.' bytes from '.
-                              $this->host_.':'.$this->port_);
+          throw new TException('TSocket: timed out writing '.$len.' bytes from '.
+                               $this->host_.':'.$this->port_);
         } else {
-            throw new Exception('TSocket: Could not write '.strlen($buf).' bytes '.
-                                $this->host_.':'.$this->port_);
+            throw new TException('TSocket: Could not write '.strlen($buf).' bytes '.
+                                 $this->host_.':'.$this->port_);
         }
       }
       $buf = substr($buf, $got);
@@ -275,8 +275,8 @@
   public function flush() {
     $ret = fflush($this->handle_);
     if ($ret === FALSE) {
-      throw new Exception('TSocket: Could not flush: '.
-                          $this->host_.':'.$this->port_);
+      throw new TException('TSocket: Could not flush: '.
+                           $this->host_.':'.$this->port_);
     }
   }
 }
diff --git a/lib/php/src/transport/TSocketPool.php b/lib/php/src/transport/TSocketPool.php
index 9e5ebcd..b3efb2d 100644
--- a/lib/php/src/transport/TSocketPool.php
+++ b/lib/php/src/transport/TSocketPool.php
@@ -221,7 +221,7 @@
             // Successful connection, return now
             return;
 
-          } catch (Exception $x) {
+          } catch (TException $tx) {
             // Connection failed
           }
         }
@@ -268,7 +268,7 @@
     if ($this->debug_) {
       call_user_func($this->debugHandler_, $error);
     }
-    throw new Exception($error);
+    throw new TException($error);
   }
 }
 
diff --git a/lib/py/src/Thrift.py b/lib/py/src/Thrift.py
index a1ec6cc..aeca9b9 100644
--- a/lib/py/src/Thrift.py
+++ b/lib/py/src/Thrift.py
@@ -43,6 +43,7 @@
 
   def __init__(self, message=None):
     Exception.__init__(self, message)
+    self.message = message
 
 class TApplicationException(TException):
 
diff --git a/lib/py/src/transport/TSocket.py b/lib/py/src/transport/TSocket.py
index 1f9c480..280d6aa 100644
--- a/lib/py/src/transport/TSocket.py
+++ b/lib/py/src/transport/TSocket.py
@@ -13,7 +13,6 @@
 
   """Socket implementation of TTransport base."""
 
-
   def __init__(self, host='localhost', port=9090):
     self.host = host
     self.port = port
@@ -34,7 +33,7 @@
       self.handle = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
       self.handle.connect((self.host, self.port))
     except socket.error, e:
-      raise TTransportException(TTransportException.NOT_OPEN)
+      raise TTransportException(TTransportException.NOT_OPEN, 'Could not connect to %s:%d' % (self.host, self.port))
 
   def close(self):
     if self.handle != None:
diff --git a/lib/py/src/transport/TTransport.py b/lib/py/src/transport/TTransport.py
index b655b32..305c10b 100644
--- a/lib/py/src/transport/TTransport.py
+++ b/lib/py/src/transport/TTransport.py
@@ -14,11 +14,11 @@
 
   """Custom Transport Exception class"""
 
-  UNKNOWN = 0,
-  NOT_OPEN = 1,
-  ALREADY_OPEN = 2,
-  TIMED_OUT = 3,
-  END_OF_FILE = 4,
+  UNKNOWN = 0
+  NOT_OPEN = 1
+  ALREADY_OPEN = 2
+  TIMED_OUT = 3
+  END_OF_FILE = 4
 
   def __init__(self, type=UNKNOWN, message=None):
     TException.__init__(self, message)
diff --git a/lib/rb/lib/thrift/protocol/tprotocol.rb b/lib/rb/lib/thrift/protocol/tprotocol.rb
index 4376024..3c13b4b 100644
--- a/lib/rb/lib/thrift/protocol/tprotocol.rb
+++ b/lib/rb/lib/thrift/protocol/tprotocol.rb
@@ -8,26 +8,8 @@
 #
 # Author: Mark Slee <mcslee@facebook.com>
 #
-class TType
-  STOP = 0
-  VOID = 1
-  BOOL = 2
-  BYTE = 3
-  DOUBLE = 4
-  I16 = 6
-  I32 = 8
-  I64 = 10
-  STRING = 11
-  STRUCT = 12
-  MAP = 13
-  SET = 14
-  LIST = 15
-end
 
-class TMessageType
-  CALL = 1
-  REPLY = 2
-end
+require 'thrift/thrift'
 
 class TProtocol
   
diff --git a/lib/rb/lib/thrift/thrift.rb b/lib/rb/lib/thrift/thrift.rb
index 16c7174..2b9ebbb 100644
--- a/lib/rb/lib/thrift/thrift.rb
+++ b/lib/rb/lib/thrift/thrift.rb
@@ -8,6 +8,99 @@
 #
 # Author: Mark Slee <mcslee@facebook.com>
 #
+
+class TType
+  STOP = 0
+  VOID = 1
+  BOOL = 2
+  BYTE = 3
+  DOUBLE = 4
+  I16 = 6
+  I32 = 8
+  I64 = 10
+  STRING = 11
+  STRUCT = 12
+  MAP = 13
+  SET = 14
+  LIST = 15
+end
+
+class TMessageType
+  CALL = 1
+  REPLY = 2
+  EXCEPTION = 3
+end
+
 module TProcessor
   def process(iprot, oprot); nil; end
 end
+
+class TException < StandardError
+  def initialize(message)
+    super(message)
+    @message = message
+  end
+
+  attr_reader :message
+end
+
+class TApplicationException < TException
+
+  UNKNOWN = 0
+  UNKNOWN_METHOD = 1
+  INVALID_MESSAGE_TYPE = 2
+  WRONG_METHOD_NAME = 3
+  BAD_SEQUENCE_ID = 4
+  MISSING_RESULT = 5
+  
+  attr_reader :type
+
+  def initialize(type=UNKNOWN, message=nil)
+    super(message)
+    @type = type
+  end
+
+  def read(iprot)
+    iprot.readStructBegin()
+    while true
+      fname, ftype, fid = iprot.readFieldBegin()
+      if (ftype === TType::STOP)
+        break
+      end
+      if (fid == 1)
+        if (ftype === TType::STRING)
+          @message = iprot.readString();
+        else
+          iprot.skip(ftype)
+        end
+      elsif (fid == 2)
+        if (ftype === TType::I32)
+          @type = iprot.readI32();
+        else
+          iprot.skip(ftype)
+        end
+      else
+        iprot.skip(ftype)
+      end
+      iprot.readFieldEnd()
+    end
+    iprot.readStructEnd()
+  end
+
+  def write(oprot)
+    oprot.writeStructBegin('TApplicationException')
+    if (@message != nil)
+      oprot.writeFieldBegin('message', TType::STRING, 1)
+      oprot.writeString(@message)
+      oprot.writeFieldEnd()
+    end
+    if (@type != nil)
+      oprot.writeFieldBegin('type', TType::I32, 2)
+      oprot.writeI32(@type)
+      oprot.writeFieldEnd()
+    end
+    oprot.writeFieldStop()
+    oprot.writeStructEnd()
+  end
+
+end
diff --git a/lib/rb/lib/thrift/transport/tsocket.rb b/lib/rb/lib/thrift/transport/tsocket.rb
index d2e197f..9de2b8e 100644
--- a/lib/rb/lib/thrift/transport/tsocket.rb
+++ b/lib/rb/lib/thrift/transport/tsocket.rb
@@ -23,7 +23,11 @@
   end
 
   def open()
-    @handle = TCPSocket.new(@host, @port)
+    begin
+      @handle = TCPSocket.new(@host, @port)
+    rescue StandardError
+      raise TTransportException.new(TTransportException::NOT_OPEN, "Could not connect to #{@host}:#{@port}")
+    end
   end
 
   def isOpen()
@@ -31,15 +35,23 @@
   end
   
   def write(str)
-    @handle.write(str)
+    begin
+      @handle.write(str)
+    rescue StandardError
+      raise TTransportException.new(TTransportException::NOT_OPEN)
+    end
   end
 
   def read(sz)
-    data = @handle.recv(sz)
-    if (data.length == 0)
-      raise TTransportException.new("TSocket: Could not read #{sz} bytes from #{@host}:#{@port}")
+    begin
+      data = @handle.recv(sz)
+      if (data.length == 0)
+        raise TTransportException.new("TSocket: Could not read #{sz} bytes from #{@host}:#{@port}")
+      end
+      return data
+    rescue StandardError
+      raise TTransportException.new(TTransportException::NOT_OPEN)
     end
-    return data
   end
 
   def close()
diff --git a/lib/rb/lib/thrift/transport/ttransport.rb b/lib/rb/lib/thrift/transport/ttransport.rb
index 92c606a..5b9b8b1 100644
--- a/lib/rb/lib/thrift/transport/ttransport.rb
+++ b/lib/rb/lib/thrift/transport/ttransport.rb
@@ -8,10 +8,24 @@
 #
 # Author: Mark Slee <mcslee@facebook.com>
 #
-class TTransportException < StandardError
-  def initialize(message)
+
+require 'thrift/thrift'
+
+class TTransportException < TException
+
+  UNKNOWN = 0
+  NOT_OPEN = 1
+  ALREADY_OPEN = 2
+  TIMED_OUT = 3
+  END_OF_FILE = 4
+
+  attr_reader :type
+
+  def initialize(type=UNKNOWN, message=nil)
     super(message)
-  end 
+    @type = type
+  end
+
 end
 
 class TTransport
@@ -54,5 +68,42 @@
     return trans
   end
 end
+  
+class TBufferedTransport < TTransport
+  def initialize(transport)
+    @transport = transport
+    @wbuf = ''
+  end
+  
+  def isOpen()
+    return @transport.isOpen()
+  end
 
-    
+  def open()
+    @transport.open()
+  end
+
+  def close()
+    @transport.close()
+  end
+  
+  def read(sz)
+    return @transport.read(sz)
+  end
+  
+  def write(buf)
+    @wbuf += buf
+  end
+
+  def flush()
+    @transport.write(@wbuf)
+    @transport.flush()
+    @wbuf = ''
+  end
+end
+
+class TBufferedTransportFactory < TTransportFactory
+  def getTransport(transport)
+    return TBufferedTransport.new(transport)
+  end
+end