At Intelliseq we are using thrift as a communication protocol for our genequery noSQL database. In this post I will describe basic thrift server and a test client.
First, we need to download and install thrift. See http://thrift.apache.org/.
Next we need to design our interface. This is what I like about thrift. You need to design interface first. It is my favorite approach to software architecture design. Below is a simple interface of service with enum, structure, exception and few methods. I didn't include collections, imports, one way methods and few more less important featuresof thrift. You can read more about them in this great missing guide.
namespace java pl.intelliseq.largedata.thrift struct Message { 1: required string message } struct User { 1: required string firstname 2: required string lastname } enum Wrong { FIRST = 1, SECOND = 2 } exception InvalidFirst { 1: string why } ExampleService { bool isAlive(), Message getHello(1:User user), void getError(1: Wrong wrong) throws (1:InvalidFirst ouch) }
We can autogenerate code with this command. Assuming that our thrift file is in thrift subdirectory.
thrift --gen java thrift/Exampleservice.thriftAll classes were autogenerated and placed in gen-java directory. We can set this directory as src directory. Next, we will implement all methods:
package pl.intelliseq.largedata.thrift; import org.apache.thrift.TException; public class ThriftHandler implements ExampleService.Iface { @Override public boolean isAlive() throws TException { return true; } @Override public Message getHello(User user) throws TException { Message message = new Message(); message.setMessage("Hello " + user.getFirstname() + " " + user.getLastname()); return message; } @Override public void getError(Wrong wrong) throws InvalidFirst, TException { if (wrong.equals(Wrong.FIRST)) throw new InvalidFirst(); } }We have also to set up our server. We need to run it in separate thread if we want to test it.
package pl.intelliseq.largedata.thrift; import org.apache.thrift.protocol.TBinaryProtocol; import org.apache.thrift.server.TServer; import org.apache.thrift.server.TThreadedSelectorServer; import org.apache.thrift.transport.TFramedTransport; import org.apache.thrift.transport.TNonblockingServerSocket; import org.apache.thrift.transport.TNonblockingServerTransport; import org.apache.thrift.transport.TTransportException; public class ThriftServer implements Runnable { TServer server; public void init() throws InterruptedException, TTransportException { System.out.println("Starting server on port 9090 ..."); ThriftHandler handler = new ThriftHandler(); ExampleService.ProcessorAnd finally, we will write our test:processor = new ExampleService.Processor ( handler); TNonblockingServerTransport trans = new TNonblockingServerSocket(9090); TThreadedSelectorServer.Args args = new TThreadedSelectorServer.Args(trans); args.transportFactory(new TFramedTransport.Factory()); args.protocolFactory(new TBinaryProtocol.Factory()); args.processor(processor); args.selectorThreads(4); args.workerThreads(32); server = new TThreadedSelectorServer(args); new Thread(this).start(); while(!server.isServing()) {Thread.sleep(1); }; System.out.println("Serving on port 9090 ..."); } public void run() { server.serve(); } }
package pl.intelliseq.largedata.thrift; import static org.junit.Assert.*; import org.apache.thrift.TException; import org.apache.thrift.protocol.TBinaryProtocol; import org.apache.thrift.protocol.TProtocol; import org.apache.thrift.transport.TFramedTransport; import org.apache.thrift.transport.TSocket; import org.apache.thrift.transport.TTransport; import org.apache.thrift.transport.TTransportException; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration({"file:conf/thrift-conf.xml"}) public class ThriftServerTest { TTransport transport; ExampleService.Client client; @Before public void init() throws TTransportException { transport = new TFramedTransport(new TSocket("localhost", 9090)); transport.open(); TProtocol protocol = new TBinaryProtocol(transport); client = new ExampleService.Client(protocol); } @After public void destroy() { transport.close(); } @Rule public ExpectedException exception = ExpectedException.none(); @Test public void isAliveTest() throws TException { assertTrue(client.isAlive()); } @Test public void getHelloTest() throws TException { User user = new User(); user.setFirstname("John"); user.setLastname("Doe"); assertEquals(client.getHello(user).getMessage(), "Hello John Doe"); } @Test public void getErrorTest() throws TException { exception.expect(InvalidFirst.class); Wrong wrong = Wrong.FIRST; client.getError(wrong); } @Test public void getErrorSecondTest() throws TException { Wrong wrong = Wrong.SECOND; client.getError(wrong); } }That's it. Works like a charm. You can clone working project here: https://github.com/marpiech/largedatablog.git -b thrift thrift-and-java
Problems:
How to get rid of ugly warnings caused by autogenerated thrift code? (in Eclipse)
Create new source directory: e.g. src/thrift. Copy autogenerated code to the new directory. Then, Right click on the directory -> Build Path -> Use as Source Folder. Right click again -> Build Path -> Configure Build Path... -> Ignore optional compile problems -> Toggle.
org.apache.thrift.TApplicationException: [your method] failed: unknown result
your server implementation returned null or see: http://stackoverflow.com/questions/4244350/how-thread-safe-is-thrift-re-i-seem-to-have-requests-disrupting-one-another
TNonblockingServer.java [line number] Read an invalid frame size of [integer number]. Are you using TFramedTransport on the client side?
Wrap your TSocket into TFramedTransport (i.e. new TFramedTransport(new TSocket("localhost", 9090)))