diff --git a/java-client/src/main/java/org/opensearch/client/transport/httpclient5/ApacheHttpClient5Transport.java b/java-client/src/main/java/org/opensearch/client/transport/httpclient5/ApacheHttpClient5Transport.java index 032e0fd204..1feb9afeef 100644 --- a/java-client/src/main/java/org/opensearch/client/transport/httpclient5/ApacheHttpClient5Transport.java +++ b/java-client/src/main/java/org/opensearch/client/transport/httpclient5/ApacheHttpClient5Transport.java @@ -15,6 +15,8 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.net.ConnectException; +import java.net.SocketTimeoutException; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; @@ -35,12 +37,15 @@ import java.util.concurrent.CompletionException; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicInteger; import java.util.zip.GZIPOutputStream; import javax.annotation.Nullable; +import javax.net.ssl.SSLHandshakeException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.apache.hc.client5.http.ConnectTimeoutException; import org.apache.hc.client5.http.auth.AuthCache; import org.apache.hc.client5.http.auth.AuthScheme; import org.apache.hc.client5.http.auth.AuthScope; @@ -55,6 +60,7 @@ import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.ClassicHttpResponse; +import org.apache.hc.core5.http.ConnectionClosedException; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpEntity; @@ -141,17 +147,16 @@ public ResponseT performRequest( TransportOptions options ) throws IOException { try { - return performRequestAsync(request, endpoint, options).join(); - } catch (final CompletionException ex) { - Throwable cause = ex.getCause(); - if (cause != null) { - if (cause instanceof IOException) { - throw (IOException) cause; - } else { - throw new IOException(cause); - } + return performRequestAsync(request, endpoint, options).get(); + } catch (final Exception ex) { + Exception cause = extractAndWrapCause(ex); + if (cause instanceof IOException) { + throw (IOException) cause; + } + if (cause instanceof RuntimeException) { + throw (RuntimeException) cause; } - throw new IOException(ex); + throw new IllegalStateException("unexpected exception type: must be either RuntimeException or IOException", cause); } } @@ -1015,4 +1020,72 @@ public InputStream asInput() { return new ByteArrayInputStream(this.buf, 0, this.count); } } + + /** + * Wrap the exception so the caller's signature shows up in the stack trace, taking care to copy the original type and message + * where possible so async and sync code don't have to check different exceptions. + */ + private static Exception extractAndWrapCause(Exception exception) { + if (exception instanceof InterruptedException) { + throw new RuntimeException("thread waiting for the response was interrupted", exception); + } + if (exception instanceof ExecutionException) { + ExecutionException executionException = (ExecutionException) exception; + Throwable t = executionException.getCause() == null ? executionException : executionException.getCause(); + if (t instanceof Error) { + throw (Error) t; + } + exception = (Exception) t; + } + if (exception instanceof ConnectTimeoutException) { + ConnectTimeoutException e = new ConnectTimeoutException(exception.getMessage()); + e.initCause(exception); + return e; + } + if (exception instanceof SocketTimeoutException) { + SocketTimeoutException e = new SocketTimeoutException(exception.getMessage()); + e.initCause(exception); + return e; + } + if (exception instanceof ConnectionClosedException) { + ConnectionClosedException e = new ConnectionClosedException(exception.getMessage()); + e.initCause(exception); + return e; + } + if (exception instanceof SSLHandshakeException) { + SSLHandshakeException e = new SSLHandshakeException( + exception.getMessage() + "\nSee https://opensearch.org/docs/latest/clients/java/ for troubleshooting." + ); + e.initCause(exception); + return e; + } + if (exception instanceof ConnectException) { + ConnectException e = new ConnectException(exception.getMessage()); + e.initCause(exception); + return e; + } + if (exception instanceof ResponseException) { + try { + ResponseException e = new ResponseException(((ResponseException) exception).getResponse()); + e.initCause(exception); + return e; + } catch (final IOException ex) { + // We are not able to reconstruct the response, throw IOException instead + return new IOException(exception.getMessage(), exception); + } + } + if (exception instanceof IOException) { + return new IOException(exception.getMessage(), exception); + } + if (exception instanceof OpenSearchException) { + final OpenSearchException e = new OpenSearchException(((OpenSearchException) exception).response()); + e.initCause(exception); + return e; + } + if (exception instanceof RuntimeException) { + return new RuntimeException(exception.getMessage(), exception); + } + return new RuntimeException("error while performing request", exception); + } + } diff --git a/java-client/src/test/java/org/opensearch/client/opensearch/integTest/AbstractAsyncStracktraceIT.java b/java-client/src/test/java/org/opensearch/client/opensearch/integTest/AbstractAsyncStracktraceIT.java index 85eb8666f7..590c0b2539 100644 --- a/java-client/src/test/java/org/opensearch/client/opensearch/integTest/AbstractAsyncStracktraceIT.java +++ b/java-client/src/test/java/org/opensearch/client/opensearch/integTest/AbstractAsyncStracktraceIT.java @@ -17,8 +17,8 @@ public void testFailureFromClientPreservesStacktraceOfCaller() throws Exception var thrown = assertThrows(Exception.class, () -> javaClient().indices().get(g -> g.index("nonexisting-index"))); var stacktraceElements = Throwables.toStringList(thrown); - var someElementContainsCallerMethodName = - stacktraceElements.stream().anyMatch(it -> it.contains("testFailureFromClientPreservesStacktraceOfCaller")); + var someElementContainsCallerMethodName = stacktraceElements.stream() + .anyMatch(it -> it.contains("testFailureFromClientPreservesStacktraceOfCaller")); assertTrue(someElementContainsCallerMethodName); } diff --git a/java-client/src/test/java/org/opensearch/client/opensearch/integTest/AbstractClusterClientIT.java b/java-client/src/test/java/org/opensearch/client/opensearch/integTest/AbstractClusterClientIT.java index 92b0b1aa7c..02c24a94d6 100644 --- a/java-client/src/test/java/org/opensearch/client/opensearch/integTest/AbstractClusterClientIT.java +++ b/java-client/src/test/java/org/opensearch/client/opensearch/integTest/AbstractClusterClientIT.java @@ -95,17 +95,10 @@ public void testClusterUpdateSettingNonExistent() throws IOException { try { openSearchClient.cluster().putSettings(request); fail(); - } catch (OpenSearchException | IOException e) { - OpenSearchException openSearchException; - if (e instanceof OpenSearchException) { - openSearchException = (OpenSearchException) e; - } else { - assertTrue(e.getCause() instanceof OpenSearchException); - openSearchException = (OpenSearchException) e.getCause(); - } - - assertEquals(openSearchException.response().status(), 400); - assertTrue(openSearchException.getMessage().contains("transient setting [no_idea_what_you_are_talking_about], not recognized")); + } catch (OpenSearchException e) { + assertNotNull(e); + assertEquals(e.response().status(), 400); + assertTrue(e.getMessage().contains("transient setting [no_idea_what_you_are_talking_about], not recognized")); } } diff --git a/java-client/src/test/java/org/opensearch/client/opensearch/integTest/AbstractCrudIT.java b/java-client/src/test/java/org/opensearch/client/opensearch/integTest/AbstractCrudIT.java index 2bc9ee3bc1..16a9d5ff57 100644 --- a/java-client/src/test/java/org/opensearch/client/opensearch/integTest/AbstractCrudIT.java +++ b/java-client/src/test/java/org/opensearch/client/opensearch/integTest/AbstractCrudIT.java @@ -118,21 +118,12 @@ public void testSourceExists() throws IOException { public void testGet() throws IOException { { - Exception exception = expectThrows( - Exception.class, - () -> javaClient().get(new GetRequest.Builder().index("index").id("id").build(), String.class) + OpenSearchException exception = expectThrows( + OpenSearchException.class, + () -> javaClient().get(new GetRequest.Builder().index("index").id("id").build(), String.class) ); - OpenSearchException openSearchException; - if (exception instanceof OpenSearchException) { - openSearchException = (OpenSearchException) exception; - } else { - assertTrue(exception.getCause() instanceof OpenSearchException); - openSearchException = (OpenSearchException) exception.getCause(); - } - - assertEquals(404, openSearchException.status()); - assertEquals("Request failed: [index_not_found_exception] no such index [index]", - openSearchException.getMessage()); + assertEquals(404, exception.status()); + assertEquals("Request failed: [index_not_found_exception] no such index [index]", exception.getMessage()); } AppData appData = new AppData(); @@ -216,20 +207,12 @@ public void testUpdate() throws Exception { .build(); try { javaClient().update(updateRequest, AppData.class); - } catch (OpenSearchException | IOException e) { - OpenSearchException openSearchException; - if (e instanceof OpenSearchException) { - openSearchException = (OpenSearchException) e; - } else { - assertTrue(e.getCause() instanceof OpenSearchException); - openSearchException = (OpenSearchException) e.getCause(); - } - + } catch (OpenSearchException e) { // 1.x: [document_missing_exception] [_doc][does_not_exist]: document missing // 2.x: [document_missing_exception] [does_not_exist]: document missing - assertTrue(openSearchException.getMessage().contains("[document_missing_exception]")); - assertTrue(openSearchException.getMessage().contains("[does_not_exist]: document missing")); - assertEquals(404, openSearchException.status()); + assertTrue(e.getMessage().contains("[document_missing_exception]")); + assertTrue(e.getMessage().contains("[does_not_exist]: document missing")); + assertEquals(404, e.status()); } } { diff --git a/java-client/src/test/java/org/opensearch/client/opensearch/integTest/AbstractIndicesClientIT.java b/java-client/src/test/java/org/opensearch/client/opensearch/integTest/AbstractIndicesClientIT.java index 88edd75877..a3f360d245 100644 --- a/java-client/src/test/java/org/opensearch/client/opensearch/integTest/AbstractIndicesClientIT.java +++ b/java-client/src/test/java/org/opensearch/client/opensearch/integTest/AbstractIndicesClientIT.java @@ -52,19 +52,10 @@ public void testIndicesExists() throws IOException { try { javaClient().indices().get(request); fail(); // should never execute - } catch (OpenSearchException | IOException ex) { - OpenSearchException openSearchException; - if (ex instanceof OpenSearchException) { - openSearchException = (OpenSearchException) ex; - } else { - assertTrue(ex.getCause() instanceof OpenSearchException); - openSearchException = (OpenSearchException) ex.getCause(); - } - - assertEquals(openSearchException.status(), 404); - assertEquals(openSearchException.getMessage(), - "Request failed: [index_not_found_exception] " + - "no such index [non_existent_index]"); + } catch (OpenSearchException ex) { + assertNotNull(ex); + assertEquals(ex.status(), 404); + assertEquals(ex.getMessage(), "Request failed: [index_not_found_exception] " + "no such index [non_existent_index]"); } } @@ -80,10 +71,8 @@ public void testIndicesExists() throws IOException { try { javaClient().indices().get(request); fail(); // should never execute - } catch (OpenSearchException | IOException ex) { - if (!(ex instanceof OpenSearchException)) { - assertTrue(ex.getCause() instanceof OpenSearchException); - } + } catch (OpenSearchException ex) { + assertNotNull(ex); } } } @@ -113,19 +102,10 @@ public void testGetSettingsNonExistentIndex() throws IOException { try { javaClient().indices().getSettings(getIndicesSettingsRequest); fail(); - } catch (OpenSearchException | IOException ex) { - OpenSearchException openSearchException; - if (ex instanceof OpenSearchException) { - openSearchException = (OpenSearchException) ex; - } else { - assertTrue(ex.getCause() instanceof OpenSearchException); - openSearchException = (OpenSearchException) ex.getCause(); - } - - assertEquals(openSearchException.status(), 404); - assertEquals(openSearchException.getMessage(), - "Request failed: [index_not_found_exception] " + - "no such index [index_that_doesnt_exist]"); + } catch (OpenSearchException ex) { + assertNotNull(ex); + assertEquals(ex.status(), 404); + assertEquals(ex.getMessage(), "Request failed: [index_not_found_exception] " + "no such index [index_that_doesnt_exist]"); } } @@ -194,16 +174,9 @@ public void testDataStream() throws IOException { try { javaClient().indices().getDataStream(b -> b.name(dataStreamName)); fail(); - } catch (OpenSearchException | IOException ex) { - OpenSearchException openSearchException; - if (ex instanceof OpenSearchException) { - openSearchException = (OpenSearchException) ex; - } else { - assertTrue(ex.getCause() instanceof OpenSearchException); - openSearchException = (OpenSearchException) ex.getCause(); - } - - assertEquals(openSearchException.status(), 404); + } catch (OpenSearchException ex) { + assertNotNull(ex); + assertEquals(ex.status(), 404); } } @@ -213,19 +186,10 @@ public void testGetNotExistingIndexAlias() throws Exception { try { GetAliasResponse response = javaClient().indices().getAlias(aliasRequest); fail(); - } catch (OpenSearchException | IOException ex) { - OpenSearchException openSearchException; - if (ex instanceof OpenSearchException) { - openSearchException = (OpenSearchException) ex; - } else { - assertTrue(ex.getCause() instanceof OpenSearchException); - openSearchException = (OpenSearchException) ex.getCause(); - } - - assertEquals(openSearchException.status(), 404); - assertEquals(openSearchException.getMessage(), - "Request failed: [string_error] " + - "alias [alias_not_exists] missing"); + } catch (OpenSearchException ex) { + assertNotNull(ex); + assertEquals(ex.status(), 404); + assertEquals(ex.getMessage(), "Request failed: [string_error] " + "alias [alias_not_exists] missing"); } } } diff --git a/java-client/src/test/java/org/opensearch/client/opensearch/integTest/AbstractRequestIT.java b/java-client/src/test/java/org/opensearch/client/opensearch/integTest/AbstractRequestIT.java index 1da5b1ad12..ecfd9ff3aa 100644 --- a/java-client/src/test/java/org/opensearch/client/opensearch/integTest/AbstractRequestIT.java +++ b/java-client/src/test/java/org/opensearch/client/opensearch/integTest/AbstractRequestIT.java @@ -419,32 +419,22 @@ public void errorResponse() throws Exception { BooleanResponse exists = javaClient().exists(_0 -> _0.index("doesnotexist").id("reallynot")); assertFalse(exists.value()); - Exception ex = assertThrows(Exception.class, () -> { - GetResponse response = javaClient().get( - _0 -> _0.index("doesnotexist").id("reallynot"), String.class - ); + OpenSearchException ex = assertThrows(OpenSearchException.class, () -> { + GetResponse response = javaClient().get(_0 -> _0.index("doesnotexist").id("reallynot"), String.class); }); - OpenSearchException openSearchException; - if (ex instanceof OpenSearchException) { - openSearchException = (OpenSearchException) ex; - } else { - assertTrue(ex.getCause() instanceof OpenSearchException); - openSearchException = (OpenSearchException) ex.getCause(); - } - - assertEquals(404, openSearchException.status()); - assertEquals("index_not_found_exception", openSearchException.error().type()); - assertEquals("doesnotexist", openSearchException.error().metadata().get("index").to(String.class)); + assertEquals(404, ex.status()); + assertEquals("index_not_found_exception", ex.error().type()); + assertEquals("doesnotexist", ex.error().metadata().get("index").to(String.class)); ExecutionException ee = assertThrows(ExecutionException.class, () -> { OpenSearchAsyncClient aClient = new OpenSearchAsyncClient(javaClient()._transport()); GetResponse response = aClient.get(_0 -> _0.index("doesnotexist").id("reallynot"), String.class).get(); }); - openSearchException = ((OpenSearchException) ee.getCause()); - assertEquals(404, openSearchException.status()); - assertEquals("index_not_found_exception", openSearchException.error().type()); + ex = ((OpenSearchException) ee.getCause()); + assertEquals(404, ex.status()); + assertEquals("index_not_found_exception", ex.error().type()); } @Test diff --git a/java-client/src/test/java/org/opensearch/client/opensearch/integTest/aws/AwsSdk2AsyncStacktraceIT.java b/java-client/src/test/java/org/opensearch/client/opensearch/integTest/aws/AwsSdk2AsyncStacktraceIT.java index 460044ce8b..865ca81da3 100644 --- a/java-client/src/test/java/org/opensearch/client/opensearch/integTest/aws/AwsSdk2AsyncStacktraceIT.java +++ b/java-client/src/test/java/org/opensearch/client/opensearch/integTest/aws/AwsSdk2AsyncStacktraceIT.java @@ -10,5 +10,4 @@ import org.opensearch.client.opensearch.integTest.AbstractAsyncStracktraceIT; -public class AwsSdk2AsyncStacktraceIT extends AbstractAsyncStracktraceIT implements AwsSdk2AsyncTransportSupport { -} +public class AwsSdk2AsyncStacktraceIT extends AbstractAsyncStracktraceIT implements AwsSdk2AsyncTransportSupport {} diff --git a/java-client/src/test/java/org/opensearch/client/opensearch/integTest/aws/AwsSdk2AsyncTransportSupport.java b/java-client/src/test/java/org/opensearch/client/opensearch/integTest/aws/AwsSdk2AsyncTransportSupport.java index 48eec6d00b..699ae40310 100644 --- a/java-client/src/test/java/org/opensearch/client/opensearch/integTest/aws/AwsSdk2AsyncTransportSupport.java +++ b/java-client/src/test/java/org/opensearch/client/opensearch/integTest/aws/AwsSdk2AsyncTransportSupport.java @@ -8,6 +8,7 @@ package org.opensearch.client.opensearch.integTest.aws; +import java.io.IOException; import org.apache.hc.core5.http.HttpHost; import org.opensearch.client.opensearch.integTest.OpenSearchTransportSupport; import org.opensearch.client.transport.OpenSearchTransport; @@ -18,17 +19,16 @@ import software.amazon.awssdk.http.crt.AwsCrtAsyncHttpClient; import software.amazon.awssdk.regions.Region; -import java.io.IOException; - interface AwsSdk2AsyncTransportSupport extends OpenSearchTransportSupport { @Override default OpenSearchTransport buildTransport(Settings settings, HttpHost[] hosts) throws IOException { return new AwsSdk2Transport( - getAsyncHttpClient(), - getTestClusterHost(), - getTestClusterServiceName(), - getTestClusterRegion(), - getTransportOptions().build()); + getAsyncHttpClient(), + getTestClusterHost(), + getTestClusterServiceName(), + getTestClusterRegion(), + getTransportOptions().build() + ); } private String getTestClusterHost() { diff --git a/java-client/src/test/java/org/opensearch/client/opensearch/integTest/httpclient5/AsyncStacktraceIT.java b/java-client/src/test/java/org/opensearch/client/opensearch/integTest/httpclient5/AsyncStacktraceIT.java index 12ee26d9b0..1312c767fe 100644 --- a/java-client/src/test/java/org/opensearch/client/opensearch/integTest/httpclient5/AsyncStacktraceIT.java +++ b/java-client/src/test/java/org/opensearch/client/opensearch/integTest/httpclient5/AsyncStacktraceIT.java @@ -10,5 +10,4 @@ import org.opensearch.client.opensearch.integTest.AbstractAsyncStracktraceIT; -public class AsyncStacktraceIT extends AbstractAsyncStracktraceIT implements HttpClient5TransportSupport { -} +public class AsyncStacktraceIT extends AbstractAsyncStracktraceIT implements HttpClient5TransportSupport {}