Differences between revisions 8 and 9
Revision 8 as of 2012-04-13 07:41:12
Size: 15158
Editor: DJDaveMark
Comment: Woops, didn't see the previous ".currentThread()" changes
Revision 9 as of 2012-06-18 08:18:32
Size: 15157
Comment: Corrected parser error (a stray ";" ) in example.thrift
Deletions are marked like this. Additions are marked like this.
Line 36: Line 36:
 7: string stringObject; ;  7: string stringObject;

If you haven't downloaded thrift yet: http://thrift.apache.org/download/

  1. Download thrift-version.tar.gz and extract it somewhere, eg. "thrift-version" (from here referred to as $thrift-version).

  2. For windows, download thrift-version.exe. and copy-paste it to C:\Windows then rename it thrift.exe.

  3. For Linux: ThriftInstallation

  4. At the command prompt, go to thrift-version/lib/java and execute ant to compile the source code into the generated build folder.

Eclipse Editor

  1. Help --> Software Updates --> Add Site...

  2. URL: http://thrift4eclipse.sourceforge.net/updatesite/

  3. Tick: Thrift4Eclipse

  4. Click Install

NOTE: It may take some time to install (5mins)

Standalone Example

  1. In Eclipse: File --> New --> Other... (Ctrl+N) --> Dynamic Web Project...

  2. Project name: ThriftExample

  3. In $thrift-version/lib/java/build copy libthrift-version.jar to ThriftExample/WebContent/WEB-INF/lib

  4. In $thrift-version/lib/java/build/lib copy all the jars into ThriftExample/WebContent/WEB-INF/lib

  5. In Eclipse, under ThriftExample/Java Resources/src create the file example.thrift with the following content:

example.thrift

namespace java example

struct BeanExample {
        1: bool booleanPrimive;
        2: byte bytePrimive;
        3: i16 shortPrimive;
        4: i32 intPrimive;
        5: i64 longPrimive;
        6: double doublePrimive;
        7: string stringObject; 
        8: binary byteArray; //ByteBuffer
}

service ServiceExample {
  BeanExample getBean(1: i32 anArg; 2: string anOther)
}

At the command prompt, got to folder containing example.thrift (eg. C:\workspace\ThriftExample\src) then execute the following command:

thrift --gen java -out . example.thrift
  1. In Eclipse "refresh" the ThriftExample project to show the newly created package example below src.

  2. In the package example create the following files:

ServiceExampleImpl.java

   1 package example;
   2 
   3 import java.nio.ByteBuffer;
   4 import org.apache.thrift.TException;
   5 
   6 public class ServiceExampleImpl implements ServiceExample.Iface {
   7         @Override
   8         public BeanExample getBean(int anArg, String anOther) throws TException {
   9                 return new BeanExample(true, (byte) 2, (short) 3, 4, 5, 6.0,
  10                     "OK", ByteBuffer.wrap(new byte[] { 3, 1, 4 }));
  11         }
  12 }

ClientExample.java

   1 package example;
   2 
   3 // import org.apache.thrift... etc.;
   4 
   5 public class ClientExample {
   6         private static final int PORT = 7911;
   7 
   8         public static void main(String[] args) {
   9                 try {
  10                         TTransport transport = new TSocket("localhost", PORT);
  11                         Protocol protocol = new TBinaryProtocol(transport);
  12                         ServiceExample.Client client = new ServiceExample.Client(protocol);
  13                         transport.open();
  14                         BeanExample bean = client.getBean(1, "string");
  15                         transport.close();
  16                         System.out.println(bean.getStringObject());
  17                 } catch (TTransportException e) {
  18                         e.printStackTrace();
  19                 } catch (TException e) {
  20                         e.printStackTrace();
  21                 }
  22         }
  23 }

ServerExample.java

   1 package example;
   2 
   3 // import org.apache.thrift... etc.;
   4 
   5 public class ServerExample implements Runnable {
   6         private static final int PORT = 7911;
   7 
   8         @Override
   9         public void run() {
  10                 try {
  11                         TServerSocket serverTransport = new TServerSocket(PORT);
  12                         ServiceExample.Processor processor = new ServiceExample.Processor(new ServiceExampleImpl());
  13                         TServer server = new TThreadPoolServer(new TThreadPoolServer.Args(serverTransport).processor(processor));
  14                         System.out.println("Starting server on port " + PORT);
  15                         server.serve();
  16                 } catch (TTransportException e) {
  17                         e.printStackTrace();
  18                 }
  19         }
  20 
  21         public static void main(String[] args) {
  22                 new Thread(new ServerExample()).run();
  23         }
  24 }
  1. Execute the server (right-click ServerExample.java --> Run as --> Java Application) to see the message: Starting server on port 7911

  2. Execute the client (right-click ClientExample.java --> Run as --> Java Application) to see the message: OK

NOTE: To stop the server you'll need to kill the process via the console.

Unit Test

  1. In Eclipse right-click the ThriftExample Project --> New --> Source Folder --> Folder name: test

  2. In test create the package example then in there create the following file:

TestExample.java

   1 package example;
   2 
   3 // imports... see last example for correct imports
   4 
   5 public class TestExample {
   6         private static final int PORT = 7911;
   7 
   8         @BeforeClass
   9         @SuppressWarnings({ "static-access" })
  10         public static void startServer() throws URISyntaxException, IOException {
  11                 // Start thrift server in a seperate thread
  12                 new Thread(new ServerExample()).start();
  13                 try {
  14                         // wait for the server start up
  15                         Thread.sleep(100);
  16                 } catch (InterruptedException e) {
  17                         e.printStackTrace();
  18                 }
  19         }
  20 
  21         @Test
  22         public void testExample() throws TTransportException, TException {
  23                 TTransport transport = new TSocket("localhost", PORT);
  24                 TProtocol protocol = new TBinaryProtocol(transport);
  25                 ServiceExample.Client client = new ServiceExample.Client(protocol);
  26                 transport.open();
  27                 BeanExample bean = client.getBean(1, "string");
  28                 transport.close();
  29                 Assert.assertEquals("OK", bean.getStringObject());
  30         }
  31 }

Run the test by right-clicking the file --> Run as --> JUnit Test

Thrift Servlet

  1. In Eclipse: File --> New --> Servlet:

  2. Project: ThriftExample

  3. Java package: example

  4. Class name: TServletExample

Note: This step modifies the web.xml automatically adding :

<servlet>
    <description></description>
    <display-name>TServletExample</display-name>
    <servlet-name>TServletExample</servlet-name>
    <servlet-class>example.TServletExample</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>TServletExample</servlet-name>
    <url-pattern>/TServletExample</url-pattern>
</servlet-mapping>


Replace the generated code with the following:

TServletExample.java

   1 package example;
   2 
   3 import org.apache.thrift.protocol.TCompactProtocol;
   4 import org.apache.thrift.server.TServlet;
   5 
   6 public class TServletExample extends TServlet {
   7         public TServletExample() {
   8                 super(
   9                         new ServiceExample.Processor(
  10                                 new ServiceExampleImpl()),
  11                                 new TCompactProtocol.Factory()
  12                 );
  13         }
  14 }
  1. Right-click the TServletExample.java --> Run as --> Run on server (hopefully you've already got a server configured ;o)

  2. You should get the following error (don't worry this is good!):

Error 500

javax.servlet.ServletException: org.apache.thrift.transport.TTransportException
        org.apache.thrift.server.TServlet.doPost(TServlet.java:86)
        org.apache.thrift.server.TServlet.doGet(TServlet.java:96)
        javax.servlet.http.HttpServlet.service(HttpServlet.java:617)
        javax.servlet.http.HttpServlet.service(HttpServlet.java:717)

This is because we haven't created an HTTP client to communicate with the TServlet.
Note the URL if it's not the same as this one: http://localhost:8080/ThriftExample/TServletExample
You'll have to replace it in the following code if it's different:

TServlet Unit Test

Add the following method (replacing the URL if needs be):

   1 @Test
   2 public void testServlet() throws TTransportException, TException {
   3         // TODO : change this URL if it's not the right one ;o)
   4         // TODO : change this URL if it's not the right one ;o)
   5         // TODO : change this URL if it's not the right one ;o)
   6         String servletUrl = "http://localhost:8080/ThriftExample/TServletExample";
   7 
   8         THttpClient thc = new THttpClient(servletUrl);
   9         TProtocol loPFactory = new TCompactProtocol(thc);
  10         ServiceExample.Client client = new ServiceExample.Client(loPFactory);
  11 
  12         BeanExample bean = client.getBean(1, "string");
  13         Assert.assertEquals("OK", bean.getStringObject());
  14 }

Now run the unit tests again to test the TServlet: Right-click TestExample.java --> Run as --> JUnit Test.

Secure Thrift Servlet

Securing a servlet is done via the web server. It's up to you to configure your web server to control the communication securely (via https rather than http). How to set that up is outside the scope of this tutorial. Please refer to your web server's documentation.

Also outside the scope of this tutorial is creating the security certificates and setting-up the Java keystore.

However, once you've you've got all that stuff in place. You're ready to unit test your secure* Thrift servlet (which hasn't changed!).

Secure Unit Test

Security Warning

You won't want to use the AnyCertTrustManager in a production environment, but it's sometimes necessary if the test server's domain differs from the one specified on the certificat issued. Trust stores and keystores are practically the same thing.

Here's the whole unit test :

   1 package example;
   2 
   3 import java.io.File;
   4 import java.io.FileInputStream;
   5 import java.security.KeyStore;
   6 import java.security.cert.CertificateException;
   7 import java.security.cert.X509Certificate;
   8 
   9 import javax.net.ssl.KeyManagerFactory;
  10 import javax.net.ssl.SSLContext;
  11 import javax.net.ssl.TrustManager;
  12 import javax.net.ssl.X509TrustManager;
  13 
  14 import junit.framework.Assert;
  15 
  16 import org.apache.http.HttpVersion;
  17 import org.apache.http.conn.params.ConnManagerParams;
  18 import org.apache.http.conn.params.ConnPerRouteBean;
  19 import org.apache.http.conn.scheme.Scheme;
  20 import org.apache.http.conn.scheme.SchemeRegistry;
  21 import org.apache.http.conn.ssl.SSLSocketFactory;
  22 import org.apache.http.impl.client.DefaultHttpClient;
  23 import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
  24 import org.apache.http.params.BasicHttpParams;
  25 import org.apache.http.params.HttpConnectionParams;
  26 import org.apache.thrift.TException;
  27 import org.apache.thrift.protocol.TBinaryProtocol;
  28 import org.apache.thrift.protocol.TCompactProtocol;
  29 import org.apache.thrift.protocol.TProtocol;
  30 import org.apache.thrift.transport.THttpClient;
  31 import org.apache.thrift.transport.TSocket;
  32 import org.apache.thrift.transport.TTransport;
  33 import org.junit.BeforeClass;
  34 import org.junit.Test;
  35 
  36 public class TestExample {
  37 
  38         private static final int PORT = 7911;
  39         private static final int PORT_SSL = 443;
  40         private static Scheme https;
  41         private static BasicHttpParams params;
  42         private static ThreadSafeClientConnManager cm;
  43 
  44         @BeforeClass
  45         @SuppressWarnings({ "static-access" })
  46         public static void startServer() throws Exception {
  47                 // Start thrift server in a seperate thread
  48                 new Thread(new ServerExample()).start();
  49                 try {
  50                         // wait for the server start up
  51                         Thread.sleep(100);
  52                 } catch (InterruptedException e) {
  53                         e.printStackTrace();
  54                 }
  55         }
  56 
  57         @BeforeClass
  58         @SuppressWarnings("deprecation")
  59         public static void initConnectionManager() throws Exception {
  60                 // TODO : specify where your keystore is
  61                 File keystoreFile = new File(" ... /keystore.jks");
  62 
  63                 // TODO : specify your password to the key pair (not the keystore)
  64                 char[] password = "yourPassword".toCharArray();
  65                 String keystoreType = KeyStore.getDefaultType(); // "jks"
  66 
  67                 KeyStore keyStore = KeyStore.getInstance(keystoreType);
  68                 FileInputStream instream = new FileInputStream(keystoreFile);
  69 
  70                 try {
  71                         keyStore.load(instream, password);
  72                 } finally {
  73                         try {
  74                                 instream.close();
  75                         } catch (Exception ignore) {
  76                         }
  77                 }
  78 
  79                 KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
  80                 kmf.init(keyStore, password);
  81 
  82                 // TODO : Specify un proper trust store. Don't use this in production!
  83                 SSLContext sslContext = SSLContext.getInstance("TLS");
  84                 sslContext.init(kmf.getKeyManagers(), new TrustManager[] { new AnyCertTrustManager() }, null);
  85 
  86                 SSLSocketFactory ssf = new SSLSocketFactory(sslContext, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
  87                 https = new Scheme("https", PORT_SSL, ssf);
  88 
  89                 SchemeRegistry schemeRegistry = new SchemeRegistry();
  90                 schemeRegistry.register(https);
  91 
  92                 // Set up Thrift HTTP client connection parameters
  93                 params = new BasicHttpParams();
  94                 params.setParameter("http.protocol.version", HttpVersion.HTTP_1_1);
  95                 params.setParameter("http.protocol.content-charset", "UTF-8");
  96                 // Disable Expect-Continue
  97                 params.setParameter("http.protocol.expect-continue", false);
  98                 // Enable staleness check
  99                 params.setParameter("http.connection.stalecheck", true);
 100                 HttpConnectionParams.setSoTimeout(params, 10000); // 10 secondes
 101                 HttpConnectionParams.setConnectionTimeout(params, 10000); // 10 secondes
 102                 ConnManagerParams.setMaxTotalConnections(params, 20);
 103                 ConnPerRouteBean connPerRoute = new ConnPerRouteBean(20);
 104                 ConnManagerParams.setMaxConnectionsPerRoute(params, connPerRoute);
 105 
 106                 cm = new ThreadSafeClientConnManager(params, schemeRegistry);
 107         }
 108 
 109         private static class AnyCertTrustManager implements X509TrustManager {
 110                 @Override
 111                 public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
 112                 }
 113 
 114                 @Override
 115                 public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
 116                 }
 117 
 118                 @Override
 119                 public X509Certificate[] getAcceptedIssuers() {
 120                         return null;
 121                 }
 122         }
 123 
 124         @Test
 125         public void testExample() throws TException {
 126                 TTransport transport = new TSocket("localhost", PORT);
 127                 TProtocol protocol = new TBinaryProtocol(transport);
 128                 ServiceExample.Client client = new ServiceExample.Client(protocol);
 129                 transport.open();
 130                 BeanExample bean = client.getBean(1, "string");
 131                 transport.close();
 132                 Assert.assertEquals("OK", bean.getStringObject());
 133         }
 134 
 135         @Test
 136         public void testServlet() throws TException {
 137                 // TODO : change this URL if it's not the right one ;o)
 138                 // TODO : change this URL if it's not the right one ;o)
 139                 // TODO : change this URL if it's not the right one ;o)
 140                 String servletUrl = "http://localhost:8080/ThriftExample/TServletExample";
 141 
 142                 THttpClient thc = new THttpClient(servletUrl);
 143                 TProtocol loPFactory = new TCompactProtocol(thc);
 144                 ServiceExample.Client client = new ServiceExample.Client(loPFactory);
 145 
 146                 BeanExample bean = client.getBean(1, "string");
 147                 Assert.assertEquals("OK", bean.getStringObject());
 148         }
 149 
 150         @Test
 151         public void testSecureServlet() throws TException {
 152                 // TODO : change this URL if it's not the right one ;o)
 153                 // TODO : change this URL if it's not the right one ;o)
 154                 // TODO : change this URL if it's not the right one ;o)
 155                 String servletUrl = "https://localhost:8080/ThriftExample/TServletExample";
 156 
 157                 THttpClient thc = new THttpClient(servletUrl, new DefaultHttpClient(cm, params));
 158                 TProtocol loPFactory = new TCompactProtocol(thc);
 159                 ServiceExample.Client client = new ServiceExample.Client(loPFactory);
 160 
 161                 BeanExample bean = client.getBean(1, "string");
 162                 Assert.assertEquals("OK", bean.getStringObject());
 163         }
 164 }

Thrift & Eclipse & JUnit with TServlet (last edited 2012-06-18 08:18:32 by SaurabhAgarwal)