Overview
12 Building Clients and Servers with Java
This chapter introduces how to build RPC clients and servers on the JVM with Apache Thrift, emphasizing Java’s portability and its ecosystem of JVM languages. It explains how to obtain or build the Thrift Java library, how logging is handled through SLF4J, and how Thrift’s “write once, run anywhere” model enables seamless interoperability across Java and other JVM languages. The narrative starts with environment setup and progresses to practical development patterns, highlighting the balance between simplicity, performance, and cross-language integration.
Using a small Hello World service, the chapter demonstrates the end-to-end workflow: writing IDL, generating Java stubs, and wiring up a client and server with transports and protocols (for example, TSocket plus TBinaryProtocol) and a simple single-threaded server. It clarifies the structure of generated service classes (Client, Processor, and Iface) and their asynchronous counterparts, then shows how to compile and run projects with Ant or Maven, including dependency management and packaging into a self-contained application JAR. The chapter also illustrates JVM polyglot use by reusing the Java-generated stubs from Clojure, underscoring how any JVM language can call Thrift services via shared bytecode.
Beyond the basics, the chapter surveys Java transports (blocking and nonblocking sockets, file, and memory), layered transports (framed, HTTP, and compression), protocols (binary, compact, and JSON), and server types ranging from simple single-threaded to high-throughput nonblocking variants and servlet integration. It then motivates and implements asynchronous RPC in Java: why avoiding blocking improves throughput, how to build an async server with nonblocking I/O and framed transport (e.g., THsHaServer), and how to write an async client with TAsyncClientManager and callback handlers, including timeout handling. Throughout, it stresses matching compatible transports/protocols between peers and choosing the server model that best fits latency and concurrency needs.
Summary
This chapter has covered:
- How to setup Apache Thrift for Java and JVM based development
- How to use Ant and Maven with Apache Thrift
- How to build Java RPC clients and servers
- An overview of Apache Thrift Java transports, protocols and servers
- How to build asynchronous RPC clients and servers
FAQ
What are the easiest ways to set up Apache Thrift for Java development?
You can either depend on a published libthrift JAR from Maven Central (groupId: org.apache.thrift, artifactId: libthrift) or build from source. Pre-1.0 releases were built with Ant (run “ant” in thrift/lib/java), and 1.0+ are built with Maven (run “mvn package” in thrift/lib/java). After building, add the resulting libthrift JAR to your classpath. Maven Central URL: http://repo1.maven.org/maven2/org/apache/thrift/libthrift
How does SLF4J integrate with the Thrift Java library and what do I need on my classpath?
libthrift uses SLF4J for logging. Ensure exactly one SLF4J binding is on the classpath; multiple or missing bindings cause runtime errors. For simple setups or examples, include slf4j-api and slf4j-nop to suppress logging, or choose another binding (e.g., slf4j-log4j12) with its implementation. Thrift historically requires SLF4J ≥ 1.5.8.
How do I generate Java code from a Thrift IDL and what classes are produced?
Run “thrift --gen java your_service.thrift”. The compiler emits a service class named after your service (for example, helloSvc.java) that contains: Client (synchronous client stub), Processor (server-side dispatcher), and Iface (service interface). It also generates asynchronous counterparts: AsyncClient, AsyncProcessor, and AsyncIface.
What does a minimal synchronous Java client look like and which exceptions should I handle?
Create a TSocket(host, port), wrap it in a TBinaryProtocol, construct the generated Service.Client with the protocol, call open() on the transport, invoke RPCs, and close() when done. Catch org.apache.thrift.TException to handle Thrift errors (including TTransportException for transport issues and TApplicationException for interface misuse such as version mismatches).
How do I implement a basic Java Thrift server and which server class should I start with?
Implement the generated Iface in a handler, wrap it in a generated Processor, create a TServerSocket(port) endpoint, and pass these into a TSimpleServer (or another server type) and call serve(). If you don’t specify a protocol factory, the default is TBinaryProtocol. TSimpleServer is single-threaded and great for getting started or simple demos.
How can I build and run a Thrift Java project with Ant?
Create a build.xml with targets to clean, generate sources (“thrift -gen java”), compile (javac with classpaths including libthrift and SLF4J), and run separate runServer/runClient targets. Use classpath entries that include libthrift, slf4j-api, your SLF4J binding (e.g., slf4j-nop), generated sources (gen-java), and your classes.
How do I build a Thrift-based Java project with Maven, including IDL generation and a fat JAR?
Use the standard layout (src/main/java and src/main/thrift). Add org.apache.thrift:thrift-maven-plugin bound to the generate-sources phase to run the Thrift compiler (ensure the plugin, compiler, and libthrift versions match). Declare dependencies on org.apache.thrift:libthrift and your SLF4J binding. To package a single runnable JAR, configure maven-assembly-plugin with the “jar-with-dependencies” descriptor and run “mvn assembly:assembly”.
Can I use the Thrift Java library from other JVM languages?
Yes. Thrift Java bytecode is usable from any JVM language. You can generate Java stubs, compile them, and call them from languages like Clojure, Scala, or Groovy. For idiomatic Scala, consider Twitter’s Scrooge, which generates native Scala bindings for Thrift.
Which transports, protocols, and servers are available in the Java library, and when should I use framed or nonblocking options?
Endpoint transports include TSocket and TNonblockingSocket (plus file/memory transports). Layered transports include TFramedTransport/TFastFramedTransport, THttpClient, and TZlibTransport. Protocols include TBinaryProtocol, TCompactProtocol, and TJSONProtocol. Nonblocking servers (TNonblockingServer, THsHaServer, TThreadedSelectorServer) require framed transport; clients should use TFramedTransport when talking to these servers.
How do asynchronous Java clients and servers work in Thrift, and what are key caveats?
Async servers use the service-specific AsyncProcessor and a nonblocking server (e.g., THsHaServer) over TNonblockingServerSocket with framed I/O. Async clients use TAsyncClientManager, TNonblockingSocket, and the generated AsyncClient; you provide an AsyncMethodCallback to receive results. Typical caveats: use framed transport, manage timeouts, and note that a single async client object generally allows only one in-flight call—sequence calls (e.g., via a latch) or use multiple client instances for parallelism.