diff options
-rw-r--r-- | compiler/cpp/src/thrift/generate/t_java_generator.cc | 90 | ||||
-rw-r--r-- | lib/java/gradle/generateTestThrift.gradle | 4 | ||||
-rw-r--r-- | lib/java/src/org/apache/thrift/async/AsyncMethodFutureAdapter.java | 35 |
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); + } +} |