All Apache Thrift tutorials require that you have:
thrift -r --gen haxe tutorial.thrift
For this tutorial, we put both the server and the client main code into one single program. Depending on the arguments passed, it runs as a server or as a client program.
package;
import org.apache.thrift.*;
import org.apache.thrift.protocol.*;
import org.apache.thrift.transport.*;
import org.apache.thrift.server.*;
import org.apache.thrift.meta_data.*;
import tutorial.*;
import shared.*;
enum Prot {
binary;
json;
compact;
}
enum Trns {
socket;
http;
}
class Main {
private static var server : Bool = false;
private static var framed : Bool = false;
private static var buffered : Bool = false;
private static var prot : Prot = binary;
private static var trns : Trns = socket;
private static var targetHost : String = "localhost";
private static var targetPort : Int = 9090;
static function main() {
#if ! (flash || js || phpwebserver)
try {
ParseArgs();
} catch (e : String) {
trace(e);
trace(GetHelp());
return;
}
#elseif phpwebserver
//forcing server
server = true;
trns = http;
initPhpWebServer();
//check method
if(php.Web.getMethod() != 'POST') {
Sys.println('http endpoint for thrift test server');
return;
}
#end
try {
if (server)
RunServer();
else
RunClient();
} catch (e : String) {
trace(e);
}
trace("Completed.");
}
#if phpwebserver
private static function initPhpWebServer()
{
//remap trace to error log
haxe.Log.trace = function(v:Dynamic, ?infos:haxe.PosInfos)
{
// handle trace
var newValue : Dynamic;
if (infos != null && infos.customParams!=null) {
var extra:String = "";
for( v in infos.customParams )
extra += "," + v;
newValue = v + extra;
}
else {
newValue = v;
}
var msg = infos != null ? infos.fileName + ':' + infos.lineNumber + ': ' : '';
Sys.stderr().writeString('${msg}${newValue}\n');
}
}
#end
#if ! (flash || js)
private static function GetHelp() : String {
return Sys.programPath+" modus layered transport protocol\n"
+"Options:\n"
+" modus: client, server (default: client)\n"
+" layered: framed, buffered (default: none)\n"
+" transport: socket, http (default: socket)\n"
+" protocol: binary, json, compact (default: binary)\n"
+"\n"
+"All arguments are optional.\n";
}
private static function ParseArgs() : Void {
var step = 0;
for (arg in Sys.args()) {
// server|client
switch(step) {
case 0:
++step;
if ( arg == "client")
server = false;
else if ( arg == "server")
server = true;
else
throw "First argument must be 'server' or 'client'";
case 1:
if ( arg == "framed") {
framed = true;
} else if ( arg == "buffered") {
buffered = true;
} else if ( arg == "socket") {
trns = socket;
++step;
} else if ( arg == "http") {
trns = http;
++step;
} else {
throw "Unknown transport "+arg;
}
case 2:
if ( arg == "binary") {
prot = binary;
++step;
} else if ( arg == "json") {
prot = json;
++step;
} else if ( arg == "compact") {
prot = compact;
++step;
} else {
throw "Unknown protocol "+arg;
}
default:
throw "Unexpected argument "+arg;
}
if ( framed && buffered)
{
trace("WN: framed supersedes buffered");
}
}
}
#end
private static function ClientSetup() : Calculator {
trace("Client configuration:");
// endpoint transport
var transport : TTransport;
switch(trns)
{
case socket:
trace('- socket transport $targetHost:$targetPort');
transport = new TSocket( targetHost, targetPort);
case http:
var uri = 'http://${targetHost}:${targetPort}';
trace('- HTTP transport $uri');
transport = new THttpClient(uri);
default:
throw "Unhandled transport";
}
// optinal layered transport
if ( framed) {
trace("- framed transport");
transport = new TFramedTransport(transport);
} else if ( buffered) {
trace("- buffered transport");
transport = new TBufferedTransport(transport);
}
// protocol
var protocol : TProtocol;
switch(prot)
{
case binary:
trace("- binary protocol");
protocol = new TBinaryProtocol( transport);
case json:
trace("- JSON protocol");
protocol = new TJSONProtocol( transport);
case compact:
trace("- compact protocol");
protocol = new TCompactProtocol( transport);
default:
throw "Unhandled protocol";
}
// put everything together
transport.open();
return new CalculatorImpl(protocol,protocol);
}
private static function RunClient() : Void {
var client = ClientSetup();
try {
client.ping();
trace("ping() successful");
} catch(error : TException) {
trace('ping() failed: $error');
} catch(error : Dynamic) {
trace('ping() failed: $error');
}
try {
var sum = client.add( 1, 1);
trace('1+1=$sum');
} catch(error : TException) {
trace('add() failed: $error');
} catch(error : Dynamic) {
trace('add() failed: $error');
}
var work = new tutorial.Work();
work.op = tutorial.Operation.DIVIDE;
work.num1 = 1;
work.num2 = 0;
try {
var quotient = client.calculate( 1, work);
trace('Whoa we can divide by 0! Result = $quotient');
} catch(error : TException) {
trace('calculate() failed: $error');
} catch(error : Dynamic) {
trace('calculate() failed: $error');
}
work.op = tutorial.Operation.SUBTRACT;
work.num1 = 15;
work.num2 = 10;
try {
var diff = client.calculate( 1, work);
trace('15-10=$diff');
} catch(error : TException) {
trace('calculate() failed: $error');
} catch(error : Dynamic) {
trace('calculate() failed: $error');
}
try {
var log : SharedStruct = client.getStruct( 1);
var logval = log.value;
trace('Check log: $logval');
} catch(error : TException) {
trace('getStruct() failed: $error');
} catch(error : Dynamic) {
trace('getStruct() failed: $error');
}
}
private static function ServerSetup() : TServer {
trace("Server configuration:");
// endpoint transport
var transport : TServerTransport = null;
switch(trns)
{
case socket:
#if (flash || js)
throw 'current platform does not support socket servers';
#else
trace('- socket transport port $targetPort');
transport = new TServerSocket( targetPort);
#end
case http:
#if !phpwebserver
throw "HTTP server not implemented yet";
//trace("- http transport");
//transport = new THttpClient( targetHost);
#else
trace("- http transport");
transport = new TWrappingServerTransport(
new TStreamTransport(
new TFileStream("php://input", Read),
new TFileStream("php://output", Append),
null
)
);
#end
default:
throw "Unhandled transport";
}
// optional: layered transport
Were done with the client, but need some more for the server: A service handler implementaion.
As the name suggests, the service handler implements the Thrift service on the server side. The code to achieve this is as follows:
package;
import haxe.ds.IntMap;
import org.apache.thrift.*;
import org.apache.thrift.protocol.*;
import org.apache.thrift.transport.*;
import org.apache.thrift.server.*;
import org.apache.thrift.meta_data.*;
import tutorial.*;
import shared.*;
class CalculatorHandler implements Calculator_service {
private var log = new IntMap<SharedStruct>();
public function new() {
}
public function ping() : Void {
trace("ping()");
}
public function add( num1 : haxe.Int32, num2 : haxe.Int32) : haxe.Int32 {
trace('add( $num1, $num2)');
return num1 + num2;
}
public function calculate( logid : haxe.Int32, work : Work) : haxe.Int32 {
trace('calculate( $logid, '+work.op+","+work.num1+","+work.num2+")");
var val : haxe.Int32 = 0;
switch (work.op)
{
case Operation.ADD:
val = work.num1 + work.num2;
case Operation.SUBTRACT:
val = work.num1 - work.num2;
case Operation.MULTIPLY:
val = work.num1 * work.num2;
case Operation.DIVIDE:
if (work.num2 == 0)
{
var io = new InvalidOperation();
io.whatOp = work.op;
io.why = "Cannot divide by 0";
throw io;
}
val = Std.int( work.num1 / work.num2);
default:
var io = new InvalidOperation();
io.whatOp = work.op;
io.why = "Unknown operation";
throw io;
}
var entry = new SharedStruct();
entry.key = logid;
entry.value = '$val';
log.set(logid, entry);
return val;
}
public function getStruct( key : haxe.Int32) : SharedStruct {
trace('getStruct($key)');
return log.get(key);
}
// oneway method, no args
public function zip() : Void {
trace("zip()");
}
}
Similar to Thrift, Haxe supports different compiler targets. Depending on the target, some features may or may not be supported. For example, if you plan to use Flash or JavaScript targets, there is currently no way to pass command line arguments to the program.