THRIFT-1805 Provide option for handling RTEs
Client: Java

Adds a Java option to the generator to generate code which lets Thrift
handle RuntimeExceptions from a service, and present them as
TApplicationException to the client.

This closes #1186
diff --git a/compiler/cpp/src/thrift/generate/t_java_generator.cc b/compiler/cpp/src/thrift/generate/t_java_generator.cc
index b732631..593eda6 100644
--- a/compiler/cpp/src/thrift/generate/t_java_generator.cc
+++ b/compiler/cpp/src/thrift/generate/t_java_generator.cc
@@ -70,6 +70,7 @@
     use_option_type_ = false;
     undated_generated_annotations_  = false;
     suppress_generated_annotations_ = false;
+    handle_runtime_exceptions_ = false;
     for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) {
       if( iter->first.compare("beans") == 0) {
         bean_style_ = true;
@@ -91,6 +92,8 @@
         reuse_objects_ = true;
       } else if( iter->first.compare("option_type") == 0) {
         use_option_type_ = true;
+      } else if( iter->first.compare("handle_runtime_exceptions") == 0) {
+        handle_runtime_exceptions_ = true;
       } else if( iter->first.compare("generated_annotations") == 0) {
         if( iter->second.compare("undated") == 0) {
           undated_generated_annotations_  = true;
@@ -367,6 +370,7 @@
   bool use_option_type_;
   bool undated_generated_annotations_;
   bool suppress_generated_annotations_;
+  bool handle_runtime_exceptions_;
 
 };
 
@@ -3527,6 +3531,11 @@
   indent(f_service_) << "  return " << ((tfunction->is_oneway()) ? "true" : "false") << ";" << endl;
   indent(f_service_) << "}" << endl << endl;
 
+  indent(f_service_) << "@Override" << endl;
+  indent(f_service_) << "protected boolean handleRuntimeExceptions() {" << endl;
+  indent(f_service_) << "  return " << ((handle_runtime_exceptions_) ? "true" : "false") << ";" << endl;
+  indent(f_service_) << "}" << endl << endl;
+
   indent(f_service_) << "public " << resultname << " getResult(I iface, " << argsname
                      << " args) throws org.apache.thrift.TException {" << endl;
   indent_up();
@@ -5265,6 +5274,9 @@
     "    android_legacy:  Do not use java.io.IOException(throwable) (available for Android 2.3 and "
     "above).\n"
     "    option_type:     Wrap optional fields in an Option type.\n"
+    "    handle_runtime_exceptions:\n"
+    "                     Send TApplicationException to the client when RuntimeException occurs on "
+    "the server. (Default behavior is to close the connection instead.)\n"
     "    java5:           Generate Java 1.5 compliant code (includes android_legacy flag).\n"
     "    reuse-objects:   Data objects will not be allocated, but existing instances will be used "
     "(read and write).\n"
diff --git a/lib/java/src/org/apache/thrift/ProcessFunction.java b/lib/java/src/org/apache/thrift/ProcessFunction.java
index 992e859..5c039fe 100644
--- a/lib/java/src/org/apache/thrift/ProcessFunction.java
+++ b/lib/java/src/org/apache/thrift/ProcessFunction.java
@@ -1,6 +1,3 @@
-/**
- * 
- */
 package org.apache.thrift;
 
 import org.apache.thrift.protocol.TMessage;
@@ -39,13 +36,12 @@
       result = getResult(iface, args);
     } catch(TException tex) {
       LOGGER.error("Internal error processing " + getMethodName(), tex);
-      if (!isOneway()) {
-        TApplicationException x = new TApplicationException(TApplicationException.INTERNAL_ERROR, 
-          "Internal error processing " + getMethodName());
-        oprot.writeMessageBegin(new TMessage(getMethodName(), TMessageType.EXCEPTION, seqid));
-        x.write(oprot);
-        oprot.writeMessageEnd();
-        oprot.getTransport().flush();
+      handleException(seqid, oprot);
+      return;
+    } catch(RuntimeException rex) {
+      LOGGER.error("Internal error processing " + getMethodName(), rex);
+      if (handleRuntimeExceptions()) {
+        handleException(seqid, oprot);
       }
       return;
     }
@@ -58,6 +54,21 @@
     }
   }
 
+  private void handleException(int seqid, TProtocol oprot) throws TException {
+    if (!isOneway()) {
+      TApplicationException x = new TApplicationException(TApplicationException.INTERNAL_ERROR,
+        "Internal error processing " + getMethodName());
+      oprot.writeMessageBegin(new TMessage(getMethodName(), TMessageType.EXCEPTION, seqid));
+      x.write(oprot);
+      oprot.writeMessageEnd();
+      oprot.getTransport().flush();
+    }
+  }
+
+  protected boolean handleRuntimeExceptions() {
+    return false;
+  }
+
   protected abstract boolean isOneway();
 
   public abstract TBase getResult(I iface, T args) throws TException;