summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Reiss <dreiss@apache.org>2009-01-06 19:49:22 +0000
committerDavid Reiss <dreiss@apache.org>2009-01-06 19:49:22 +0000
commit6319133cacb49c815aa1783c129aa427c762b2fd (patch)
treed6af7dd2eee92f98de31d7671eac808eafafdbed
parentd6b7182df59ae7cb1849c36b5d9513fce681ce35 (diff)
downloadthrift-6319133cacb49c815aa1783c129aa427c762b2fd.tar.gz
THRIFT-25. csharp: Various compiler and library improvements
Compiler: - Thrift structures are serializable. - The member fields of thrift structures are now private and only accessible through Properties, which keep the appropriate __isset up to date. Library - Addition of TBufferedTransport, which can be used to wrap other Transports. - Addition of TThreadedServer, which manually manages threads instead of relying on .NET ThreadPool. - Servers use a log delegate that defaults to System.Console but allows servers to use log4net without introducing the dependency. ThriftTest Visual Studio Project - Test client and server that use ThriftTest.thrift. The project references thrift.exe and Thrift.dll from the subversion tree and automatically builds generated code. This makes it very easy to test changes in both the compiler and library. git-svn-id: https://svn.apache.org/repos/asf/incubator/thrift/trunk@732079 13f79535-47bb-0310-9956-ffa450edef68
-rw-r--r--compiler/cpp/src/generate/t_csharp_generator.cc87
-rw-r--r--lib/csharp/ThriftMSBuildTask/Properties/AssemblyInfo.cs2
-rw-r--r--lib/csharp/ThriftMSBuildTask/ThriftBuild.cs2
-rw-r--r--lib/csharp/src/Server/TServer.cs27
-rw-r--r--lib/csharp/src/Server/TSimpleServer.cs29
-rw-r--r--lib/csharp/src/Server/TThreadPoolServer.cs49
-rw-r--r--lib/csharp/src/Server/TThreadedServer.cs226
-rw-r--r--lib/csharp/src/Thrift.csproj2
-rw-r--r--lib/csharp/src/Transport/TBufferedTransport.cs92
-rw-r--r--lib/csharp/src/Transport/TServerSocket.cs25
-rw-r--r--lib/csharp/src/Transport/TSocket.cs16
-rw-r--r--lib/csharp/src/Transport/TStreamTransport.cs10
-rw-r--r--test/csharp/ThriftTest/Program.cs44
-rw-r--r--test/csharp/ThriftTest/Properties/AssemblyInfo.cs36
-rw-r--r--test/csharp/ThriftTest/TestClient.cs407
-rw-r--r--test/csharp/ThriftTest/TestServer.cs327
-rw-r--r--test/csharp/ThriftTest/ThriftTest.csproj122
17 files changed, 1448 insertions, 55 deletions
diff --git a/compiler/cpp/src/generate/t_csharp_generator.cc b/compiler/cpp/src/generate/t_csharp_generator.cc
index ec5d90b77..d592a7cc9 100644
--- a/compiler/cpp/src/generate/t_csharp_generator.cc
+++ b/compiler/cpp/src/generate/t_csharp_generator.cc
@@ -38,6 +38,7 @@ class t_csharp_generator : public t_oop_generator
void generate_struct (t_struct* tstruct);
void generate_xception (t_struct* txception);
void generate_service (t_service* tservice);
+ void generate_property(ofstream& out, t_field* tfield, bool isPublic);
bool print_const_value (std::ofstream& out, std::string name, t_type* type, t_const_value* value, bool in_static, bool defval=false, bool needtype=false);
std::string render_const_value(std::ofstream& out, std::string name, t_type* type, t_const_value* value);
void print_const_constructor(std::ofstream& out, std::vector<t_const*> consts);
@@ -82,6 +83,7 @@ class t_csharp_generator : public t_oop_generator
std::string function_signature(t_function* tfunction, std::string prefix="");
std::string argument_list(t_struct* tstruct);
std::string type_to_enum(t_type* ttype);
+ std::string prop_name(t_field* tfield);
bool type_can_be_null(t_type* ttype) {
while (ttype->is_typedef()) {
@@ -142,6 +144,7 @@ string t_csharp_generator::csharp_type_usings() {
"using System.Collections;\n" +
"using System.Collections.Generic;\n" +
"using System.Text;\n" +
+ "using System.IO;\n" +
"using Thrift;\n";
}
@@ -290,7 +293,6 @@ void t_csharp_generator::print_const_constructor(std::ofstream& out, std::vector
//it seems like all that methods that call this are using in_static to be the opposite of what it would imply
bool t_csharp_generator::print_const_value(std::ofstream& out, string name, t_type* type, t_const_value* value, bool in_static, bool defval, bool needtype) {
- type = get_true_type(type);
indent(out);
bool need_static_construction = !in_static;
if (!defval || needtype) {
@@ -311,8 +313,6 @@ bool t_csharp_generator::print_const_value(std::ofstream& out, string name, t_ty
out << name << " = new " << type_name(type, true, true) << "();" << endl;
} else if (type->is_list() || type->is_set()) {
out << name << " = new " << type_name(type) << "();" << endl;
- } else {
- throw "compiler error: no const of type " + type->get_name();
}
if (defval && !type->is_base_type() && !type->is_enum()) {
@@ -392,8 +392,9 @@ void t_csharp_generator::generate_csharp_struct_definition(ofstream &out, t_stru
start_csharp_namespace(out);
}
- indent(out) <<
- "public class " << tstruct->get_name() << " ";
+ out << endl;
+ indent(out) << "[Serializable]" << endl;
+ indent(out) << "public class " << tstruct->get_name() << " ";
if (is_exception) {
out << ": Exception ";
@@ -406,15 +407,22 @@ void t_csharp_generator::generate_csharp_struct_definition(ofstream &out, t_stru
const vector<t_field*>& members = tstruct->get_members();
vector<t_field*>::const_iterator m_iter;
+ //make private members with public Properties
for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
indent(out) <<
- "public " << declare_field(*m_iter, false) << endl;
+ "private " << declare_field(*m_iter, false) << endl;
+ }
+ out << endl;
+
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ generate_property(out, *m_iter, true);
}
if (members.size() > 0) {
out <<
endl <<
indent() << "public Isset __isset;" << endl <<
+ indent() << "[Serializable]" << endl <<
indent() << "public struct Isset {" << endl;
indent_up();
for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
@@ -554,7 +562,13 @@ void t_csharp_generator::generate_csharp_struct_writer(ofstream& out, t_struct*
bool null_allowed = type_can_be_null((*f_iter)->get_type());
if (null_allowed) {
indent(out) <<
- "if (this." << (*f_iter)->get_name() << " != null) {" << endl;
+ "if (this." << (*f_iter)->get_name() << " != null && __isset." << (*f_iter)->get_name() << ") {" << endl;
+ indent_up();
+ }
+ else
+ {
+ indent(out) <<
+ "if (__isset." << (*f_iter)->get_name() << ") {" << endl;
indent_up();
}
@@ -572,10 +586,8 @@ void t_csharp_generator::generate_csharp_struct_writer(ofstream& out, t_struct*
indent(out) <<
"oprot.WriteFieldEnd();" << endl;
- if (null_allowed) {
- indent_down();
- indent(out) << "}" << endl;
- }
+ indent_down();
+ indent(out) << "}" << endl;
}
}
@@ -871,7 +883,7 @@ void t_csharp_generator::generate_service_client(t_service* tservice) {
for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) {
f_service_ <<
- indent() << "args." << (*fld_iter)->get_name() << " = " << (*fld_iter)->get_name() << ";" << endl;
+ indent() << "args." << prop_name(*fld_iter) << " = " << (*fld_iter)->get_name() << ";" << endl;
}
f_service_ <<
@@ -912,7 +924,7 @@ void t_csharp_generator::generate_service_client(t_service* tservice) {
if (!(*f_iter)->get_returntype()->is_void()) {
f_service_ <<
indent() << "if (result.__isset.success) {" << endl <<
- indent() << " return result.success;" << endl <<
+ indent() << " return result.Success;" << endl <<
indent() << "}" << endl;
}
@@ -923,7 +935,7 @@ void t_csharp_generator::generate_service_client(t_service* tservice) {
for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
f_service_ <<
indent() << "if (result.__isset." << (*x_iter)->get_name() << ") {" << endl <<
- indent() << " throw result." << (*x_iter)->get_name() << ";" << endl <<
+ indent() << " throw result." << prop_name(*x_iter) << ";" << endl <<
indent() << "}" << endl;
}
@@ -1004,6 +1016,9 @@ void t_csharp_generator::generate_service_server(t_service* tservice) {
}
scope_up(f_service_);
+ f_service_ << indent() << "try" << endl;
+ scope_up(f_service_);
+
f_service_ <<
indent() << "TMessage msg = iprot.ReadMessageBegin();" << endl;
@@ -1021,6 +1036,15 @@ void t_csharp_generator::generate_service_server(t_service* tservice) {
indent() << "}" << endl <<
indent() << "fn(msg.SeqID, iprot, oprot);" << endl;
+ scope_down(f_service_);
+
+ f_service_ <<
+ indent() << "catch (IOException)" << endl;
+ scope_up(f_service_);
+ f_service_ <<
+ indent() << "return false;" << endl;
+ scope_down(f_service_);
+
f_service_ <<
indent() << "return true;" << endl;
@@ -1092,7 +1116,7 @@ void t_csharp_generator::generate_process_function(t_service* tservice, t_functi
f_service_ << indent();
if (!tfunction->is_async() && !tfunction->get_returntype()->is_void()) {
- f_service_ << "result.success = ";
+ f_service_ << "result.Success = ";
}
f_service_ <<
"iface_." << tfunction->get_name() << "(";
@@ -1103,15 +1127,10 @@ void t_csharp_generator::generate_process_function(t_service* tservice, t_functi
} else {
f_service_ << ", ";
}
- f_service_ << "args." << (*f_iter)->get_name();
+ f_service_ << "args." << prop_name(*f_iter);
}
f_service_ << ");" << endl;
- if (!tfunction->is_async() && !tfunction->get_returntype()->is_void()) {
- f_service_ <<
- indent() << "result.__isset.success = true;" << endl;
- }
-
if (!tfunction->is_async() && xceptions.size() > 0) {
indent_down();
f_service_ << indent() << "}";
@@ -1120,8 +1139,7 @@ void t_csharp_generator::generate_process_function(t_service* tservice, t_functi
if (!tfunction->is_async()) {
indent_up();
f_service_ <<
- indent() << "result." << (*x_iter)->get_name() << " = " << (*x_iter)->get_name() << ";" << endl <<
- indent() << "result.__isset." << (*x_iter)->get_name() << " = true;" << endl;
+ indent() << "result." << prop_name(*x_iter) << " = " << (*x_iter)->get_name() << ";" << endl;
indent_down();
f_service_ << indent() << "}";
} else {
@@ -1477,6 +1495,29 @@ void t_csharp_generator::generate_serialize_list_element(ofstream& out, t_list*
generate_serialize_field(out, &efield, "");
}
+void t_csharp_generator::generate_property(ofstream& out, t_field* tfield, bool isPublic) {
+ indent(out) << (isPublic ? "public " : "private ") << type_name(tfield->get_type())
+ << " " << prop_name(tfield) << endl;
+ scope_up(out);
+ indent(out) << "get" << endl;
+ scope_up(out);
+ indent(out) << "return " << tfield->get_name() << ";" << endl;
+ scope_down(out);
+ indent(out) << "set" << endl;
+ scope_up(out);
+ indent(out) << "__isset." << tfield->get_name() << " = true;" << endl;
+ indent(out) << tfield->get_name() << " = value;" << endl;
+ scope_down(out);
+ scope_down(out);
+ out << endl;
+}
+
+std::string t_csharp_generator::prop_name(t_field* tfield) {
+ string name (tfield->get_name());
+ name[0] = toupper(name[0]);
+ return name;
+}
+
string t_csharp_generator::type_name(t_type* ttype, bool in_container, bool in_init) {
while (ttype->is_typedef()) {
ttype = ((t_typedef*)ttype)->get_type();
diff --git a/lib/csharp/ThriftMSBuildTask/Properties/AssemblyInfo.cs b/lib/csharp/ThriftMSBuildTask/Properties/AssemblyInfo.cs
index e91a156b2..69653b94c 100644
--- a/lib/csharp/ThriftMSBuildTask/Properties/AssemblyInfo.cs
+++ b/lib/csharp/ThriftMSBuildTask/Properties/AssemblyInfo.cs
@@ -10,7 +10,7 @@ using System.Runtime.InteropServices;
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("ThriftMSBuildTask")]
-[assembly: AssemblyCopyright("Copyright © 2007")]
+[assembly: AssemblyCopyright("Copyright © 2009 The Apache Software Foundation")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
diff --git a/lib/csharp/ThriftMSBuildTask/ThriftBuild.cs b/lib/csharp/ThriftMSBuildTask/ThriftBuild.cs
index 69ae8d3ee..85f12b87e 100644
--- a/lib/csharp/ThriftMSBuildTask/ThriftBuild.cs
+++ b/lib/csharp/ThriftMSBuildTask/ThriftBuild.cs
@@ -180,7 +180,7 @@ namespace ThriftMSBuildTask
LogMessage("Generating code for: " + thriftFile, MessageImportance.Normal);
Process p = new Process();
p.StartInfo.FileName = SafePath(ThriftExecutable.ItemSpec);
- p.StartInfo.Arguments = "-csharp -o " + SafePath(thriftDir) + " -r " + thriftFile;
+ p.StartInfo.Arguments = "--gen csharp -o " + SafePath(thriftDir) + " -r " + thriftFile;
p.StartInfo.UseShellExecute = false;
p.StartInfo.CreateNoWindow = true;
p.StartInfo.RedirectStandardOutput = false;
diff --git a/lib/csharp/src/Server/TServer.cs b/lib/csharp/src/Server/TServer.cs
index a9b2816a7..afb2b9f38 100644
--- a/lib/csharp/src/Server/TServer.cs
+++ b/lib/csharp/src/Server/TServer.cs
@@ -14,6 +14,7 @@ using System;
using System.Collections.Generic;
using Thrift.Protocol;
using Thrift.Transport;
+using System.IO;
namespace Thrift.Server
{
@@ -48,6 +49,8 @@ namespace Thrift.Server
* Output Protocol Factory
*/
protected TProtocolFactory outputProtocolFactory;
+ public delegate void LogDelegate(string str);
+ protected LogDelegate logDelegate;
/**
* Default constructors.
@@ -55,7 +58,14 @@ namespace Thrift.Server
public TServer(TProcessor processor,
TServerTransport serverTransport)
- :this(processor, serverTransport, new TTransportFactory(), new TTransportFactory(), new TBinaryProtocol.Factory(), new TBinaryProtocol.Factory())
+ :this(processor, serverTransport, new TTransportFactory(), new TTransportFactory(), new TBinaryProtocol.Factory(), new TBinaryProtocol.Factory(), DefaultLogDelegate)
+ {
+ }
+
+ public TServer(TProcessor processor,
+ TServerTransport serverTransport,
+ LogDelegate logDelegate)
+ : this(processor, serverTransport, new TTransportFactory(), new TTransportFactory(), new TBinaryProtocol.Factory(), new TBinaryProtocol.Factory(), DefaultLogDelegate)
{
}
@@ -67,7 +77,8 @@ namespace Thrift.Server
transportFactory,
transportFactory,
new TBinaryProtocol.Factory(),
- new TBinaryProtocol.Factory())
+ new TBinaryProtocol.Factory(),
+ DefaultLogDelegate)
{
}
@@ -80,7 +91,8 @@ namespace Thrift.Server
transportFactory,
transportFactory,
protocolFactory,
- protocolFactory)
+ protocolFactory,
+ DefaultLogDelegate)
{
}
@@ -89,7 +101,8 @@ namespace Thrift.Server
TTransportFactory inputTransportFactory,
TTransportFactory outputTransportFactory,
TProtocolFactory inputProtocolFactory,
- TProtocolFactory outputProtocolFactory)
+ TProtocolFactory outputProtocolFactory,
+ LogDelegate logDelegate)
{
this.processor = processor;
this.serverTransport = serverTransport;
@@ -97,6 +110,7 @@ namespace Thrift.Server
this.outputTransportFactory = outputTransportFactory;
this.inputProtocolFactory = inputProtocolFactory;
this.outputProtocolFactory = outputProtocolFactory;
+ this.logDelegate = logDelegate;
}
/**
@@ -105,6 +119,11 @@ namespace Thrift.Server
public abstract void Serve();
public abstract void Stop();
+
+ protected static void DefaultLogDelegate(string s)
+ {
+ Console.Error.WriteLine(s);
+ }
}
}
diff --git a/lib/csharp/src/Server/TSimpleServer.cs b/lib/csharp/src/Server/TSimpleServer.cs
index 0679bb2d9..95f39623d 100644
--- a/lib/csharp/src/Server/TSimpleServer.cs
+++ b/lib/csharp/src/Server/TSimpleServer.cs
@@ -23,9 +23,17 @@ namespace Thrift.Server
public class TSimpleServer : TServer
{
private bool stop = false;
+
public TSimpleServer(TProcessor processor,
TServerTransport serverTransport)
- :base(processor, serverTransport, new TTransportFactory(), new TTransportFactory(), new TBinaryProtocol.Factory(), new TBinaryProtocol.Factory())
+ :base(processor, serverTransport, new TTransportFactory(), new TTransportFactory(), new TBinaryProtocol.Factory(), new TBinaryProtocol.Factory(), DefaultLogDelegate)
+ {
+ }
+
+ public TSimpleServer(TProcessor processor,
+ TServerTransport serverTransport,
+ LogDelegate logDel)
+ : base(processor, serverTransport, new TTransportFactory(), new TTransportFactory(), new TBinaryProtocol.Factory(), new TBinaryProtocol.Factory(), logDel)
{
}
@@ -37,7 +45,8 @@ namespace Thrift.Server
transportFactory,
transportFactory,
new TBinaryProtocol.Factory(),
- new TBinaryProtocol.Factory())
+ new TBinaryProtocol.Factory(),
+ DefaultLogDelegate)
{
}
@@ -50,7 +59,8 @@ namespace Thrift.Server
transportFactory,
transportFactory,
protocolFactory,
- protocolFactory)
+ protocolFactory,
+ DefaultLogDelegate)
{
}
@@ -62,7 +72,7 @@ namespace Thrift.Server
}
catch (TTransportException ttx)
{
- Console.Error.WriteLine(ttx);
+ logDelegate(ttx.ToString());
return;
}
@@ -85,13 +95,17 @@ namespace Thrift.Server
while (processor.Process(inputProtocol, outputProtocol)) { }
}
}
- catch (TTransportException)
+ catch (TTransportException ttx)
{
// Client died, just move on
+ if (stop)
+ {
+ logDelegate("TSimpleServer was shutting down, caught " + ttx.GetType().Name);
+ }
}
catch (Exception x)
{
- Console.Error.WriteLine(x);
+ logDelegate(x.ToString());
}
if (inputTransport != null)
@@ -113,7 +127,7 @@ namespace Thrift.Server
}
catch (TTransportException ttx)
{
- Console.Error.WriteLine("TServerTrasnport failed on close: " + ttx.Message);
+ logDelegate("TServerTranport failed on close: " + ttx.Message);
}
stop = false;
}
@@ -122,6 +136,7 @@ namespace Thrift.Server
public override void Stop()
{
stop = true;
+ serverTransport.Close();
}
}
}
diff --git a/lib/csharp/src/Server/TThreadPoolServer.cs b/lib/csharp/src/Server/TThreadPoolServer.cs
index ac7aa8b07..b44487fd0 100644
--- a/lib/csharp/src/Server/TThreadPoolServer.cs
+++ b/lib/csharp/src/Server/TThreadPoolServer.cs
@@ -31,10 +31,19 @@ namespace Thrift.Server
:this(processor, serverTransport,
new TTransportFactory(), new TTransportFactory(),
new TBinaryProtocol.Factory(), new TBinaryProtocol.Factory(),
- DEFAULT_MIN_THREADS, DEFAULT_MAX_THREADS)
+ DEFAULT_MIN_THREADS, DEFAULT_MAX_THREADS, DefaultLogDelegate)
{
}
+ public TThreadPoolServer(TProcessor processor, TServerTransport serverTransport, LogDelegate logDelegate)
+ : this(processor, serverTransport,
+ new TTransportFactory(), new TTransportFactory(),
+ new TBinaryProtocol.Factory(), new TBinaryProtocol.Factory(),
+ DEFAULT_MIN_THREADS, DEFAULT_MAX_THREADS, logDelegate)
+ {
+ }
+
+
public TThreadPoolServer(TProcessor processor,
TServerTransport serverTransport,
TTransportFactory transportFactory,
@@ -42,7 +51,7 @@ namespace Thrift.Server
:this(processor, serverTransport,
transportFactory, transportFactory,
protocolFactory, protocolFactory,
- DEFAULT_MIN_THREADS, DEFAULT_MAX_THREADS)
+ DEFAULT_MIN_THREADS, DEFAULT_MAX_THREADS, DefaultLogDelegate)
{
}
@@ -52,9 +61,9 @@ namespace Thrift.Server
TTransportFactory outputTransportFactory,
TProtocolFactory inputProtocolFactory,
TProtocolFactory outputProtocolFactory,
- int minThreadPoolThreads, int maxThreadPoolThreads)
+ int minThreadPoolThreads, int maxThreadPoolThreads, LogDelegate logDel)
:base(processor, serverTransport, inputTransportFactory, outputTransportFactory,
- inputProtocolFactory, outputProtocolFactory)
+ inputProtocolFactory, outputProtocolFactory, logDel)
{
if (!ThreadPool.SetMinThreads(minThreadPoolThreads, minThreadPoolThreads))
{
@@ -64,7 +73,6 @@ namespace Thrift.Server
{
throw new Exception("Error: could not SetMaxThreads in ThreadPool");
}
-
}
/// <summary>
@@ -78,7 +86,7 @@ namespace Thrift.Server
}
catch (TTransportException ttx)
{
- Console.Error.WriteLine("Error, could not listen on ServerTransport: " + ttx);
+ logDelegate("Error, could not listen on ServerTransport: " + ttx);
return;
}
@@ -92,8 +100,16 @@ namespace Thrift.Server
}
catch (TTransportException ttx)
{
- ++failureCount;
- Console.Error.WriteLine(ttx);
+ if (stop)
+ {
+ logDelegate("TThreadPoolServer was shutting down, caught " + ttx.GetType().Name);
+ }
+ else
+ {
+ ++failureCount;
+ logDelegate(ttx.ToString());
+ }
+
}
}
@@ -105,18 +121,12 @@ namespace Thrift.Server
}
catch (TTransportException ttx)
{
- Console.Error.WriteLine("TServerTrasnport failed on close: " + ttx.Message);
+ logDelegate("TServerTransport failed on close: " + ttx.Message);
}
stop = false;
}
}
-
- public override void Stop()
- {
- stop = true;
- }
-
/// <summary>
/// Loops on processing a client forever
/// threadContext will be a TTransport instance
@@ -143,11 +153,12 @@ namespace Thrift.Server
catch (TTransportException)
{
// Assume the client died and continue silently
+ //Console.WriteLine(ttx);
}
catch (Exception x)
{
- Console.Error.WriteLine("Error: " + x);
+ logDelegate("Error: " + x);
}
if (inputTransport != null)
@@ -159,5 +170,11 @@ namespace Thrift.Server
outputTransport.Close();
}
}
+
+ public override void Stop()
+ {
+ stop = true;
+ serverTransport.Close();
+ }
}
}
diff --git a/lib/csharp/src/Server/TThreadedServer.cs b/lib/csharp/src/Server/TThreadedServer.cs
new file mode 100644
index 000000000..a4d33a5ed
--- /dev/null
+++ b/lib/csharp/src/Server/TThreadedServer.cs
@@ -0,0 +1,226 @@
+//
+// TThreadPoolServer.cs
+//
+// Begin: Apr 21, 2008
+// Authors:
+// Will Palmeri <wpalmeri@imeem.com>
+//
+// Distributed under the Thrift Software License
+//
+// See accompanying file LICENSE or visit the Thrift site at:
+// http://developers.facebook.com/thrift/using
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading;
+using Thrift.Protocol;
+using Thrift.Transport;
+
+namespace Thrift.Server
+{
+ /// <summary>
+ /// Server that uses C# threads (as opposed to the ThreadPool) when handling requests
+ /// </summary>
+ public class TThreadedServer : TServer
+ {
+ private const int DEFAULT_MAX_THREADS = 100;
+ private volatile bool stop = false;
+ private readonly int maxThreads;
+
+ private Queue<TTransport> clientQueue;
+ private HashSet<Thread> clientThreads;
+ private object clientLock;
+ private Thread workerThread;
+
+ public TThreadedServer(TProcessor processor, TServerTransport serverTransport)
+ : this(processor, serverTransport,
+ new TTransportFactory(), new TTransportFactory(),
+ new TBinaryProtocol.Factory(), new TBinaryProtocol.Factory(),
+ DEFAULT_MAX_THREADS, DefaultLogDelegate)
+ {
+ }
+
+ public TThreadedServer(TProcessor processor, TServerTransport serverTransport, LogDelegate logDelegate)
+ : this(processor, serverTransport,
+ new TTransportFactory(), new TTransportFactory(),
+ new TBinaryProtocol.Factory(), new TBinaryProtocol.Factory(),
+ DEFAULT_MAX_THREADS, logDelegate)
+ {
+ }
+
+
+ public TThreadedServer(TProcessor processor,
+ TServerTransport serverTransport,
+ TTransportFactory transportFactory,
+ TProtocolFactory protocolFactory)
+ : this(processor, serverTransport,
+ transportFactory, transportFactory,
+ protocolFactory, protocolFactory,
+ DEFAULT_MAX_THREADS, DefaultLogDelegate)
+ {
+ }
+
+ public TThreadedServer(TProcessor processor,
+ TServerTransport serverTransport,
+ TTransportFactory inputTransportFactory,
+ TTransportFactory outputTransportFactory,
+ TProtocolFactory inputProtocolFactory,
+ TProtocolFactory outputProtocolFactory,
+ int maxThreads, LogDelegate logDel)
+ : base(processor, serverTransport, inputTransportFactory, outputTransportFactory,
+ inputProtocolFactory, outputProtocolFactory, logDel)
+ {
+ this.maxThreads = maxThreads;
+ clientQueue = new Queue<TTransport>();
+ clientLock = new object();
+ clientThreads = new HashSet<Thread>();
+ }
+
+ /// <summary>
+ /// Use new Thread for each new client connection. block until numConnections < maxTHreads
+ /// </summary>
+ public override void Serve()
+ {
+ try
+ {
+ //start worker thread
+ workerThread = new Thread(new ThreadStart(Execute));
+ workerThread.Start();
+ serverTransport.Listen();
+ }
+ catch (TTransportException ttx)
+ {
+ logDelegate("Error, could not listen on ServerTransport: " + ttx);
+ return;
+ }
+
+ while (!stop)
+ {
+ int failureCount = 0;
+ try
+ {
+ TTransport client = serverTransport.Accept();
+ lock (clientLock)
+ {
+ clientQueue.Enqueue(client);
+ Monitor.Pulse(clientLock);
+ }
+ }
+ catch (TTransportException ttx)
+ {
+ if (stop)
+ {
+ logDelegate("TThreadPoolServer was shutting down, caught " + ttx);
+ }
+ else
+ {
+ ++failureCount;
+ logDelegate(ttx.ToString());
+ }
+
+ }
+ }
+
+ if (stop)
+ {
+ try
+ {
+ serverTransport.Close();
+ }
+ catch (TTransportException ttx)
+ {
+ logDelegate("TServeTransport failed on close: " + ttx.Message);
+ }
+ stop = false;
+ }
+ }
+
+ /// <summary>
+ /// Loops on processing a client forever
+ /// threadContext will be a TTransport instance
+ /// </summary>
+ /// <param name="threadContext"></param>
+ private void Execute()
+ {
+ while (!stop)
+ {
+ TTransport client;
+ Thread t;
+ lock (clientLock)
+ {
+ //don't dequeue if too many connections
+ while (clientThreads.Count >= maxThreads)
+ {
+ Monitor.Wait(clientLock);
+ }
+
+ while (clientQueue.Count == 0)
+ {
+ Monitor.Wait(clientLock);
+ }
+
+ client = clientQueue.Dequeue();
+ t = new Thread(new ParameterizedThreadStart(ClientWorker));
+ clientThreads.Add(t);
+ }
+ //start processing requests from client on new thread
+ t.Start(client);
+ }
+ }
+
+ private void ClientWorker(Object context)
+ {
+ TTransport client = (TTransport)context;
+ TTransport inputTransport = null;
+ TTransport outputTransport = null;
+ TProtocol inputProtocol = null;
+ TProtocol outputProtocol = null;
+ try
+ {
+ inputTransport = inputTransportFactory.GetTransport(client);
+ outputTransport = outputTransportFactory.GetTransport(client);
+ inputProtocol = inputProtocolFactory.GetProtocol(inputTransport);
+ outputProtocol = outputProtocolFactory.GetProtocol(outputTransport);
+ while (processor.Process(inputProtocol, outputProtocol))
+ {
+ //keep processing requests until client disconnects
+ }
+ }
+ catch (TTransportException)
+ {
+ }
+ catch (Exception x)
+ {
+ logDelegate("Error: " + x);
+ }
+
+ if (inputTransport != null)
+ {
+ inputTransport.Close();
+ }
+ if (outputTransport != null)
+ {
+ outputTransport.Close();
+ }
+
+ lock (clientLock)
+ {
+ clientThreads.Remove(Thread.CurrentThread);
+ Monitor.Pulse(clientLock);
+ }
+ return;
+ }
+
+ public override void Stop()
+ {
+ stop = true;
+ serverTransport.Close();
+ //clean up all the threads myself
+ workerThread.Abort();
+ foreach (Thread t in clientThreads)
+ {
+ t.Abort();
+ }
+ }
+ }
+}
diff --git a/lib/csharp/src/Thrift.csproj b/lib/csharp/src/Thrift.csproj
index 4b407b1de..1a1a332d4 100644
--- a/lib/csharp/src/Thrift.csproj
+++ b/lib/csharp/src/Thrift.csproj
@@ -59,11 +59,13 @@
<Compile Include="Protocol\TSet.cs" />
<Compile Include="Protocol\TStruct.cs" />
<Compile Include="Protocol\TType.cs" />
+ <Compile Include="Server\TThreadedServer.cs" />
<Compile Include="Server\TServer.cs" />
<Compile Include="Server\TSimpleServer.cs" />
<Compile Include="Server\TThreadPoolServer.cs" />
<Compile Include="TApplicationException.cs" />
<Compile Include="TProcessor.cs" />
+ <Compile Include="Transport\TBufferedTransport.cs" />
<Compile Include="Transport\TServerSocket.cs" />
<Compile Include="Transport\TServerTransport.cs" />
<Compile Include="Transport\TSocket.cs" />
diff --git a/lib/csharp/src/Transport/TBufferedTransport.cs b/lib/csharp/src/Transport/TBufferedTransport.cs
new file mode 100644
index 000000000..96a074175
--- /dev/null
+++ b/lib/csharp/src/Transport/TBufferedTransport.cs
@@ -0,0 +1,92 @@
+//
+// TBufferedTransport.cs
+//
+// Begin: May 22, 2008
+// Authors:
+// Will Palmeri <wpalmeri@imeem.com>
+//
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.IO;
+
+namespace Thrift.Transport
+{
+ public class TBufferedTransport : TTransport
+ {
+ private BufferedStream inputBuffer;
+ private BufferedStream outputBuffer;
+ private int bufSize;
+ private TStreamTransport transport;
+
+ public TBufferedTransport(TStreamTransport transport)
+ :this(transport, 1024)
+ {
+
+ }
+
+ public TBufferedTransport(TStreamTransport transport, int bufSize)
+ {
+ this.bufSize = bufSize;
+ this.transport = transport;
+ InitBuffers();
+ }
+
+ private void InitBuffers()
+ {
+ if (transport.InputStream != null)
+ {
+ inputBuffer = new BufferedStream(transport.InputStream, bufSize);
+ }
+ if (transport.OutputStream != null)
+ {
+ outputBuffer = new BufferedStream(transport.OutputStream, bufSize);
+ }
+ }
+
+ public TTransport UnderlyingTransport
+ {
+ get { return transport; }
+ }
+
+ public override bool IsOpen
+ {
+ get { return transport.IsOpen; }
+ }
+
+ public override void Open()
+ {
+ transport.Open();
+ InitBuffers();
+ }
+
+ public override void Close()
+ {
+ if (inputBuffer != null && inputBuffer.CanRead)
+ {
+ inputBuffer.Close();
+ }
+ if (outputBuffer != null && outputBuffer.CanWrite)
+ {
+ outputBuffer.Close();
+ }
+ }
+
+ public override int Read(byte[] buf, int off, int len)
+ {
+ return inputBuffer.Read(buf, off, len);
+ }
+
+ public override void Write(byte[] buf, int off, int len)
+ {
+ outputBuffer.Write(buf, off, len);
+ }
+
+ public override void Flush()
+ {
+ outputBuffer.Flush();
+ }
+ }
+}
diff --git a/lib/csharp/src/Transport/TServerSocket.cs b/lib/csharp/src/Transport/TServerSocket.cs
index 33ada7bab..a6caf90c0 100644
--- a/lib/csharp/src/Transport/TServerSocket.cs
+++ b/lib/csharp/src/Transport/TServerSocket.cs
@@ -34,6 +34,11 @@ namespace Thrift.Transport
private int clientTimeout = 0;
/**
+ * Whether or not to wrap new TSocket connections in buffers
+ */
+ private bool useBufferedSockets = false;
+
+ /**
* Creates a server socket from underlying socket object
*/
public TServerSocket(TcpListener listener)
@@ -62,9 +67,15 @@ namespace Thrift.Transport
* Creates just a port listening server socket
*/
public TServerSocket(int port, int clientTimeout)
+ :this(port, clientTimeout, false)
+ {
+ }
+
+ public TServerSocket(int port, int clientTimeout, bool useBufferedSockets)
{
this.port = port;
this.clientTimeout = clientTimeout;
+ this.useBufferedSockets = useBufferedSockets;
try
{
// Make server socket
@@ -88,7 +99,7 @@ namespace Thrift.Transport
}
catch (SocketException sx)
{
- Console.Error.WriteLine(sx);
+ throw new TTransportException("Could not accept on listening socket: " + sx.Message);
}
}
}
@@ -104,7 +115,15 @@ namespace Thrift.Transport
TcpClient result = server.AcceptTcpClient();
TSocket result2 = new TSocket(result);
result2.Timeout = clientTimeout;
- return result2;
+ if (useBufferedSockets)
+ {
+ TBufferedTransport result3 = new TBufferedTransport(result2);
+ return result3;
+ }
+ else
+ {
+ return result2;
+ }
}
catch (Exception ex)
{
@@ -122,7 +141,7 @@ namespace Thrift.Transport
}
catch (Exception ex)
{
- Console.Error.WriteLine("WARNING: Could not close server socket: " + ex);
+ throw new TTransportException("WARNING: Could not close server socket: " + ex);
}
server = null;
}
diff --git a/lib/csharp/src/Transport/TSocket.cs b/lib/csharp/src/Transport/TSocket.cs
index a3f362388..c790fce27 100644
--- a/lib/csharp/src/Transport/TSocket.cs
+++ b/lib/csharp/src/Transport/TSocket.cs
@@ -69,6 +69,22 @@ namespace Thrift.Transport
}
}
+ public string Host
+ {
+ get
+ {
+ return host;
+ }
+ }
+
+ public int Port
+ {
+ get
+ {
+ return port;
+ }
+ }
+
public override bool IsOpen
{
get
diff --git a/lib/csharp/src/Transport/TStreamTransport.cs b/lib/csharp/src/Transport/TStreamTransport.cs
index dbbec1912..ca14ecf8f 100644
--- a/lib/csharp/src/Transport/TStreamTransport.cs
+++ b/lib/csharp/src/Transport/TStreamTransport.cs
@@ -31,6 +31,16 @@ namespace Thrift.Transport
this.outputStream = outputStream;
}
+ public Stream OutputStream
+ {
+ get { return outputStream; }
+ }
+
+ public Stream InputStream
+ {
+ get { return inputStream; }
+ }
+
public override bool IsOpen
{
get { return true; }
diff --git a/test/csharp/ThriftTest/Program.cs b/test/csharp/ThriftTest/Program.cs
new file mode 100644
index 000000000..09bd84f9c
--- /dev/null
+++ b/test/csharp/ThriftTest/Program.cs
@@ -0,0 +1,44 @@
+// Distributed under the Thrift Software License
+//
+// See accompanying file LICENSE or visit the Thrift site at:
+// http://developers.facebook.com/thrift/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Thrift.Transport;
+using Thrift.Protocol;
+using Thrift.Test; //generated code
+
+namespace Test
+{
+ class Program
+ {
+ static void Main(string[] args)
+ {
+ if (args.Length == 0)
+ {
+ Console.WriteLine("must provide 'server' or 'client' arg");
+ return;
+ }
+
+ string[] subArgs = new string[args.Length - 1];
+ for(int i = 1; i < args.Length; i++)
+ {
+ subArgs[i-1] = args[i];
+ }
+ if (args[0] == "client")
+ {
+ TestClient.Execute(subArgs);
+ }
+ else if (args[0] == "server")
+ {
+ TestServer.Execute(subArgs);
+ }
+ else
+ {
+ Console.WriteLine("first argument must be 'server' or 'client'");
+ }
+ }
+ }
+}
diff --git a/test/csharp/ThriftTest/Properties/AssemblyInfo.cs b/test/csharp/ThriftTest/Properties/AssemblyInfo.cs
new file mode 100644
index 000000000..5b459498e
--- /dev/null
+++ b/test/csharp/ThriftTest/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("ThriftTest")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("ThriftTest")]
+[assembly: AssemblyCopyright("Copyright © 2009 The Apache Software Foundation")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("f41b193b-f1ab-48ee-8843-f88e43084e26")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/test/csharp/ThriftTest/TestClient.cs b/test/csharp/ThriftTest/TestClient.cs
new file mode 100644
index 000000000..bcdb00e5e
--- /dev/null
+++ b/test/csharp/ThriftTest/TestClient.cs
@@ -0,0 +1,407 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+using Thrift.Protocol;
+using Thrift.Transport;
+using Thrift.Test;
+using System.Threading;
+
+namespace Test
+{
+ public class TestClient
+ {
+ private static int numIterations = 1;
+
+ public static void Execute(string[] args)
+ {
+ try
+ {
+ string host = "localhost";
+ int port = 9090;
+ string url = null;
+ int numThreads = 1;
+ bool buffered = false;
+
+ try
+ {
+ for (int i = 0; i < args.Length; i++)
+ {
+ if (args[i] == "-h")
+ {
+ string[] hostport = args[++i].Split(':');
+ host = hostport[0];
+ if (hostport.Length > 1)
+ {
+ port = Convert.ToInt32(hostport[1]);
+ }
+ }
+ else if (args[i] == "-u")
+ {
+ url = args[++i];
+ }
+ else if (args[i] == "-n")
+ {
+ numIterations = Convert.ToInt32(args[++i]);
+ }
+ else if (args[i] == "-b" || args[i] == "-buffered")
+ {
+ buffered = true;
+ Console.WriteLine("Using buffered sockets");
+ }
+ else if (args[i] == "-t")
+ {
+ numThreads = Convert.ToInt32(args[++i]);
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine(e.StackTrace);
+ }
+
+
+
+ //issue tests on separate threads simultaneously
+ Thread[] threads = new Thread[numThreads];
+ DateTime start = DateTime.Now;
+ for (int test = 0; test < numThreads; test++)
+ {
+ Thread t = new Thread(new ParameterizedThreadStart(ClientThread));
+ threads[test] = t;
+ TSocket socket = new TSocket(host, port);
+ if (buffered)
+ {
+ TBufferedTransport buffer = new TBufferedTransport(socket);
+ t.Start(buffer);
+ }
+ else
+ {
+ t.Start(socket);
+ }
+ }
+
+ for (int test = 0; test < numThreads; test++)
+ {
+ threads[test].Join();
+ }
+ Console.Write("Total time: " + (DateTime.Now - start));
+ }
+ catch (Exception outerEx)
+ {
+ Console.WriteLine(outerEx.Message + " ST: " + outerEx.StackTrace);
+ }
+
+ Console.WriteLine();
+ Console.WriteLine();
+ }
+
+ public static void ClientThread(object obj)
+ {
+ TTransport transport = (TTransport)obj;
+ for (int i = 0; i < numIterations; i++)
+ {
+ ClientTest(transport);
+ }
+ transport.Close();
+ }
+
+ public static void ClientTest(TTransport transport)
+ {
+ TBinaryProtocol binaryProtocol = new TBinaryProtocol(transport);
+
+ ThriftTest.Client client = new ThriftTest.Client(binaryProtocol);
+ try
+ {
+ if (!transport.IsOpen)
+ {
+ transport.Open();
+ }
+ }
+ catch (TTransportException ttx)
+ {
+ Console.WriteLine("Connect failed: " + ttx.Message);
+ return;
+ }
+
+ long start = DateTime.Now.ToFileTime();
+
+ Console.Write("testVoid()");
+ client.testVoid();
+ Console.WriteLine(" = void");
+
+ Console.Write("testString(\"Test\")");
+ string s = client.testString("Test");
+ Console.WriteLine(" = \"" + s + "\"");
+
+ Console.Write("testByte(1)");
+ byte i8 = client.testByte((byte)1);
+ Console.WriteLine(" = " + i8);
+
+ Console.Write("testI32(-1)");
+ int i32 = client.testI32(-1);
+ Console.WriteLine(" = " + i32);
+
+ Console.Write("testI64(-34359738368)");
+ long i64 = client.testI64(-34359738368);
+ Console.WriteLine(" = " + i64);
+
+ Console.Write("testDouble(5.325098235)");
+ double dub = client.testDouble(5.325098235);
+ Console.WriteLine(" = " + dub);
+
+ Console.Write("testStruct({\"Zero\", 1, -3, -5})");
+ Xtruct o = new Xtruct();
+ o.String_thing = "Zero";
+ o.Byte_thing = (byte)1;
+ o.I32_thing = -3;
+ o.I64_thing = -5;
+ Xtruct i = client.testStruct(o);
+ Console.WriteLine(" = {\"" + i.String_thing + "\", " + i.Byte_thing + ", " + i.I32_thing + ", " + i.I64_thing + "}");
+
+ Console.Write("testNest({1, {\"Zero\", 1, -3, -5}, 5})");
+ Xtruct2 o2 = new Xtruct2();
+ o2.Byte_thing = (byte)1;
+ o2.Struct_thing = o;
+ o2.I32_thing = 5;
+ Xtruct2 i2 = client.testNest(o2);
+ i = i2.Struct_thing;
+ Console.WriteLine(" = {" + i2.Byte_thing + ", {\"" + i.String_thing + "\", " + i.Byte_thing + ", " + i.I32_thing + ", " + i.I64_thing + "}, " + i2.I32_thing + "}");
+
+ Dictionary<int, int> mapout = new Dictionary<int, int>();
+ for (int j = 0; j < 5; j++)
+ {
+ mapout[j] = j - 10;
+ }
+ Console.Write("testMap({");
+ bool first = true;
+ foreach (int key in mapout.Keys)
+ {
+ if (first)
+ {
+ first = false;
+ }
+ else
+ {
+ Console.Write(", ");
+ }
+ Console.Write(key + " => " + mapout[key]);
+ }
+ Console.Write("})");
+
+ Dictionary<int, int> mapin = client.testMap(mapout);
+
+ Console.Write(" = {");
+ first = true;
+ foreach (int key in mapin.Keys)
+ {
+ if (first)
+ {
+ first = false;
+ }
+ else
+ {
+ Console.Write(", ");
+ }
+ Console.Write(key + " => " + mapin[key]);
+ }
+ Console.WriteLine("}");
+
+ List<int> listout = new List<int>();
+ for (int j = -2; j < 3; j++)
+ {
+ listout.Add(j);
+ }
+ Console.Write("testList({");
+ first = true;
+ foreach (int j in listout)
+ {
+ if (first)
+ {
+ first = false;
+ }
+ else
+ {
+ Console.Write(", ");
+ }
+ Console.Write(j);
+ }
+ Console.Write("})");
+
+ List<int> listin = client.testList(listout);
+
+ Console.Write(" = {");
+ first = true;
+ foreach (int j in listin)
+ {
+ if (first)
+ {
+ first = false;
+ }
+ else
+ {
+ Console.Write(", ");
+ }
+ Console.Write(j);
+ }
+ Console.WriteLine("}");
+
+ //set
+ HashSet<int> setout = new HashSet<int>();
+ for (int j = -2; j < 3; j++)
+ {
+ setout.Add(j);
+ }
+ Console.Write("testSet({");
+ first = true;
+ foreach (int j in setout)
+ {
+ if (first)
+ {
+ first = false;
+ }
+ else
+ {
+ Console.Write(", ");
+ }
+ Console.Write(j);
+ }
+ Console.Write("})");
+
+ HashSet<int> setin = client.testSet(setout);
+
+ Console.Write(" = {");
+ first = true;
+ foreach (int j in setin)
+ {
+ if (first)
+ {
+ first = false;
+ }
+ else
+ {
+ Console.Write(", ");
+ }
+ Console.Write(j);
+ }
+ Console.WriteLine("}");
+
+
+ Console.Write("testEnum(ONE)");
+ Numberz ret = client.testEnum(Numberz.ONE);
+ Console.WriteLine(" = " + ret);
+
+ Console.Write("testEnum(TWO)");
+ ret = client.testEnum(Numberz.TWO);
+ Console.WriteLine(" = " + ret);
+
+ Console.Write("testEnum(THREE)");
+ ret = client.testEnum(Numberz.THREE);
+ Console.WriteLine(" = " + ret);
+
+ Console.Write("testEnum(FIVE)");
+ ret = client.testEnum(Numberz.FIVE);
+ Console.WriteLine(" = " + ret);
+
+ Console.Write("testEnum(EIGHT)");
+ ret = client.testEnum(Numberz.EIGHT);
+ Console.WriteLine(" = " + ret);
+
+ Console.Write("testTypedef(309858235082523)");
+ long uid = client.testTypedef(309858235082523L);
+ Console.WriteLine(" = " + uid);
+
+ Console.Write("testMapMap(1)");
+ Dictionary<int, Dictionary<int, int>> mm = client.testMapMap(1);
+ Console.Write(" = {");
+ foreach (int key in mm.Keys)
+ {
+ Console.Write(key + " => {");
+ Dictionary<int, int> m2 = mm[key];
+ foreach (int k2 in m2.Keys)
+ {
+ Console.Write(k2 + " => " + m2[k2] + ", ");
+ }
+ Console.Write("}, ");
+ }
+ Console.WriteLine("}");
+
+ Insanity insane = new Insanity();
+ insane.UserMap = new Dictionary<Numberz, long>();
+ insane.UserMap[Numberz.FIVE] = 5000L;
+ Xtruct truck = new Xtruct();
+ truck.String_thing = "Truck";
+ truck.Byte_thing = (byte)8;
+ truck.I32_thing = 8;
+ truck.I64_thing = 8;
+ insane.Xtructs = new List<Xtruct>();
+ insane.Xtructs.Add(truck);
+ Console.Write("testInsanity()");
+ Dictionary<long, Dictionary<Numberz, Insanity>> whoa = client.testInsanity(insane);
+ Console.Write(" = {");
+ foreach (long key in whoa.Keys)
+ {
+ Dictionary<Numberz, Insanity> val = whoa[key];
+ Console.Write(key + " => {");
+
+ foreach (Numberz k2 in val.Keys)
+ {
+ Insanity v2 = val[k2];
+
+ Console.Write(k2 + " => {");
+ Dictionary<Numberz, long> userMap = v2.UserMap;
+
+ Console.Write("{");
+ if (userMap != null)
+ {
+ foreach (Numberz k3 in userMap.Keys)
+ {
+ Console.Write(k3 + " => " + userMap[k3] + ", ");
+ }
+ }
+ else
+ {
+ Console.Write("null");
+ }
+ Console.Write("}, ");
+
+ List<Xtruct> xtructs = v2.Xtructs;
+
+ Console.Write("{");
+ if (xtructs != null)
+ {
+ foreach (Xtruct x in xtructs)
+ {
+ Console.Write("{\"" + x.String_thing + "\", " + x.Byte_thing + ", " + x.I32_thing + ", " + x.I32_thing + "}, ");
+ }
+ }
+ else
+ {
+ Console.Write("null");
+ }
+ Console.Write("}");
+
+ Console.Write("}, ");
+ }
+ Console.Write("}, ");
+ }
+ Console.WriteLine("}");
+
+
+ byte arg0 = 1;
+ int arg1 = 2;
+ long arg2 = long.MaxValue;
+ Dictionary<short, string> multiDict = new Dictionary<short, string>();
+ multiDict[1] = "one";
+ Numberz arg4 = Numberz.FIVE;
+ long arg5 = 5000000;
+ Console.Write("Test Multi(" + arg0 + "," + arg1 + "," + arg2 + "," + multiDict + "," + arg4 + "," + arg5 + ")");
+ Xtruct multiResponse = client.testMulti(arg0, arg1, arg2, multiDict, arg4, arg5);
+ Console.Write(" = Xtruct(byte_thing:" + multiResponse.Byte_thing + ",String_thing:" + multiResponse.String_thing
+ + ",i32_thing:" + multiResponse.I32_thing + ",i64_thing:" + multiResponse.I64_thing + ")\n");
+
+ Console.WriteLine("Test Async(1)");
+ client.testAsync(1);
+ }
+ }
+}
diff --git a/test/csharp/ThriftTest/TestServer.cs b/test/csharp/ThriftTest/TestServer.cs
new file mode 100644
index 000000000..4efe4b4df
--- /dev/null
+++ b/test/csharp/ThriftTest/TestServer.cs
@@ -0,0 +1,327 @@
+// Distributed under the Thrift Software License
+//
+// See accompanying file LICENSE or visit the Thrift site at:
+// http://developers.facebook.com/thrift/
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Thrift.Test; //generated code
+
+using Thrift.Transport;
+using Thrift.Protocol;
+using Thrift.Server;
+
+namespace Test
+{
+ public class TestServer
+ {
+ public class TestHandler : ThriftTest.Iface
+ {
+ public TServer server;
+
+ public TestHandler() { }
+
+ public void testVoid()
+ {
+ Console.WriteLine("testVoid()");
+ }
+
+ public string testString(string thing)
+ {
+ Console.WriteLine("teststring(\"" + thing + "\")");
+ return thing;
+ }
+
+ public byte testByte(byte thing)
+ {
+ Console.WriteLine("testByte(" + thing + ")");
+ return thing;
+ }
+
+ public int testI32(int thing)
+ {
+ Console.WriteLine("testI32(" + thing + ")");
+ return thing;
+ }
+
+ public long testI64(long thing)
+ {
+ Console.WriteLine("testI64(" + thing + ")");
+ return thing;
+ }
+
+ public double testDouble(double thing)
+ {
+ Console.WriteLine("testDouble(" + thing + ")");
+ return thing;
+ }
+
+ public Xtruct testStruct(Xtruct thing)
+ {
+ Console.WriteLine("testStruct({" +
+ "\"" + thing.String_thing + "\", " +
+ thing.Byte_thing + ", " +
+ thing.I32_thing + ", " +
+ thing.I64_thing + "})");
+ return thing;
+ }
+
+ public Xtruct2 testNest(Xtruct2 nest)
+ {
+ Xtruct thing = nest.Struct_thing;
+ Console.WriteLine("testNest({" +
+ nest.Byte_thing + ", {" +
+ "\"" + thing.String_thing + "\", " +
+ thing.Byte_thing + ", " +
+ thing.I32_thing + ", " +
+ thing.I64_thing + "}, " +
+ nest.I32_thing + "})");
+ return nest;
+ }
+
+ public Dictionary<int, int> testMap(Dictionary<int, int> thing)
+ {
+ Console.WriteLine("testMap({");
+ bool first = true;
+ foreach (int key in thing.Keys)
+ {
+ if (first)
+ {
+ first = false;
+ }
+ else
+ {
+ Console.WriteLine(", ");
+ }
+ Console.WriteLine(key + " => " + thing[key]);
+ }
+ Console.WriteLine("})");
+ return thing;
+ }
+
+ public HashSet<int> testSet(HashSet<int> thing)
+ {
+ Console.WriteLine("testSet({");
+ bool first = true;
+ foreach (int elem in thing)
+ {
+ if (first)
+ {
+ first = false;
+ }
+ else
+ {
+ Console.WriteLine(", ");
+ }
+ Console.WriteLine(elem);
+ }
+ Console.WriteLine("})");
+ return thing;
+ }
+
+ public List<int> testList(List<int> thing)
+ {
+ Console.WriteLine("testList({");
+ bool first = true;
+ foreach (int elem in thing)
+ {
+ if (first)
+ {
+ first = false;
+ }
+ else
+ {
+ Console.WriteLine(", ");
+ }
+ Console.WriteLine(elem);
+ }
+ Console.WriteLine("})");
+ return thing;
+ }
+
+ public Numberz testEnum(Numberz thing)
+ {
+ Console.WriteLine("testEnum(" + thing + ")");
+ return thing;
+ }
+
+ public long testTypedef(long thing)
+ {
+ Console.WriteLine("testTypedef(" + thing + ")");
+ return thing;
+ }
+
+ public Dictionary<int, Dictionary<int, int>> testMapMap(int hello)
+ {
+ Console.WriteLine("testMapMap(" + hello + ")");
+ Dictionary<int, Dictionary<int, int>> mapmap =
+ new Dictionary<int, Dictionary<int, int>>();
+
+ Dictionary<int, int> pos = new Dictionary<int, int>();
+ Dictionary<int, int> neg = new Dictionary<int, int>();
+ for (int i = 1; i < 5; i++)
+ {
+ pos[i] = i;
+ neg[-i] = -i;
+ }
+
+ mapmap[4] = pos;
+ mapmap[-4] = neg;
+
+ return mapmap;
+ }
+
+ public Dictionary<long, Dictionary<Numberz, Insanity>> testInsanity(Insanity argument)
+ {
+ Console.WriteLine("testInsanity()");
+
+ Xtruct hello = new Xtruct();
+ hello.String_thing = "Hello2";
+ hello.Byte_thing = 2;
+ hello.I32_thing = 2;
+ hello.I64_thing = 2;
+
+ Xtruct goodbye = new Xtruct();
+ goodbye.String_thing = "Goodbye4";
+ goodbye.Byte_thing = (byte)4;
+ goodbye.I32_thing = 4;
+ goodbye.I64_thing = (long)4;
+
+ Insanity crazy = new Insanity();
+ crazy.UserMap = new Dictionary<Numberz, long>();
+ crazy.UserMap[Numberz.EIGHT] = (long)8;
+ crazy.Xtructs = new List<Xtruct>();
+ crazy.Xtructs.Add(goodbye);
+
+ Insanity looney = new Insanity();
+ crazy.UserMap[Numberz.FIVE] = (long)5;
+ crazy.Xtructs.Add(hello);
+
+ Dictionary<Numberz, Insanity> first_map = new Dictionary<Numberz, Insanity>();
+ Dictionary<Numberz, Insanity> second_map = new Dictionary<Numberz, Insanity>(); ;
+
+ first_map[Numberz.TWO] = crazy;
+ first_map[Numberz.THREE] = crazy;
+
+ second_map[Numberz.SIX] = looney;
+
+ Dictionary<long, Dictionary<Numberz, Insanity>> insane =
+ new Dictionary<long, Dictionary<Numberz, Insanity>>();
+ insane[(long)1] = first_map;
+ insane[(long)2] = second_map;
+
+ return insane;
+ }
+
+ public Xtruct testMulti(byte arg0, int arg1, long arg2, Dictionary<short, string> arg3, Numberz arg4, long arg5)
+ {
+ Console.WriteLine("testMulti()");
+
+ Xtruct hello = new Xtruct(); ;
+ hello.String_thing = "Hello2";
+ hello.Byte_thing = arg0;
+ hello.I32_thing = arg1;
+ hello.I64_thing = arg2;
+ return hello;
+ }
+
+ public void testException(string arg)
+ {
+ Console.WriteLine("testException(" + arg + ")");
+ if (arg == "Xception")
+ {
+ Xception x = new Xception();
+ x.ErrorCode = 1001;
+ x.Message = "This is an Xception";
+ throw x;
+ }
+ return;
+ }
+
+ public Xtruct testMultiException(string arg0, string arg1)
+ {
+ Console.WriteLine("testMultiException(" + arg0 + ", " + arg1 + ")");
+ if (arg0 == "Xception")
+ {
+ Xception x = new Xception();
+ x.ErrorCode = 1001;
+ x.Message = "This is an Xception";
+ throw x;
+ }
+ else if (arg0 == "Xception2")
+ {
+ Xception2 x = new Xception2();
+ x.ErrorCode = 2002;
+ x.Struct_thing = new Xtruct();
+ x.Struct_thing.String_thing = "This is an Xception2";
+ throw x;
+ }
+
+ Xtruct result = new Xtruct();
+ result.String_thing = arg1;
+ return result;
+ }
+
+ public void testStop()
+ {
+ if (server != null)
+ {
+ server.Stop();
+ }
+ }
+
+ public void testAsync(int arg)
+ {
+ Console.WriteLine("testAsync(" + arg + "), sleeping...");
+ System.Threading.Thread.Sleep(arg * 1000);
+ Console.WriteLine("testAsync finished");
+ }
+
+ } // class TestHandler
+
+ public static void Execute(string[] args)
+ {
+ try
+ {
+ bool useBufferedSockets = false;
+ int port = 9090;
+ if (args.Length > 0)
+ {
+ port = int.Parse(args[0]);
+
+ if (args.Length > 1)
+ {
+ bool.TryParse(args[1], out useBufferedSockets);
+ }
+ }
+
+ // Processor
+ TestHandler testHandler = new TestHandler();
+ ThriftTest.Processor testProcessor = new ThriftTest.Processor(testHandler);
+
+ // Transport
+ TServerSocket tServerSocket = new TServerSocket(port, 0, useBufferedSockets);
+
+ TServer serverEngine;
+
+ // Simple Server
+ // serverEngine = new TSimpleServer(testProcessor, tServerSocket);
+
+ // ThreadPool Server
+ serverEngine = new TThreadPoolServer(testProcessor, tServerSocket);
+
+ testHandler.server = serverEngine;
+
+ // Run it
+ Console.WriteLine("Starting the server on port " + port + (useBufferedSockets ? " with buffered socket" : "") + "...");
+ serverEngine.Serve();
+
+ }
+ catch (Exception x)
+ {
+ Console.Error.Write(x);
+ }
+ Console.WriteLine("done.");
+ }
+ }
+}
diff --git a/test/csharp/ThriftTest/ThriftTest.csproj b/test/csharp/ThriftTest/ThriftTest.csproj
new file mode 100644
index 000000000..8e61173de
--- /dev/null
+++ b/test/csharp/ThriftTest/ThriftTest.csproj
@@ -0,0 +1,122 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProductVersion>9.0.21022</ProductVersion>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{48DD757F-CA95-4DD7-BDA4-58DB6F108C2C}</ProjectGuid>
+ <OutputType>Exe</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>ThriftTest</RootNamespace>
+ <AssemblyName>ThriftTest</AssemblyName>
+ <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
+ <FileAlignment>512</FileAlignment>
+ <PublishUrl>publish\</PublishUrl>
+ <Install>true</Install>
+ <InstallFrom>Disk</InstallFrom>
+ <UpdateEnabled>false</UpdateEnabled>
+ <UpdateMode>Foreground</UpdateMode>
+ <UpdateInterval>7</UpdateInterval>
+ <UpdateIntervalUnits>Days</UpdateIntervalUnits>
+ <UpdatePeriodically>false</UpdatePeriodically>
+ <UpdateRequired>false</UpdateRequired>
+ <MapFileExtensions>true</MapFileExtensions>
+ <ApplicationRevision>0</ApplicationRevision>
+ <ApplicationVersion>1.0.0.%2a</ApplicationVersion>
+ <IsWebBootstrapper>false</IsWebBootstrapper>
+ <UseApplicationTrust>false</UseApplicationTrust>
+ <BootstrapperEnabled>true</BootstrapperEnabled>
+ <SccProjectName>SAK</SccProjectName>
+ <SccLocalPath>SAK</SccLocalPath>
+ <SccAuxPath>SAK</SccAuxPath>
+ <SccProvider>SAK</SccProvider>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="System" />
+ <Reference Include="System.Core">
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.Xml.Linq">
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.Data.DataSetExtensions">
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.Data" />
+ <Reference Include="System.Xml" />
+ <Reference Include="ThriftImpl, Version=0.0.0.0, Culture=neutral, processorArchitecture=MSIL">
+ <SpecificVersion>False</SpecificVersion>
+ <HintPath>.\ThriftImpl.dll</HintPath>
+ </Reference>
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="Program.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ <Compile Include="TestClient.cs" />
+ <Compile Include="TestServer.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <BootstrapperPackage Include="Microsoft.Net.Framework.2.0">
+ <Visible>False</Visible>
+ <ProductName>.NET Framework 2.0 %28x86%29</ProductName>
+ <Install>false</Install>
+ </BootstrapperPackage>
+ <BootstrapperPackage Include="Microsoft.Net.Framework.3.0">
+ <Visible>False</Visible>
+ <ProductName>.NET Framework 3.0 %28x86%29</ProductName>
+ <Install>false</Install>
+ </BootstrapperPackage>
+ <BootstrapperPackage Include="Microsoft.Net.Framework.3.5">
+ <Visible>False</Visible>
+ <ProductName>.NET Framework 3.5</ProductName>
+ <Install>true</Install>
+ </BootstrapperPackage>
+ <BootstrapperPackage Include="Microsoft.Windows.Installer.3.1">
+ <Visible>False</Visible>
+ <ProductName>Windows Installer 3.1</ProductName>
+ <Install>true</Install>
+ </BootstrapperPackage>
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\..\..\lib\csharp\src\Thrift.csproj">
+ <Project>{499EB63C-D74C-47E8-AE48-A2FC94538E9D}</Project>
+ <Name>Thrift</Name>
+ </ProjectReference>
+ </ItemGroup>
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
+ Other similar extension points exist, see Microsoft.Common.targets.
+ <Target Name="BeforeBuild">
+ </Target>
+ <Target Name="AfterBuild">
+ </Target>
+ -->
+ <PropertyGroup>
+ <PreBuildEvent>rmdir /s /q $(ProjectDir)gen-csharp
+del /f /q $(ProjectDir)ThriftImpl.dll
+
+$(ProjectDir)\..\..\..\compiler\cpp\thrift.exe -csharp -o $(ProjectDir) $(ProjectDir)\..\..\ThriftTest.thrift
+
+cd $(ProjectDir)
+
+C:\Windows\Microsoft.NET\Framework\v3.5\Csc.exe /t:library /out:.\ThriftImpl.dll /recurse:.\gen-csharp\* /reference:$(ProjectDir)..\..\..\lib\csharp\src\bin\Debug\Thrift.dll</PreBuildEvent>
+ </PropertyGroup>
+</Project>