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.Processor 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();
}
}
And finally, we will write our test:
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)))
evguser:Desktop evguser$ git clone https://github.com/marpiech/largedatablog.git -b thrift thrift-and-java
OdpowiedzUsuńCloning into 'thrift-and-java'...
remote: Repository not found.
fatal: repository 'https://github.com/marpiech/largedatablog.git/' not found
evgmoskalenko:Desktop evguser$