Thrift java 客户端无法正确处理联合

Thrift java client cannot handle union properly

死的简单节俭联盟的例子。 环境:最新的 thrift,cpp 作为服务器,java 作为客户端 mytest.thrift:

namespace java com.wilbeibi.thrift

union Value {
  1: i16     i16_v,
  2: string  str_v,
}

struct Box {
  1: Value value;
}

service MyTest {
  Box echoUnion(1: i32 number);
}

C++ server code:

#include "MyTest.h"
#include <thrift/protocol/TBinaryProtocol.h>
#include <thrift/server/TSimpleServer.h>
#include <thrift/transport/TServerSocket.h>
#include <thrift/transport/TBufferTransports.h>

using namespace ::apache::thrift;
using namespace ::apache::thrift::protocol;
using namespace ::apache::thrift::transport;
using namespace ::apache::thrift::server;

using boost::shared_ptr;

class MyTestHandler : virtual public MyTestIf {
 public:
  MyTestHandler() {
    // Your initialization goes here
  }

  void echoUnion(Box& _return, const int32_t number) {
    // Your implementation goes here
  printf("Into echoUnion\n");
  if (number % 2 == 0) {
    Value v;
    v.__set_i16_v(100);
    v.__isset.i16_v = true;
    _return.__set_value(v);
    printf("Even number set int32\n");
  } else {
    Value v;
    v.__set_str_v("String value");
    v.__isset.str_v = true;
    _return.__set_value(v);
    printf("Odd number set string\n");
  }
    printf("echoUnion\n");
  }

};

int main(int argc, char **argv) {
  int port = 9090;
  shared_ptr<MyTestHandler> handler(new MyTestHandler());
  shared_ptr<TProcessor> processor(new MyTestProcessor(handler));
  shared_ptr<TServerTransport> serverTransport(new TServerSocket(port));
  shared_ptr<TTransportFactory> transportFactory(new TBufferedTransportFactory());
  shared_ptr<TProtocolFactory> protocolFactory(new TBinaryProtocolFactory());

  TSimpleServer server(processor, serverTransport, transportFactory, protocolFactory);
  printf("Server is running on %d\n", port);
  server.serve();
  return 0;
}

java client code:

// some imports here
public class Client {
    public void startClient() {
        TTransport transport;
            try {
               transport = new TSocket("localhost", 9090);
               TProtocol protocol = new TBinaryProtocol(transport);
               MyTest.Client client = new MyTest.Client(protocol);
               transport.open();
               Box box = client.echoUnion(1);              
               System.out.println(box.toString());

               Box box2 = client.echoUnion(2);
               System.out.println(box2.toString());

               transport.close();
           } catch (TTransportException e) {
               e.printStackTrace();
           } catch (TException e) {
               e.printStackTrace();
           }
       }
     public static void main(String[] args) {
           Client client = new Client();
           client.startClient();
       }
}     

不知何故,java 客户端无法正确打印字符串。 (我还写了一个 python 客户端,但这似乎可行)

要点上的完整代码:thrift file, c++ and java code

实际上您正在观察 THRIFT-1833 错误,该错误会导致编译器为联合类型生成无效的 C++ 代码。

在您的情况下,服务器写入联合类型的两个字段,而客户端始终只读取第一个 - i16_v(剩余字节仍驻留在缓冲区中)。所以第二次读取永远不会结束,因为它在缓冲区中发现了一些意外数据。

您可以使用 struct 代替 union 并手动维护单字段逻辑。或者你可以 contribute/wait 直到错误被修复。

最后一个选项是对错误生成的 C++ 源代码应用补丁,如下所示:

--- mytest_types.cpp    2016-02-26 20:02:57.210652969 +0300
+++ mytest_types.cpp.old   2016-02-26 20:02:39.650652742 +0300 
@@ -80,13 +80,17 @@
apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot);
xfer += oprot->writeStructBegin("Value");

-  xfer += oprot->writeFieldBegin("i16_v", ::apache::thrift::protocol::T_I16, 1);
-  xfer += oprot->writeI16(this->i16_v);
-  xfer += oprot->writeFieldEnd();
+  if (this->__isset.i16_v) {
+    xfer += oprot->writeFieldBegin("i16_v", ::apache::thrift::protocol::T_I16, 1);
+    xfer += oprot->writeI16(this->i16_v);
+    xfer += oprot->writeFieldEnd();
+  }

-  xfer += oprot->writeFieldBegin("str_v", ::apache::thrift::protocol::T_STRING, 2);
-  xfer += oprot->writeString(this->str_v);
-  xfer += oprot->writeFieldEnd();
+  if (this->__isset.str_v) {
+    xfer += oprot->writeFieldBegin("str_v", ::apache::thrift::protocol::T_STRING, 2);
+    xfer += oprot->writeString(this->str_v);
+    xfer += oprot->writeFieldEnd();
+  }