summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Kormukhin <a.kormukhin.github@yandex.ru>2022-02-18 21:04:14 +0300
committerJens Geyer <Jens-G@users.noreply.github.com>2022-03-02 22:14:55 +0100
commitc9b7bd7b1285e8feb902daa208eacd7655a380ee (patch)
treeaf1db4cfe5c81b8b1eec4fa4d0be81fe0c5ed64f
parent8ab86c3303a8157ecfed6ff588d71e6e13dd7017 (diff)
downloadthrift-c9b7bd7b1285e8feb902daa208eacd7655a380ee.tar.gz
THRIFT-5519 Java async client loses exceptions in void methods
Client: java (cherry picked from commit c4d3e36ed4db97fb6213cc13a4e611a4e658b4b7)
-rw-r--r--compiler/cpp/src/thrift/generate/t_java_generator.cc4
-rw-r--r--lib/java/gradle/generateTestThrift.gradle1
-rw-r--r--lib/java/test/org/apache/thrift/test/voidmethexceptions/ServiceAsyncImp.java85
-rw-r--r--lib/java/test/org/apache/thrift/test/voidmethexceptions/ServiceBase.java44
-rw-r--r--lib/java/test/org/apache/thrift/test/voidmethexceptions/ServiceSyncImp.java72
-rw-r--r--lib/java/test/org/apache/thrift/test/voidmethexceptions/TestVoidMethExceptions.java549
-rwxr-xr-xtest/Makefile.am1
-rw-r--r--test/VoidMethExceptionsTest.thrift13
8 files changed, 769 insertions, 0 deletions
diff --git a/compiler/cpp/src/thrift/generate/t_java_generator.cc b/compiler/cpp/src/thrift/generate/t_java_generator.cc
index ceabe6687..3fa5f5783 100644
--- a/compiler/cpp/src/thrift/generate/t_java_generator.cc
+++ b/compiler/cpp/src/thrift/generate/t_java_generator.cc
@@ -3250,6 +3250,10 @@ void t_java_generator::generate_service_async_client(t_service* tservice) {
"client.getProtocolFactory().getProtocol(memoryTransport);" << endl;
indent(f_service_);
if (ret_type->is_void()) { // NB: Includes oneways which always return void.
+ if (!(*f_iter)->is_oneway()) {
+ f_service_ << "(new Client(prot)).recv" + sep + javaname + "();" << endl;
+ indent(f_service_);
+ }
f_service_ << "return null;" << endl;
} else {
f_service_ << "return (new Client(prot)).recv" + sep + javaname + "();" << endl;
diff --git a/lib/java/gradle/generateTestThrift.gradle b/lib/java/gradle/generateTestThrift.gradle
index 4b712ca23..996c5fa10 100644
--- a/lib/java/gradle/generateTestThrift.gradle
+++ b/lib/java/gradle/generateTestThrift.gradle
@@ -81,6 +81,7 @@ task generateJava(group: 'Build') {
thriftCompile(it, 'JavaDeepCopyTest.thrift')
thriftCompile(it, 'EnumContainersTest.thrift')
thriftCompile(it, 'JavaBinaryDefault.thrift')
+ thriftCompile(it, 'VoidMethExceptionsTest.thrift')
thriftCompile(it, 'partial/thrift_test_schema.thrift')
}
diff --git a/lib/java/test/org/apache/thrift/test/voidmethexceptions/ServiceAsyncImp.java b/lib/java/test/org/apache/thrift/test/voidmethexceptions/ServiceAsyncImp.java
new file mode 100644
index 000000000..419e327de
--- /dev/null
+++ b/lib/java/test/org/apache/thrift/test/voidmethexceptions/ServiceAsyncImp.java
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.test.voidmethexceptions;
+
+import org.apache.thrift.TApplicationException;
+import org.apache.thrift.TException;
+import org.apache.thrift.async.AsyncMethodCallback;
+import thrift.test.voidmethexceptions.TAppService01;
+import thrift.test.voidmethexceptions.TExampleException;
+
+public class ServiceAsyncImp extends ServiceBase implements TAppService01.AsyncIface {
+
+ @Override
+ public void returnString(String msg,
+ boolean throwException,
+ AsyncMethodCallback<String> resultHandler) throws TException {
+ if (throwException) {
+ resultHandler.onError(new TExampleException(msg));
+ } else {
+ resultHandler.onComplete(msg);
+ }
+ }
+
+ @Override
+ public void returnVoidThrows(String msg,
+ boolean throwException,
+ AsyncMethodCallback<Void> resultHandler) throws TException {
+ if (throwException) {
+ resultHandler.onError(new TExampleException(msg));
+ } else {
+ resultHandler.onComplete(null);
+ }
+ }
+
+ @Override
+ public void returnVoidNoThrowsRuntimeException(String msg,
+ boolean throwException,
+ AsyncMethodCallback<Void> resultHandler) throws TException {
+ if (throwException) {
+ resultHandler.onError(new RuntimeException(msg));
+ } else {
+ resultHandler.onComplete(null);
+ }
+ }
+
+ @Override
+ public void returnVoidNoThrowsTApplicationException(String msg,
+ boolean throwException,
+ AsyncMethodCallback<Void> resultHandler) throws TException {
+ if (throwException) {
+ resultHandler.onError(new TApplicationException(TApplicationException.INTERNAL_ERROR, msg));
+ } else {
+ resultHandler.onComplete(null);
+ }
+ }
+
+ @Override
+ public void onewayVoidNoThrows(String msg,
+ boolean throwException,
+ AsyncMethodCallback<Void> resultHandler) throws TException {
+ if (throwException) {
+ resultHandler.onError(new TApplicationException(TApplicationException.INTERNAL_ERROR, msg));
+ } else {
+ // simulate hang up
+ }
+ }
+
+}
diff --git a/lib/java/test/org/apache/thrift/test/voidmethexceptions/ServiceBase.java b/lib/java/test/org/apache/thrift/test/voidmethexceptions/ServiceBase.java
new file mode 100644
index 000000000..5502b09cd
--- /dev/null
+++ b/lib/java/test/org/apache/thrift/test/voidmethexceptions/ServiceBase.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.test.voidmethexceptions;
+
+public class ServiceBase {
+
+ private volatile boolean cancelled = false;
+
+ public boolean isCancelled() {
+ return cancelled;
+ }
+
+ public void setCancelled(boolean cancelled) {
+ this.cancelled = cancelled;
+ }
+
+ protected void waitForCancel() {
+ while (!isCancelled()) {
+ try {
+ Thread.sleep(10L);
+ } catch (InterruptedException x) {
+ break;
+ }
+ }
+ }
+
+}
diff --git a/lib/java/test/org/apache/thrift/test/voidmethexceptions/ServiceSyncImp.java b/lib/java/test/org/apache/thrift/test/voidmethexceptions/ServiceSyncImp.java
new file mode 100644
index 000000000..a8a08b5b9
--- /dev/null
+++ b/lib/java/test/org/apache/thrift/test/voidmethexceptions/ServiceSyncImp.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.test.voidmethexceptions;
+
+import org.apache.thrift.TApplicationException;
+import org.apache.thrift.TException;
+import thrift.test.voidmethexceptions.TAppService01;
+import thrift.test.voidmethexceptions.TExampleException;
+
+public class ServiceSyncImp extends ServiceBase implements TAppService01.Iface {
+
+ @Override
+ public String returnString(String msg,
+ boolean throwException) throws TExampleException, TException {
+ if (throwException) {
+ throw new TExampleException(msg);
+ }
+ return msg;
+ }
+
+ @Override
+ public void returnVoidThrows(String msg,
+ boolean throwException) throws TExampleException, TException {
+ if (throwException) {
+ throw new TExampleException(msg);
+ }
+ }
+
+ @Override
+ public void returnVoidNoThrowsRuntimeException(String msg,
+ boolean throwException) throws TException {
+ if (throwException) {
+ throw new RuntimeException(msg);
+ }
+ }
+
+ @Override
+ public void returnVoidNoThrowsTApplicationException(String msg,
+ boolean throwException) throws TException {
+ if (throwException) {
+ throw new TApplicationException(TApplicationException.INTERNAL_ERROR, msg);
+ }
+ }
+
+ @Override
+ public void onewayVoidNoThrows(String msg, boolean throwException) throws TException {
+ if (throwException) {
+ throw new TApplicationException(TApplicationException.INTERNAL_ERROR, msg);
+ } else {
+ // simulate hang up
+ waitForCancel();
+ }
+ }
+
+}
diff --git a/lib/java/test/org/apache/thrift/test/voidmethexceptions/TestVoidMethExceptions.java b/lib/java/test/org/apache/thrift/test/voidmethexceptions/TestVoidMethExceptions.java
new file mode 100644
index 000000000..af39262bb
--- /dev/null
+++ b/lib/java/test/org/apache/thrift/test/voidmethexceptions/TestVoidMethExceptions.java
@@ -0,0 +1,549 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.test.voidmethexceptions;
+
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.thrift.TApplicationException;
+import org.apache.thrift.TConfiguration;
+import org.apache.thrift.TProcessor;
+import org.apache.thrift.async.AsyncMethodCallback;
+import org.apache.thrift.async.TAsyncClientManager;
+import org.apache.thrift.protocol.TBinaryProtocol;
+import org.apache.thrift.server.TNonblockingServer;
+import org.apache.thrift.server.TServer;
+import org.apache.thrift.transport.TNonblockingServerSocket;
+import org.apache.thrift.transport.TNonblockingSocket;
+import org.apache.thrift.transport.TSocket;
+import org.apache.thrift.transport.TTransport;
+import org.apache.thrift.transport.layered.TFramedTransport;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import thrift.test.voidmethexceptions.TAppService01;
+import thrift.test.voidmethexceptions.TExampleException;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Supplier;
+
+@RunWith(Parameterized.class)
+public class TestVoidMethExceptions {
+
+ private static final Logger log = LoggerFactory.getLogger(TestVoidMethExceptions.class);
+
+ private static final int TIMEOUT_MILLIS = 5_000;
+
+ private final ServerImplementationType serverImplementationType;
+
+ private TServer server;
+ private Thread serverThread;
+ private int serverPort;
+
+
+ public TestVoidMethExceptions(ServerImplementationType serverImplementationType) {
+ Assert.assertNotNull(serverImplementationType);
+ this.serverImplementationType = serverImplementationType;
+ }
+
+
+ @Parameters(name = "serverImplementationType = {0}")
+ public static Object[][] parameters() {
+ return new Object[][]{{ServerImplementationType.SYNC_SERVER},
+ {ServerImplementationType.ASYNC_SERVER}};
+ }
+
+
+ @Before
+ public void setUp() throws Exception {
+ serverPort = -1;
+ serverImplementationType.service.setCancelled(false);
+ CompletableFuture<Void> futureServerStarted = new CompletableFuture<>();
+ TNonblockingServerSocket serverTransport = new TNonblockingServerSocket(0);
+ TNonblockingServer.Args args = new TNonblockingServer.Args(serverTransport);
+ args.processor(serverImplementationType.processor);
+ server = new TNonblockingServer(args) {
+
+ @Override
+ protected void setServing(boolean serving) {
+ super.setServing(serving);
+
+ if (serving) {
+ serverPort = serverTransport.getPort();
+ futureServerStarted.complete(null);
+ }
+ }
+
+ };
+
+ serverThread = new Thread(() -> {
+ server.serve();
+ }, "thrift-server");
+ serverThread.setDaemon(true);
+ serverThread.start();
+ futureServerStarted.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ serverImplementationType.service.setCancelled(true);
+ server.stop();
+ serverThread.join(TIMEOUT_MILLIS);
+ }
+
+
+ @Test
+ public void testSyncClientMustReturnResultReturnString() throws Exception {
+ checkSyncClient("returnString",
+ "sent msg",
+ false,
+ "sent msg",
+ null,
+ null,
+ (client, msg, throwException) -> {
+ return client.returnString(msg, throwException);
+ });
+ }
+
+ @Test
+ public void testSyncClientMustReturnResultReturnVoidThrows() throws Exception {
+ checkSyncClient("returnVoidThrows",
+ "sent msg",
+ false,
+ null,
+ null,
+ null,
+ (client, msg, throwException) -> {
+ client.returnVoidThrows(msg, throwException);
+ return null;
+ });
+ }
+
+ @Test
+ public void testSyncClientMustReturnResultReturnVoidNoThrowsRuntimeException() throws Exception {
+ checkSyncClient("returnVoidNoThrowsRuntimeException",
+ "sent msg",
+ false,
+ null,
+ null,
+ null,
+ (client, msg, throwException) -> {
+ client.returnVoidNoThrowsRuntimeException(msg, throwException);
+ return null;
+ });
+ }
+
+ @Test
+ public void testSyncClientMustReturnResultReturnVoidNoThrowsTApplicationException() throws Exception {
+ checkSyncClient("returnVoidNoThrowsTApplicationException",
+ "sent msg",
+ false,
+ null,
+ null,
+ null,
+ (client, msg, throwException) -> {
+ client.returnVoidNoThrowsTApplicationException(msg, throwException);
+ return null;
+ });
+ }
+
+
+ @Test
+ public void testSyncClientMustThrowExceptionReturnString() throws Exception {
+ checkSyncClient("returnString",
+ "sent msg",
+ true,
+ null,
+ TExampleException.class,
+ "sent msg",
+ (client, msg, throwException) -> {
+ return client.returnString(msg, throwException);
+ });
+ }
+
+ @Test
+ public void testSyncClientMustThrowExceptionReturnVoidThrows() throws Exception {
+ checkSyncClient("returnVoidThrows",
+ "sent msg",
+ true,
+ null,
+ TExampleException.class,
+ "sent msg",
+ (client, msg, throwException) -> {
+ client.returnVoidThrows(msg, throwException);
+ return null;
+ });
+ }
+
+ @Test
+ public void testSyncClientMustThrowExceptionReturnVoidNoThrowsRuntimeException() throws Exception {
+ checkSyncClient("returnVoidNoThrowsRuntimeException",
+ "sent msg",
+ true,
+ null,
+ TApplicationException.class,
+ serverImplementationType == ServerImplementationType.ASYNC_SERVER ? "sent msg"
+ : null, // sync server return "Internal error processing returnVoidNoThrowsRuntimeException" message
+ (client, msg, throwException) -> {
+ client.returnVoidNoThrowsRuntimeException(msg, throwException);
+ return null;
+ });
+ }
+
+ @Test
+ public void testSyncClientMustThrowExceptionReturnVoidNoThrowsTApplicationException() throws Exception {
+ checkSyncClient("returnVoidNoThrowsTApplicationException",
+ "sent msg",
+ true,
+ null,
+ TApplicationException.class,
+ "sent msg",
+ (client, msg, throwException) -> {
+ client.returnVoidNoThrowsTApplicationException(msg, throwException);
+ return null;
+ });
+ }
+
+
+ @Test
+ public void testAsyncClientMustReturnResultReturnString() throws Throwable {
+ checkAsyncClient("returnString",
+ "sent msg",
+ false,
+ "sent msg",
+ null,
+ null,
+ (client, msg, throwException, resultHandler) -> {
+ client.returnString(msg, throwException, resultHandler);
+ });
+ }
+
+ @Test
+ public void testAsyncClientMustReturnResultReturnVoidThrows() throws Throwable {
+ checkAsyncClient("returnVoidThrows",
+ "sent msg",
+ false,
+ (Void) null,
+ null,
+ null,
+ (client, msg, throwException, resultHandler) -> {
+ client.returnVoidThrows(msg, throwException, resultHandler);
+ });
+ }
+
+ @Test
+ public void testAsyncClientMustReturnResultReturnVoidNoThrowsRuntimeException() throws Throwable {
+ checkAsyncClient("returnVoidNoThrowsRuntimeException",
+ "sent msg",
+ false,
+ (Void) null,
+ null,
+ null,
+ (client, msg, throwException, resultHandler) -> {
+ client.returnVoidNoThrowsRuntimeException(msg, throwException, resultHandler);
+ });
+ }
+
+ @Test
+ public void testAsyncClientMustReturnResultReturnVoidNoThrowsTApplicationException() throws Throwable {
+ checkAsyncClient("returnVoidNoThrowsTApplicationException",
+ "sent msg",
+ false,
+ (Void) null,
+ null,
+ null,
+ (client, msg, throwException, resultHandler) -> {
+ client.returnVoidNoThrowsTApplicationException(msg, throwException, resultHandler);
+ });
+ }
+
+
+ @Test
+ public void testAsyncClientMustThrowExceptionReturnString() throws Throwable {
+ checkAsyncClient("returnString",
+ "sent msg",
+ true,
+ (String) null,
+ TExampleException.class,
+ "sent msg",
+ (client, msg, throwException, resultHandler) -> {
+ client.returnString(msg, throwException, resultHandler);
+ });
+ }
+
+ @Test
+ public void testAsyncClientMustThrowExceptionReturnVoidThrows() throws Throwable {
+ checkAsyncClient("returnVoidThrows",
+ "sent msg",
+ true,
+ (Void) null,
+ TExampleException.class,
+ "sent msg",
+ (client, msg, throwException, resultHandler) -> {
+ client.returnVoidThrows(msg, throwException, resultHandler);
+ });
+ }
+
+ @Test
+ public void testAsyncClientMustThrowExceptionReturnVoidNoThrowsRuntimeException() throws Throwable {
+ checkAsyncClient("returnVoidNoThrowsRuntimeException",
+ "sent msg",
+ true,
+ (Void) null,
+ TApplicationException.class,
+ serverImplementationType == ServerImplementationType.ASYNC_SERVER ? "sent msg"
+ : null, // sync server return "Internal error processing returnVoidNoThrowsRuntimeException" message
+ (client, msg, throwException, resultHandler) -> {
+ client.returnVoidNoThrowsRuntimeException(msg, throwException, resultHandler);
+ });
+ }
+
+ @Test
+ public void testAsyncClientMustThrowExceptionReturnVoidNoThrowsTApplicationException() throws Throwable {
+ checkAsyncClient("returnVoidNoThrowsTApplicationException",
+ "sent msg",
+ true,
+ (Void) null,
+ TApplicationException.class,
+ "sent msg",
+ (client, msg, throwException, resultHandler) -> {
+ client.returnVoidNoThrowsTApplicationException(msg, throwException, resultHandler);
+ });
+ }
+
+
+ @Test
+ public void testSyncClientNoWaitForResultNoExceptionOnewayVoidNoThrows() throws Exception {
+ checkSyncClient("onewayVoidNoThrows",
+ "sent msg",
+ false,
+ null,
+ null,
+ null,
+ (client, msg, throwException) -> {
+ client.onewayVoidNoThrows(msg, throwException);
+ return null;
+ });
+ }
+
+ @Test
+ public void testSyncClientNoWaitForResultExceptionOnewayVoidNoThrows() throws Exception {
+ checkSyncClient("onewayVoidNoThrows",
+ "sent msg",
+ true,
+ null,
+ null,
+ null,
+ (client, msg, throwException) -> {
+ client.onewayVoidNoThrows(msg, throwException);
+ return null;
+ });
+ }
+
+ @Test
+ public void testAsyncClientNoWaitForResultNoExceptionOnewayVoidNoThrows() throws Throwable {
+ checkAsyncClient("onewayVoidNoThrows",
+ "sent msg",
+ false,
+ (Void) null,
+ null,
+ null,
+ (client, msg, throwException, resultHandler) -> {
+ client.onewayVoidNoThrows(msg, throwException, resultHandler);
+ });
+ }
+
+ @Test
+ public void testAsyncClientNoWaitForResultExceptionOnewayVoidNoThrows() throws Throwable {
+ checkAsyncClient("onewayVoidNoThrows",
+ "sent msg",
+ true,
+ (Void) null,
+ null,
+ null,
+ (client, msg, throwException, resultHandler) -> {
+ client.onewayVoidNoThrows(msg, throwException, resultHandler);
+ });
+ }
+
+
+ private void checkSyncClient(String desc,
+ String msg,
+ boolean throwException,
+ String expectedResult,
+ Class<?> expectedExceptionClass,
+ String expectedExceptionMsg,
+ SyncCall<TAppService01.Iface, String, Boolean, String> call) throws Exception {
+ if (log.isInfoEnabled()) {
+ log.info("start test checkSyncClient::" + desc + ", throwException: " + throwException
+ + ", serverImplementationType: "
+ + serverImplementationType);
+ }
+ Assert.assertNotEquals(-1, serverPort);
+ try (TTransport clientTransport = new TFramedTransport(new TSocket(new TConfiguration(),
+ "localhost",
+ serverPort,
+ TIMEOUT_MILLIS))) {
+ clientTransport.open();
+ TAppService01.Iface client = new TAppService01.Client(new TBinaryProtocol(clientTransport));
+
+ try {
+
+ String result = call.apply(client, msg, throwException);
+
+ if (throwException && expectedExceptionClass != null) {
+ Assert.fail("No exception, but must!!!");
+ } else {
+ // expected
+ Assert.assertEquals(expectedResult, result);
+ }
+ } catch (TExampleException | TApplicationException x) {
+ if (log.isInfoEnabled()) {
+ log.info("Exception: " + x, x);
+ }
+ if (throwException) {
+ // expected
+ Assert.assertEquals(expectedExceptionClass, x.getClass());
+ if (expectedExceptionMsg != null) {
+ Assert.assertEquals(expectedExceptionMsg, x.getMessage());
+ }
+ } else {
+ Assert.fail();
+ }
+ }
+ }
+ }
+
+ private <T> void checkAsyncClient(String desc,
+ String msg,
+ boolean throwException,
+ T expectedResult,
+ Class<?> expectedExceptionClass,
+ String expectedExceptionMsg,
+ AsyncCall<TAppService01.AsyncClient, String, Boolean, AsyncMethodCallback<T>> call) throws Throwable {
+ if (log.isInfoEnabled()) {
+ log.info("start test checkAsyncClient::" + desc + ", throwException: " + throwException
+ + ", serverImplementationType: "
+ + serverImplementationType);
+ }
+ Assert.assertNotEquals(serverPort, -1);
+ try (TNonblockingSocket clientTransportAsync = new TNonblockingSocket("localhost", serverPort, TIMEOUT_MILLIS)) {
+ TAsyncClientManager asyncClientManager = new TAsyncClientManager();
+ try {
+ TAppService01.AsyncClient asyncClient = new TAppService01.AsyncClient(new TBinaryProtocol.Factory(),
+ asyncClientManager,
+ clientTransportAsync);
+ asyncClient.setTimeout(TIMEOUT_MILLIS);
+
+ CompletableFuture<T> futureResult = new CompletableFuture<>();
+
+ call.apply(asyncClient, msg, throwException, new AsyncMethodCallback<T>() {
+
+ @Override
+ public void onError(Exception exception) {
+ futureResult.completeExceptionally(exception);
+ }
+
+ @Override
+ public void onComplete(T response) {
+ futureResult.complete(response);
+ }
+
+ });
+
+ try {
+ T result;
+ try {
+ result = futureResult.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
+ } catch (ExecutionException x) {
+ throw x.getCause();
+ }
+
+ if (throwException && expectedExceptionClass != null) {
+ Assert.fail("No exception, but must!!!");
+ } else {
+ // expected
+ Assert.assertEquals(expectedResult, result);
+ }
+ } catch (TExampleException | TApplicationException x) {
+ if (log.isInfoEnabled()) {
+ log.info("Exception: " + x, x);
+ }
+ if (throwException) {
+ // expected
+ Assert.assertEquals(expectedExceptionClass, x.getClass());
+ if (expectedExceptionMsg != null) {
+ Assert.assertEquals(expectedExceptionMsg, x.getMessage());
+ }
+ } else {
+ Assert.fail();
+ }
+ }
+ } finally {
+ asyncClientManager.stop();
+ }
+ }
+ }
+
+
+ private enum ServerImplementationType {
+
+ SYNC_SERVER(() -> {
+ ServiceSyncImp service = new ServiceSyncImp();
+ return Pair.of(new TAppService01.Processor<>(service), service);
+ }),
+ ASYNC_SERVER(() -> {
+ ServiceAsyncImp service = new ServiceAsyncImp();
+ return Pair.of(new TAppService01.AsyncProcessor<>(service), service);
+ });
+
+ final TProcessor processor;
+ final ServiceBase service;
+
+ ServerImplementationType(Supplier<Pair<TProcessor, ServiceBase>> supplier) {
+ Pair<TProcessor, ServiceBase> pair = supplier.get();
+ this.processor = pair.getLeft();
+ this.service = pair.getRight();
+ }
+ }
+
+
+ @FunctionalInterface
+ private interface SyncCall<T, U, V, R> {
+
+ R apply(T t, U u, V v) throws Exception;
+
+ }
+
+
+ @FunctionalInterface
+ private interface AsyncCall<T, U, V, X> {
+
+ void apply(T t, U u, V v, X x) throws Exception;
+
+ }
+
+}
diff --git a/test/Makefile.am b/test/Makefile.am
index 2199f1e11..6bf12b813 100755
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -167,6 +167,7 @@ EXTRA_DIST = \
UnsafeTypes.thrift \
Service.thrift \
SpecificNameTest.thrift \
+ VoidMethExceptionsTest.thrift \
partial/thrift_test_schema.thrift \
known_failures_Linux.json \
test.py \
diff --git a/test/VoidMethExceptionsTest.thrift b/test/VoidMethExceptionsTest.thrift
new file mode 100644
index 000000000..fc759767f
--- /dev/null
+++ b/test/VoidMethExceptionsTest.thrift
@@ -0,0 +1,13 @@
+namespace java thrift.test.voidmethexceptions
+
+exception TExampleException {
+ 1: required string message;
+}
+
+service TAppService01 {
+ string returnString(1: string msg, 2: bool throwException) throws (1:TExampleException error);
+ void returnVoidThrows(1: string msg, 2: bool throwException) throws (1:TExampleException error);
+ void returnVoidNoThrowsRuntimeException(1: string msg, 2: bool throwException);
+ void returnVoidNoThrowsTApplicationException(1: string msg, 2: bool throwException);
+ oneway void onewayVoidNoThrows(1: string msg, 2: bool throwException);
+}