Search code examples
javascalathriftthrift-protocol

Can generate .thrift files from existing java/scala interfaces and data types?


Is there an easy way to take existing Java/scala datatypes and API interfaces and produce corresponding .thrift files? Having Thrift generate server data structures is excessively invasive as it has the consequences:

  • I cannot annotate my data structures (e.g. for, XML, JSON, hibernate persistence, ...)
  • this pattern conflicts with other serialization frameworks that want to own, or require modification of my source files.

As a result, its seems like thrift forces itself into being the exclusive persistence format for my server -- unless, that is, I create a data-marshalling wrapper around Thrift or the other my persistence formats that deal with these data structures (hibernate, Jackson, scala BeanProperty, ...). However, this defeats the purpose of an automated data-marshalling tool such as thrift and leads straight to the error-prone world of having to maintain identical-but-separate interfaces and data-structures (= waste of talented engineer time and energy).

I'm totally happy with Thrift auto-generating client code. However, I (strongly) feel that I need the freedom to edit the data structures my server deals with in the APIs.


Solution

  • You can use Swift.

    To make a long story short; annotate your classes and interfaces (structs and services in Thrift parlance). Then you can either run Swift's client/server code or you can use the swift2thrift generator to produce equivalent IDL and use the Thrift compiler to generate clients (the latter is what I recommend for what you're describing).

    Once that is done to create a TProcessor that you can use in a TServlet with normal TProtocol/TTransport objects, do something like this in your servlet's init():

    protected void addProcessor(String name, Object svc) {
        ThriftCodecManager codecManager = new ThriftCodecManager(
            new CompilerThriftCodecFactory(false)
        );
        List<ThriftEventHandler> eventList = Collections.emptyList();
        ThriftServiceProcessor proc = new ThriftServiceProcessor(codecManager, eventList, svc);
        this.processors.put(name, proc);
        this.multiplex.registerProcessor(name, NiftyProcessorAdapters.processorToTProcessor(proc));
    }
    

    The multiplex instance variable in this example is an instance of TMultiplexedProcessor from libthrift.jar.

    Then just do this in your doPost():

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        getServletContext().log("entering doPost()");
        TTransport inTransport = null;
        TTransport outTransport = null;
        try {
    
            InputStream in = request.getInputStream();
            OutputStream out = response.getOutputStream();
    
            TTransport transport = new TIOStreamTransport(in, out);
            inTransport = transport;
            outTransport = transport;
    
            TProtocol inProtocol = getInProtocolFactory().getProtocol(inTransport);
            TProtocol outProtocol = getOutProtocolFactory().getProtocol(outTransport);
    
            if (multiplex.process(inProtocol, outProtocol)) {
                out.flush();
            } else {
                throw new ServletException("multiplex.process() returned false");
            }
        } catch (TException te) {
            throw new ServletException(te);
        } finally {
            if (inTransport != null) {
                inTransport.close();
            }
            if (outTransport != null) {
                outTransport.close();
            }
        }
    }
    

    FYI - TJSONProtocol doesn't work with the version of Swift prior to version 0.14 so at this time you'll need to build from source if you need to use that.

    Also... Swift forces your structs to be marked final... JPA spec says entities can't be final... seems to work ok with Eclipselink anyhow but YMMV