summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJiayu Liu <jiayu.liu@airbnb.com>2022-04-12 22:42:10 +0800
committerJens Geyer <Jens-G@users.noreply.github.com>2022-04-13 21:49:21 +0200
commitaa82214dbc1ee981040719067a6d4eb7791fe7ec (patch)
treed4f6cbe4ace2e8cf64ea27d9f2690fae354d011a
parent2f6ddc91456aaa18e017ba1aa95dbd5f98525fce (diff)
downloadthrift-aa82214dbc1ee981040719067a6d4eb7791fe7ec.tar.gz
add interface generation
-rw-r--r--compiler/cpp/src/thrift/generate/t_java_generator.cc90
-rw-r--r--lib/java/gradle/generateTestThrift.gradle4
-rw-r--r--lib/java/src/org/apache/thrift/async/AsyncMethodFutureAdapter.java35
3 files changed, 127 insertions, 2 deletions
diff --git a/compiler/cpp/src/thrift/generate/t_java_generator.cc b/compiler/cpp/src/thrift/generate/t_java_generator.cc
index 87ec49079..d2be702fc 100644
--- a/compiler/cpp/src/thrift/generate/t_java_generator.cc
+++ b/compiler/cpp/src/thrift/generate/t_java_generator.cc
@@ -69,6 +69,7 @@ public:
java5_ = false;
reuse_objects_ = false;
use_option_type_ = false;
+ generate_future_iface_ = false;
undated_generated_annotations_ = false;
suppress_generated_annotations_ = false;
rethrow_unhandled_exceptions_ = false;
@@ -92,6 +93,8 @@ public:
sorted_containers_ = true;
} else if (iter->first.compare("java5") == 0) {
java5_ = true;
+ } else if (iter->first.compare("future_iface") == 0) {
+ generate_future_iface_ = true;
} else if (iter->first.compare("reuse_objects") == 0
|| iter->first.compare("reuse-objects") == 0) {
// keep both reuse_objects and reuse-objects (legacy) for backwards compatibility
@@ -199,9 +202,11 @@ public:
void generate_service_interface(t_service* tservice);
void generate_service_async_interface(t_service* tservice);
+ void generate_service_future_interface(t_service* tservice);
void generate_service_helpers(t_service* tservice);
void generate_service_client(t_service* tservice);
void generate_service_async_client(t_service* tservice);
+ void generate_service_future_client(t_service* tservice);
void generate_service_server(t_service* tservice);
void generate_service_async_server(t_service* tservice);
void generate_process_function(t_service* tservice, t_function* tfunction);
@@ -329,6 +334,7 @@ public:
std::string function_signature_async(t_function* tfunction,
bool use_base_method = false,
std::string prefix = "");
+ std::string function_signature_future(t_function* tfunction, std::string prefix = "");
std::string argument_list(t_struct* tstruct, bool include_types = true);
std::string async_function_call_arglist(t_function* tfunc,
bool use_base_method = true,
@@ -414,6 +420,7 @@ private:
bool java5_;
bool sorted_containers_;
bool reuse_objects_;
+ bool generate_future_iface_;
bool use_option_type_;
bool undated_generated_annotations_;
bool suppress_generated_annotations_;
@@ -2904,8 +2911,14 @@ void t_java_generator::generate_service(t_service* tservice) {
// Generate the three main parts of the service
generate_service_interface(tservice);
generate_service_async_interface(tservice);
+ if (generate_future_iface_) {
+ generate_service_future_interface(tservice);
+ }
generate_service_client(tservice);
generate_service_async_client(tservice);
+ if (generate_future_iface_) {
+ generate_service_future_client(tservice);
+ }
generate_service_server(tservice);
generate_service_async_server(tservice);
generate_service_helpers(tservice);
@@ -2962,6 +2975,25 @@ void t_java_generator::generate_service_async_interface(t_service* tservice) {
f_service_ << indent() << "}" << endl << endl;
}
+void t_java_generator::generate_service_future_interface(t_service* tservice) {
+ string extends = "";
+ string extends_iface = "";
+ if (tservice->get_extends() != nullptr) {
+ extends = type_name(tservice->get_extends());
+ extends_iface = " extends " + extends + " .FutureIface";
+ }
+
+ f_service_ << indent() << "public interface FutureIface" << extends_iface << " {" << endl << endl;
+ indent_up();
+ for (auto tfunc : tservice->get_functions()) {
+ indent(f_service_) << "public " << function_signature_future(tfunc)
+ << " throws org.apache.thrift.TException;" << endl
+ << endl;
+ }
+ scope_down(f_service_);
+ f_service_ << endl << endl;
+}
+
/**
* Generates structs for all the service args and return types
*
@@ -3146,6 +3178,50 @@ void t_java_generator::generate_service_client(t_service* tservice) {
indent(f_service_) << "}" << endl;
}
+void t_java_generator::generate_service_future_client(t_service* tservice) {
+ static string adapter_class = "org.apache.thrift.async.AsyncMethodFutureAdapter";
+ indent(f_service_) << "public static class FutureClient implements FutureIface {" << endl;
+ indent_up();
+ indent(f_service_) << "public FutureClient(AsyncIface delegate) {" << endl;
+ indent_up();
+ indent(f_service_) << "this.delegate = delegate;" << endl;
+ scope_down(f_service_);
+ indent(f_service_) << "private final AsyncIface delegate;" << endl;
+ for (auto tfunc : tservice->get_functions()) {
+ string funname = tfunc->get_name();
+ string sep = "_";
+ string javaname = funname;
+ if (fullcamel_style_) {
+ sep = "";
+ javaname = as_camel_case(javaname);
+ }
+ auto ret_type_name = type_name(tfunc->get_returntype(), /*in_container=*/true);
+ t_struct* arg_struct = tfunc->get_arglist();
+ string funclassname = funname + "_call";
+ auto fields = arg_struct->get_members();
+
+ string args_name = funname + "_args";
+ string result_name = funname + "_result";
+
+ indent(f_service_) << "@Override" << endl;
+ indent(f_service_) << "public " << function_signature_future(tfunc)
+ << " throws org.apache.thrift.TException {" << endl;
+ indent_up();
+ auto adapter = tmp("asyncMethodFutureAdapter");
+ indent(f_service_) << adapter_class << "<" << ret_type_name << "> " << adapter << " = "
+ << adapter_class << ".<" << ret_type_name << ">create();" << endl;
+ bool empty_args = tfunc->get_arglist()->get_members().empty();
+ indent(f_service_) << "delegate." << get_rpc_method_name(funname) << "("
+ << argument_list(tfunc->get_arglist(), false) << (empty_args ? "" : ", ")
+ << adapter << ");" << endl;
+ indent(f_service_) << "return " << adapter << ".getFuture();" << endl;
+ scope_down(f_service_);
+ f_service_ << endl;
+ }
+ scope_down(f_service_);
+ f_service_ << endl;
+}
+
void t_java_generator::generate_service_async_client(t_service* tservice) {
string extends = "org.apache.thrift.async.TAsyncClient";
string extends_client = "";
@@ -4518,6 +4594,19 @@ string t_java_generator::function_signature_async(t_function* tfunction,
return result;
}
+/**
+ * Renders a function signature of the form 'CompletableFuture<R> name(args)'
+ *
+ * @params tfunction Function definition
+ * @return String of rendered function definition
+ */
+string t_java_generator::function_signature_future(t_function* tfunction, string prefix) {
+ t_type* ttype = tfunction->get_returntype();
+ std::string fn_name = get_rpc_method_name(tfunction->get_name());
+ return "java.util.concurrent.CompletableFuture<" + type_name(ttype, /*in_container=*/true) + "> "
+ + prefix + fn_name + "(" + argument_list(tfunction->get_arglist()) + ")";
+}
+
string t_java_generator::async_function_call_arglist(t_function* tfunc,
bool use_base_method,
bool include_types) {
@@ -5534,6 +5623,7 @@ THRIFT_REGISTER_GENERATOR(
" Enable rethrow of unhandled exceptions and let them propagate further."
" (Default behavior is to catch and log it.)\n"
" java5: Generate Java 1.5 compliant code (includes android_legacy flag).\n"
+ " future_iface: Generate CompletableFuture based iface based on async client.\n"
" reuse_objects: Data objects will not be allocated, but existing instances will be used "
"(read and write).\n"
" reuse-objects: Same as 'reuse_objects' (deprecated).\n"
diff --git a/lib/java/gradle/generateTestThrift.gradle b/lib/java/gradle/generateTestThrift.gradle
index 924dd0d0f..d5bc3afaf 100644
--- a/lib/java/gradle/generateTestThrift.gradle
+++ b/lib/java/gradle/generateTestThrift.gradle
@@ -91,7 +91,7 @@ task generateBeanJava(group: 'Build') {
ext.outputBuffer = new ByteArrayOutputStream()
- thriftCompile(it, 'JavaBeansTest.thrift', 'java:beans,nocamel', genBeanSrc)
+ thriftCompile(it, 'JavaBeansTest.thrift', 'java:beans,nocamel,future_iface', genBeanSrc)
}
task generateReuseJava(group: 'Build') {
@@ -100,7 +100,7 @@ task generateReuseJava(group: 'Build') {
ext.outputBuffer = new ByteArrayOutputStream()
- thriftCompile(it, 'FullCamelTest.thrift', 'java:fullcamel', genFullCamelSrc)
+ thriftCompile(it, 'FullCamelTest.thrift', 'java:fullcamel,future_iface', genFullCamelSrc)
}
task generateFullCamelJava(group: 'Build') {
diff --git a/lib/java/src/org/apache/thrift/async/AsyncMethodFutureAdapter.java b/lib/java/src/org/apache/thrift/async/AsyncMethodFutureAdapter.java
new file mode 100644
index 000000000..0bee3a7cf
--- /dev/null
+++ b/lib/java/src/org/apache/thrift/async/AsyncMethodFutureAdapter.java
@@ -0,0 +1,35 @@
+package org.apache.thrift.async;
+
+import java.util.concurrent.CompletableFuture;
+
+/**
+ * A simple adapter that bridges {@link AsyncMethodCallback} with {@link CompletableFuture}-returning style clients.
+ * Compiler generated code will invoke this adapter to implement {@code FutureClient}s.
+ *
+ * @param <T> return type (can be {@link Void}).
+ */
+public final class AsyncMethodFutureAdapter<T> implements AsyncMethodCallback<T> {
+
+ private AsyncMethodFutureAdapter() {
+ }
+
+ public static <T> AsyncMethodFutureAdapter<T> create() {
+ return new AsyncMethodFutureAdapter<>();
+ }
+
+ private final CompletableFuture<T> future = new CompletableFuture<>();
+
+ public CompletableFuture<T> getFuture() {
+ return future;
+ }
+
+ @Override
+ public void onComplete(T response) {
+ future.complete(response);
+ }
+
+ @Override
+ public void onError(Exception exception) {
+ future.completeExceptionally(exception);
+ }
+}