From 7ba29d1069e1f0daaf8219e8c57d0ed34d92ba9c Mon Sep 17 00:00:00 2001 From: Matt Timmermans Date: Sun, 10 Jul 2022 11:15:39 -0400 Subject: [PATCH 1/4] Implement AwsSdk2Transport Implementation of automatic request and response compression. Integration test Make sure bulk requests work Properly parse 403 errors from OpenSearch service (they don't follow OS format) Ensure that every transport error with a status code is reported as an OpenSearchError Signed-off-by: Matt Timmermans --- java-client/build.gradle.kts | 17 + .../client/transport/TransportOptions.java | 97 ++++ .../aws/AsyncByteArrayContentPublisher.java | 67 +++ .../aws/AsyncCapturingResponseHandler.java | 80 +++ .../aws/AsyncCapturingSubscriber.java | 95 ++++ .../transport/aws/AwsSdk2Transport.java | 523 ++++++++++++++++++ .../aws/AwsSdk2TransportOptions.java | 228 ++++++++ .../rest_client/RestClientOptions.java | 4 + .../util/OpenSearchRequestBodyBuffer.java | 319 +++++++++++ .../integTest/aws/AwsSdk2BulkRequestIT.java | 65 +++ .../integTest/aws/AwsSdk2SearchIT.java | 136 +++++ .../aws/AwsSdk2TransportTestCase.java | 304 ++++++++++ 12 files changed, 1935 insertions(+) create mode 100644 java-client/src/main/java/org/opensearch/client/transport/aws/AsyncByteArrayContentPublisher.java create mode 100644 java-client/src/main/java/org/opensearch/client/transport/aws/AsyncCapturingResponseHandler.java create mode 100644 java-client/src/main/java/org/opensearch/client/transport/aws/AsyncCapturingSubscriber.java create mode 100644 java-client/src/main/java/org/opensearch/client/transport/aws/AwsSdk2Transport.java create mode 100644 java-client/src/main/java/org/opensearch/client/transport/aws/AwsSdk2TransportOptions.java create mode 100644 java-client/src/main/java/org/opensearch/client/util/OpenSearchRequestBodyBuffer.java create mode 100644 java-client/src/test/java/org/opensearch/client/opensearch/integTest/aws/AwsSdk2BulkRequestIT.java create mode 100644 java-client/src/test/java/org/opensearch/client/opensearch/integTest/aws/AwsSdk2SearchIT.java create mode 100644 java-client/src/test/java/org/opensearch/client/opensearch/integTest/aws/AwsSdk2TransportTestCase.java diff --git a/java-client/build.gradle.kts b/java-client/build.gradle.kts index 585c8a312f..04afeacebb 100644 --- a/java-client/build.gradle.kts +++ b/java-client/build.gradle.kts @@ -61,6 +61,10 @@ java { withJavadocJar() withSourcesJar() + + registerFeature("awsSdk2Support") { + usingSourceSet(sourceSets.get("main")) + } } tasks.withType { @@ -119,6 +123,10 @@ val integrationTest = task("integrationTest") { systemProperty("https", System.getProperty("https", "true")) systemProperty("user", System.getProperty("user", "admin")) systemProperty("password", System.getProperty("password", "admin")) + systemProperty("tests.awsSdk2support.domainHost", + System.getProperty("tests.awsSdk2support.domainHost", null)) + systemProperty("tests.awsSdk2support.domainRegion", + System.getProperty("tests.awsSdk2support.domainRegion", "us-east-1")) } dependencies { @@ -154,6 +162,15 @@ dependencies { implementation("com.fasterxml.jackson.core", "jackson-databind", jacksonDatabindVersion) testImplementation("com.fasterxml.jackson.datatype", "jackson-datatype-jakarta-jsonp", jacksonVersion) + // For AwsSdk2Transport + "awsSdk2SupportImplementation"("software.amazon.awssdk","sdk-core","[2.15,3.0)") + "awsSdk2SupportImplementation"("software.amazon.awssdk","auth","[2.15,3.0)") + testImplementation("software.amazon.awssdk","sdk-core","[2.15,3.0)") + testImplementation("software.amazon.awssdk","auth","[2.15,3.0)") + testImplementation("software.amazon.awssdk","aws-crt-client","[2.15,3.0)") + testImplementation("software.amazon.awssdk","apache-client","[2.15,3.0)") + testImplementation("software.amazon.awssdk","sts","[2.15,3.0)") + // EPL-2.0 OR BSD-3-Clause // https://eclipse-ee4j.github.io/yasson/ implementation("org.eclipse", "yasson", "2.0.2") diff --git a/java-client/src/main/java/org/opensearch/client/transport/TransportOptions.java b/java-client/src/main/java/org/opensearch/client/transport/TransportOptions.java index c49abdb68e..581922e24c 100644 --- a/java-client/src/main/java/org/opensearch/client/transport/TransportOptions.java +++ b/java-client/src/main/java/org/opensearch/client/transport/TransportOptions.java @@ -34,7 +34,10 @@ import org.opensearch.client.util.ObjectBuilder; +import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.function.Consumer; @@ -59,6 +62,10 @@ default TransportOptions with(Consumer fn) { return builder.build(); } + static Builder builder() { + return new BuilderImpl(); + } + interface Builder extends ObjectBuilder { Builder addHeader(String name, String value); @@ -67,4 +74,94 @@ interface Builder extends ObjectBuilder { Builder onWarnings(Function, Boolean> listener); } + + class BuilderImpl implements Builder { + protected List> headers = Collections.emptyList(); + protected Map queryParameters = Collections.emptyMap(); + protected Function, Boolean> onWarnings = null; + + public BuilderImpl() { + } + + public BuilderImpl(TransportOptions src) { + Collection> srcHeaders = src.headers(); + if (srcHeaders != null && !srcHeaders.isEmpty()) { + headers = new ArrayList<>(srcHeaders); + } + Map srcParams = src.queryParameters(); + if (srcParams != null && !srcParams.isEmpty()) { + queryParameters = new HashMap<>(srcParams); + } + onWarnings = src.onWarnings(); + } + + @Override + public Builder addHeader(String name, String value) { + if (headers.isEmpty()) { + headers = new ArrayList<>(); + } + headers.add(Map.entry(name, value)); + return this; + } + + @Override + public Builder setParameter(String name, String value) { + if (value == null) { + if (!queryParameters.isEmpty()) { + queryParameters.remove(name); + } + } else { + if (queryParameters.isEmpty()) { + queryParameters = new HashMap<>(); + } + queryParameters.put(name, value); + } + return this; + } + + @Override + public Builder onWarnings(Function, Boolean> listener) { + onWarnings = listener; + return this; + } + + @Override + public TransportOptions build() { + return new DefaultImpl(this); + } + } + + class DefaultImpl implements TransportOptions { + private final List> headers; + private final Map params; + private final Function, Boolean> onWarnings; + + protected DefaultImpl(BuilderImpl builder) { + this.headers = builder.headers.isEmpty() ? Collections.emptyList() : List.copyOf(builder.headers); + this.params = builder.queryParameters.isEmpty() ? + Collections.emptyMap() : + Map.copyOf(builder.queryParameters); + this.onWarnings = builder.onWarnings; + } + + @Override + public Collection> headers() { + return headers; + } + + @Override + public Map queryParameters() { + return params; + } + + @Override + public Function, Boolean> onWarnings() { + return onWarnings; + } + + @Override + public Builder toBuilder() { + return new BuilderImpl(this); + } + } } diff --git a/java-client/src/main/java/org/opensearch/client/transport/aws/AsyncByteArrayContentPublisher.java b/java-client/src/main/java/org/opensearch/client/transport/aws/AsyncByteArrayContentPublisher.java new file mode 100644 index 0000000000..a61f1e2807 --- /dev/null +++ b/java-client/src/main/java/org/opensearch/client/transport/aws/AsyncByteArrayContentPublisher.java @@ -0,0 +1,67 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. 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. + */ + +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.client.transport.aws; + +import org.reactivestreams.Subscriber; +import software.amazon.awssdk.core.async.AsyncRequestBody; +import software.amazon.awssdk.http.async.SdkHttpContentPublisher; + +import javax.annotation.CheckForNull; +import java.nio.ByteBuffer; +import java.util.Optional; + +/** + * An implementation of AWS {@SdkHttpContentPublisher} that transfers a pre-existing + * byte array + */ +class AsyncByteArrayContentPublisher implements SdkHttpContentPublisher { + private final AsyncRequestBody delegate; + + AsyncByteArrayContentPublisher(@CheckForNull byte[] data) { + if (data == null) { + delegate = AsyncRequestBody.empty(); + } else { + delegate = AsyncRequestBody.fromBytes(data); + } + } + + @Override + public Optional contentLength() { + return delegate.contentLength(); + } + + @Override + public void subscribe(Subscriber s) { + delegate.subscribe(s); + } +} diff --git a/java-client/src/main/java/org/opensearch/client/transport/aws/AsyncCapturingResponseHandler.java b/java-client/src/main/java/org/opensearch/client/transport/aws/AsyncCapturingResponseHandler.java new file mode 100644 index 0000000000..08ddf70eb6 --- /dev/null +++ b/java-client/src/main/java/org/opensearch/client/transport/aws/AsyncCapturingResponseHandler.java @@ -0,0 +1,80 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. 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. + */ + +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.client.transport.aws; + +import org.reactivestreams.Publisher; +import software.amazon.awssdk.http.SdkHttpResponse; +import software.amazon.awssdk.http.async.SdkAsyncHttpResponseHandler; + +import java.nio.ByteBuffer; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * An implementation of AWS {@link SdkAsyncHttpResponseHandler} that captures the response, + * and the content as a byte array. + */ +final class AsyncCapturingResponseHandler implements SdkAsyncHttpResponseHandler { + private final CompletableFuture responseFuture; + private final AsyncCapturingSubscriber bodySubscriber = new AsyncCapturingSubscriber(); + private final AtomicBoolean subscribed = new AtomicBoolean(false); + + AsyncCapturingResponseHandler() { + responseFuture = new CompletableFuture<>(); + } + + public CompletableFuture getHeaderPromise() { + return responseFuture; + } + + public CompletableFuture getBodyPromise() { + return bodySubscriber.getPromise(); + } + + @Override + public void onHeaders(SdkHttpResponse response) { + responseFuture.complete(response); + } + + @Override + public void onStream(Publisher publisher) { + if (!subscribed.getAndSet(true)) { + publisher.subscribe(bodySubscriber); + } + } + + @Override + public void onError(Throwable e) { + responseFuture.completeExceptionally(e); + } +} diff --git a/java-client/src/main/java/org/opensearch/client/transport/aws/AsyncCapturingSubscriber.java b/java-client/src/main/java/org/opensearch/client/transport/aws/AsyncCapturingSubscriber.java new file mode 100644 index 0000000000..9857ec5c9c --- /dev/null +++ b/java-client/src/main/java/org/opensearch/client/transport/aws/AsyncCapturingSubscriber.java @@ -0,0 +1,95 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. 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. + */ + +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.client.transport.aws; + +import org.reactivestreams.Subscriber; +import org.reactivestreams.Subscription; + +import java.io.ByteArrayOutputStream; +import java.nio.ByteBuffer; +import java.util.concurrent.CompletableFuture; + +/** + * A reactive subscriber that captures a byte stream into a byte array + */ +class AsyncCapturingSubscriber implements Subscriber { + private final ByteArrayOutputStream buffer; + private final CompletableFuture promise; + private Subscription subscription; + + AsyncCapturingSubscriber() { + buffer = new ByteArrayOutputStream(); + promise = new CompletableFuture<>(); + } + + public CompletableFuture getPromise() { + return promise; + } + + @Override + public void onSubscribe(Subscription subscription) { + this.subscription = subscription; + subscription.request(Long.MAX_VALUE); + } + + @Override + public void onNext(ByteBuffer buf) { + try { + if (buf != null && buf.remaining() > 0) { + if (buf.hasArray()) { + buffer.write(buf.array(), buf.arrayOffset() + buf.position(), buf.remaining()); + } else { + byte[] data = new byte[buf.remaining()]; + buf.asReadOnlyBuffer().get(data); + buffer.write(data); + } + } + this.subscription.request(1); + } catch (Throwable e) { + promise.completeExceptionally(e); + } + } + + @Override + public void onError(Throwable e) { + if (e == null) { + e = new IllegalArgumentException("Subscriber.onError called with null paramter"); + } + promise.completeExceptionally(e); + } + + @Override + public void onComplete() { + promise.complete(buffer.toByteArray()); + } +} diff --git a/java-client/src/main/java/org/opensearch/client/transport/aws/AwsSdk2Transport.java b/java-client/src/main/java/org/opensearch/client/transport/aws/AwsSdk2Transport.java new file mode 100644 index 0000000000..5e6eeefaed --- /dev/null +++ b/java-client/src/main/java/org/opensearch/client/transport/aws/AwsSdk2Transport.java @@ -0,0 +1,523 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. 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. + */ + +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.client.transport.aws; + +import jakarta.json.JsonObject; +import jakarta.json.stream.JsonParser; +import org.opensearch.client.json.JsonpDeserializer; +import org.opensearch.client.json.JsonpMapper; +import org.opensearch.client.json.jackson.JacksonJsonpMapper; +import org.opensearch.client.opensearch._types.ErrorCause; +import org.opensearch.client.opensearch._types.ErrorResponse; +import org.opensearch.client.opensearch._types.OpenSearchException; +import org.opensearch.client.transport.Endpoint; +import org.opensearch.client.transport.JsonEndpoint; +import org.opensearch.client.transport.OpenSearchTransport; +import org.opensearch.client.transport.TransportException; +import org.opensearch.client.transport.TransportOptions; +import org.opensearch.client.transport.endpoints.BooleanEndpoint; +import org.opensearch.client.transport.endpoints.BooleanResponse; +import org.opensearch.client.util.OpenSearchRequestBodyBuffer; +import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider; +import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider; +import software.amazon.awssdk.auth.signer.Aws4Signer; +import software.amazon.awssdk.auth.signer.params.Aws4SignerParams; +import software.amazon.awssdk.http.AbortableInputStream; +import software.amazon.awssdk.http.HttpExecuteRequest; +import software.amazon.awssdk.http.HttpExecuteResponse; +import software.amazon.awssdk.http.SdkHttpClient; +import software.amazon.awssdk.http.SdkHttpFullRequest; +import software.amazon.awssdk.http.SdkHttpMethod; +import software.amazon.awssdk.http.SdkHttpResponse; +import software.amazon.awssdk.http.async.AsyncExecuteRequest; +import software.amazon.awssdk.http.async.SdkAsyncHttpClient; +import software.amazon.awssdk.regions.Region; + +import javax.annotation.CheckForNull; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.Collection; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.zip.GZIPInputStream; + + +/** + * Implementation of the OpenSearchTransport interface that sends signed requests using + * the AWS v2 SDK HTTP clients, to connect to an AWS OpenSearch service using IAM authentication + */ +public class AwsSdk2Transport implements OpenSearchTransport { + /** + * By default, requests that exceed this size will be automatically compressed. + * {@link AwsSdk2TransportOptions} can be used to override this setting or disable compresson. + */ + public static final Integer DEFAULT_REQUEST_COMPRESSION_SIZE = 8192; + + private static final byte[] NO_BYTES = new byte[0]; + private final SdkHttpClient httpClient; + private final SdkAsyncHttpClient asyncHttpClient; + private final String host; + private final Region signingRegion; + private final JsonpMapper defaultMapper; + private final AwsSdk2TransportOptions transportOptions; + + /** + * Create an {@link OpenSearchTransport} with a SYNCHRONOUS AWS Http client + *

+ * Note that asynchronous OpenSearch requests sent through this transport will be dispatched + * *synchronously* on the calling thread. + * + * @param httpClient HTTP client to use for OpenSearch requests + * @param host The fully qualified domain name to connect to + * @param signingRegion The AWS region for which requests will be signed. This should typically match + * the region in `host`. + * @param options Options that apply to all requests. Can be null. Create with + * {@link AwsSdk2TransportOptions#builder()} and use these to specify non-default credentials, + * compression options, etc. + */ + public AwsSdk2Transport( + @Nonnull SdkHttpClient httpClient, + @Nonnull String host, + @Nonnull Region signingRegion, + @CheckForNull AwsSdk2TransportOptions options) { + this(httpClient, null, host, signingRegion, options); + } + + /** + * Create an {@link OpenSearchTransport} with an ASYNCHRONOUS AWS Http client + *

+ * Note that synchronous OpenSearch requests sent through this transport will be dispatched + * using the asynchronous client, but the calling thread will block until they are complete. + * + * @param asyncHttpClient HTTP client to use for OpenSearch requests + * @param host The target host + * @param signingRegion The AWS region for which requests will be signed. This should typically match + * the region in `host`. + * @param options Options that apply to all requests. Can be null. Create with + * {@link AwsSdk2TransportOptions#builder()} and use these to specify non-default credentials, + * compression options, etc. + */ + public AwsSdk2Transport( + @Nonnull SdkAsyncHttpClient asyncHttpClient, + @Nonnull String host, + @Nonnull Region signingRegion, + @CheckForNull AwsSdk2TransportOptions options) { + this(null, asyncHttpClient, host, signingRegion, options); + } + + /** + * Create an {@link OpenSearchTransport} with both synchronous and asynchronous AWS HTTP clients. + *

+ * The synchronous client will be used for synchronous OpenSearch requests, and the asynchronous client + * will be used for asynchronous HTTP requests. + * + * @param httpClient HTTP client to use for OpenSearch requests + * @param asyncHttpClient HTTP client to use for synchronous OpenSearch requests + * @param host The fully qualified domain name to connect to + * @param signingRegion The AWS region for which requests will be signed. This should typically match + * the region in `host`. + * @param options Options that apply to all requests. Can be null. Create with + * {@link AwsSdk2TransportOptions#builder()} and use these to specify non-default credentials, + * compression options, etc. + */ + public AwsSdk2Transport( + @CheckForNull SdkHttpClient httpClient, + @CheckForNull SdkAsyncHttpClient asyncHttpClient, + @Nonnull String host, + @Nonnull Region signingRegion, + @CheckForNull AwsSdk2TransportOptions options) { + if (httpClient == null && asyncHttpClient == null) + { + throw new IllegalArgumentException("At least one SdkHttpClient or SdkAsyncHttpClient must be provided"); + } + Objects.requireNonNull(host, "Target OpenSearch service host must not be null"); + this.httpClient = httpClient; + this.asyncHttpClient = asyncHttpClient; + this.host = host; + this.signingRegion = signingRegion; + this.transportOptions = options != null ? options : AwsSdk2TransportOptions.builder().build(); + this.defaultMapper = Optional.ofNullable(options) + .map(AwsSdk2TransportOptions::mapper) + .orElse(new JacksonJsonpMapper()); + } + + @Override + public ResponseT performRequest( + RequestT request, + Endpoint endpoint, + @Nullable TransportOptions options + ) throws IOException { + + OpenSearchRequestBodyBuffer requestBody = prepareRequestBody(request, endpoint, options); + SdkHttpFullRequest clientReq = prepareRequest(request, endpoint, options, requestBody); + + if (httpClient != null) { + return executeSync(clientReq, endpoint, options); + } else { + try { + return executeAsync(clientReq, requestBody, endpoint, options).get(); + } catch (ExecutionException e) { + Throwable cause = e.getCause(); + if (cause != null) { + if (cause instanceof IOException) { + throw (IOException) cause; + } + if (cause instanceof RuntimeException) { + throw (RuntimeException) cause; + } + throw new RuntimeException(cause); + } + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new IOException("HttpRequest was interrupted", e); + } + } + } + + @Override + public CompletableFuture performRequestAsync( + RequestT request, + Endpoint endpoint, + @Nullable TransportOptions options + ) { + try { + OpenSearchRequestBodyBuffer requestBody = prepareRequestBody(request, endpoint, options); + SdkHttpFullRequest clientReq = prepareRequest(request, endpoint, options, requestBody); + if (asyncHttpClient != null) { + return executeAsync(clientReq, requestBody, endpoint, options); + } else { + ResponseT result = executeSync(clientReq, endpoint, options); + return CompletableFuture.completedFuture(result); + } + } catch (Throwable e) { + CompletableFuture cf = new CompletableFuture<>(); + cf.completeExceptionally(e); + return cf; + } + } + + @Override + public JsonpMapper jsonpMapper() { + return defaultMapper; + } + + @Override + public AwsSdk2TransportOptions options() { + return transportOptions; + } + + @Override + public void close() { + } + + @CheckForNull + private OpenSearchRequestBodyBuffer prepareRequestBody( + RequestT request, + Endpoint endpoint, + TransportOptions options + ) throws IOException { + if (endpoint.hasRequestBody()) { + final JsonpMapper mapper = Optional.ofNullable(options) + .map(o -> o instanceof AwsSdk2TransportOptions ? ((AwsSdk2TransportOptions) o) : null) + .map(AwsSdk2TransportOptions::mapper) + .orElse(defaultMapper); + final int maxUncompressedSize = Optional.ofNullable(options) + .map(o -> o instanceof AwsSdk2TransportOptions ? ((AwsSdk2TransportOptions) o) : null) + .map(AwsSdk2TransportOptions::requestCompressionSize) + .or(()->Optional.ofNullable(transportOptions.requestCompressionSize())) + .orElse(DEFAULT_REQUEST_COMPRESSION_SIZE); + + OpenSearchRequestBodyBuffer buffer = new OpenSearchRequestBodyBuffer(mapper, maxUncompressedSize); + buffer.addContent(request); + buffer.close(); + return buffer; + } + return null; + } + + private SdkHttpFullRequest prepareRequest( + RequestT request, + Endpoint endpoint, + @CheckForNull TransportOptions options, + @CheckForNull OpenSearchRequestBodyBuffer body + ) { + SdkHttpFullRequest.Builder req = SdkHttpFullRequest.builder() + .method(SdkHttpMethod.fromValue(endpoint.method(request))); + + StringBuilder url = new StringBuilder(); + url.append("https://").append(host); + String path = endpoint.requestUrl(request); + if (!path.startsWith("/")) { + url.append('/'); + } + url.append(path); + Map params = endpoint.queryParameters(request); + if (params != null && !params.isEmpty()) { + char sep = '?'; + for (var ent : params.entrySet()) { + url.append(sep).append(ent.getKey()).append('='); + url.append(URLEncoder.encode(ent.getValue(), StandardCharsets.UTF_8)); + sep = '&'; + } + } + applyOptionsParams(url, transportOptions); + applyOptionsParams(url, options); + try { + req.uri(new URI(url.toString())); + } catch (URISyntaxException e) { + throw new IllegalArgumentException("Invalid request URI: " + url.toString()); + } + applyOptionsHeaders(req, transportOptions); + applyOptionsHeaders(req, options); + if (endpoint.hasRequestBody() && body != null) { + req.putHeader("Content-Type", body.getContentType()); + String encoding = body.getContentEncoding(); + if (encoding != null) { + req.putHeader("Content-Encoding", encoding); + } + req.putHeader("Content-Length", String.valueOf(body.getContentLength())); + req.contentStreamProvider(body::getInputStream); + } + + boolean responseCompression = Optional.ofNullable(options) + .map(o -> o instanceof AwsSdk2TransportOptions ? ((AwsSdk2TransportOptions) o) : null) + .map(AwsSdk2TransportOptions::responseCompression) + .or(() -> Optional.ofNullable(transportOptions.responseCompression())) + .orElse(Boolean.TRUE); + if (responseCompression) { + req.putHeader("Accept-Encoding", "gzip"); + } else { + req.removeHeader("Accept-Encoding"); + } + + final AwsCredentialsProvider credentials = Optional.ofNullable(options) + .map(o -> o instanceof AwsSdk2TransportOptions ? ((AwsSdk2TransportOptions) o) : null) + .map(AwsSdk2TransportOptions::credentials) + .or(() -> Optional.ofNullable(transportOptions.credentials())) + .orElse(DefaultCredentialsProvider.create()); + + Aws4SignerParams signerParams = Aws4SignerParams.builder() + .awsCredentials(credentials.resolveCredentials()) + .signingName("es") + .signingRegion(signingRegion) + .build(); + return Aws4Signer.create().sign(req.build(), signerParams); + } + + private void applyOptionsParams(StringBuilder url, TransportOptions options) { + if (options == null) { + return; + } + Map params = options.queryParameters(); + if (params != null && !params.isEmpty()) { + char sep = url.indexOf("?") < 0 ? '?' : '&'; + for (Map.Entry param : params.entrySet()) { + url.append(sep).append(param.getKey()).append('='); + url.append(URLEncoder.encode(param.getValue(), StandardCharsets.UTF_8)); + sep = '?'; + } + } + } + + private void applyOptionsHeaders(SdkHttpFullRequest.Builder builder, TransportOptions options) { + if (options == null) { + return; + } + Collection> headers = options.headers(); + if (headers != null && !headers.isEmpty()) { + for (Map.Entry header : headers) { + builder.appendHeader(header.getKey(), header.getValue()); + } + } + } + + private ResponseT executeSync( + SdkHttpFullRequest httpRequest, + Endpoint endpoint, + TransportOptions options + ) throws IOException { + + HttpExecuteRequest.Builder executeRequest = HttpExecuteRequest.builder().request(httpRequest); + if (httpRequest.contentStreamProvider().isPresent()) { + executeRequest.contentStreamProvider(httpRequest.contentStreamProvider().get()); + } + HttpExecuteResponse executeResponse = httpClient.prepareRequest(executeRequest.build()).call(); + AbortableInputStream bodyStream = null; + try { + bodyStream = executeResponse.responseBody().orElse(null); + SdkHttpResponse httpResponse = executeResponse.httpResponse(); + return parseResponse(httpResponse, bodyStream, endpoint, options); + } finally { + if (bodyStream != null) { + bodyStream.close(); + } + } + } + + private CompletableFuture executeAsync( + SdkHttpFullRequest httpRequest, + @CheckForNull OpenSearchRequestBodyBuffer requestBody, + Endpoint endpoint, + TransportOptions options + ) { + byte[] requestBodyArray = requestBody == null ? NO_BYTES : requestBody.getByteArray(); + + final AsyncCapturingResponseHandler responseHandler = new AsyncCapturingResponseHandler(); + AsyncExecuteRequest.Builder executeRequest = AsyncExecuteRequest.builder() + .request(httpRequest) + .requestContentPublisher(new AsyncByteArrayContentPublisher(requestBodyArray)) + .responseHandler(responseHandler); + CompletableFuture executeFuture = asyncHttpClient.execute(executeRequest.build()); + return executeFuture + .thenCompose(_v -> responseHandler.getHeaderPromise()) + .thenCompose(response -> responseHandler.getBodyPromise().thenCompose(responseBody -> { + CompletableFuture ret = new CompletableFuture<>(); + try { + InputStream bodyStream = new ByteArrayInputStream(responseBody); + ret.complete(parseResponse(response, bodyStream, endpoint, options)); + } catch (Throwable e) { + ret.completeExceptionally(e); + } + return ret; + })); + } + + private ResponseT parseResponse( + @Nonnull SdkHttpResponse httpResponse, + @CheckForNull InputStream bodyStream, + @Nonnull Endpoint endpoint, + @CheckForNull TransportOptions options + ) throws IOException { + final JsonpMapper mapper = Optional.ofNullable(options) + .map(o -> o instanceof AwsSdk2TransportOptions ? ((AwsSdk2TransportOptions) o) : null) + .map(AwsSdk2TransportOptions::mapper) + .orElse(defaultMapper); + + int statusCode = httpResponse.statusCode(); + boolean isZipped = httpResponse.firstMatchingHeader("Content-Encoding") + .map(enc -> enc.contains("gzip")) + .orElse(Boolean.FALSE); + if (bodyStream != null && isZipped) { + bodyStream = new GZIPInputStream(bodyStream); + } + + if (statusCode == 403) { + // Authentication errors from AWS do not follow OpenSearch exception format + ErrorCause.Builder cause = new ErrorCause.Builder(); + cause.type("security_exception"); + cause.reason("authentication/authorization failure"); + + if (bodyStream != null) { + try (JsonParser parser = mapper.jsonProvider().createParser(bodyStream)) { + JsonObject val = JsonpDeserializer.jsonValueDeserializer() + .deserialize(parser, mapper) + .asJsonObject(); + String message = val.getString("Message", null); + if (message == null) { + message = val.getString("message", null); + } + if (message != null) { + cause.reason(message); + } + } catch (Exception e) { + // OK. We'll use default message + } + } + + ErrorResponse error = ErrorResponse.of(err -> err.status(statusCode).error(cause.build())); + throw new OpenSearchException(error); + } + + if (endpoint.isError(statusCode)) { + JsonpDeserializer errorDeserializer = endpoint.errorDeserializer(statusCode); + if (errorDeserializer == null || bodyStream == null) { + throw new TransportException( + "Request failed with status code '" + statusCode + "'" + ); + } + try { + try (JsonParser parser = mapper.jsonProvider().createParser(bodyStream)) { + ErrorT error = errorDeserializer.deserialize(parser, mapper); + throw new OpenSearchException((ErrorResponse) error); + } + } catch (Exception e) { + // can't parse the error - use a general exception + ErrorCause.Builder cause = new ErrorCause.Builder(); + cause.type("http_exception"); + cause.reason("server returned " + statusCode); + ErrorResponse error = ErrorResponse.of(err -> err.status(statusCode).error(cause.build())); + throw new OpenSearchException(error); + } + } else { + if (endpoint instanceof BooleanEndpoint) { + BooleanEndpoint bep = (BooleanEndpoint) endpoint; + @SuppressWarnings("unchecked") + ResponseT response = (ResponseT) new BooleanResponse(bep.getResult(statusCode)); + return response; + } else if (endpoint instanceof JsonEndpoint) { + @SuppressWarnings("unchecked") + JsonEndpoint jsonEndpoint = (JsonEndpoint) endpoint; + // Successful response + ResponseT response = null; + JsonpDeserializer responseParser = jsonEndpoint.responseDeserializer(); + if (responseParser != null) { + // Expecting a body + if (bodyStream == null) { + throw new TransportException("Expecting a response body, but none was sent"); + } + try (JsonParser parser = mapper.jsonProvider().createParser(bodyStream)) { + try { + response = responseParser.deserialize(parser, mapper); + } catch (NullPointerException e) { + response = responseParser.deserialize(parser, mapper); + } + } + ; + } + return response; + } else { + throw new TransportException("Unhandled endpoint type: '" + endpoint.getClass().getName() + "'"); + } + } + } +} diff --git a/java-client/src/main/java/org/opensearch/client/transport/aws/AwsSdk2TransportOptions.java b/java-client/src/main/java/org/opensearch/client/transport/aws/AwsSdk2TransportOptions.java new file mode 100644 index 0000000000..76046d7bde --- /dev/null +++ b/java-client/src/main/java/org/opensearch/client/transport/aws/AwsSdk2TransportOptions.java @@ -0,0 +1,228 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. 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. + */ + +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.client.transport.aws; + +import org.opensearch.client.json.JsonpMapper; +import org.opensearch.client.transport.TransportOptions; +import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider; + +import java.util.List; +import java.util.function.Function; + +public interface AwsSdk2TransportOptions extends TransportOptions { + + /** + * Get the credentials provider to user for signing requests. + *

+ * If this is null, then a default provider will be used -- either a provider specified + * in a more general {@link AwsSdk2TransportOptions} that applies to the request, or the + * default credential chain if there is none. + *

+ * + * @return A credentials provider or null + */ + AwsCredentialsProvider credentials(); + + /** + * Get the maximum size for uncompressed requests. Requests larger than this size will + * be sent with Content-Encoding: gzip. + *

+ * If this is null, then a default will be used -- either a value specified + * in a more general {@link AwsSdk2TransportOptions} that applies to the request, or a + * reasonable default if there is none. + *

+ * If this is Integer.MAX_VALUE, then requests will not be compressed. If this is 0, then all non-empty + * request bodies will be compressed. + *

+ * + * @return An integer size limit or null + */ + Integer requestCompressionSize(); + + /** + * Get the response compression enable/disable value. If this is true, then an + * Accept-Encoding: gzip header will be sent with the request. The server will + * decide whether or not to compress its responses. + *

+ * If this is null, then a default will be used -- either a value specified + * in a more general {@link AwsSdk2TransportOptions} that applies to the request, or + * {@link Boolean#TRUE} if there is none. + *

+ * + * @return response compression enable/disable flag, or null + */ + Boolean responseCompression(); + + /** + * Get mapper used for serializing and deserializing requests and responses. + *

+ * If this is null, then a default will be used -- either a value specified + * in a more general {@link AwsSdk2TransportOptions} that applies to the request, or a + * new {@link org.opensearch.client.json.jackson.JacksonJsonpMapper} or equivalent if + * there is none. + *

+ * + * @return A mapper or null + */ + JsonpMapper mapper(); + + AwsSdk2TransportOptions.Builder toBuilder(); + + static AwsSdk2TransportOptions.Builder builder() { + return new BuilderImpl(); + } + + interface Builder extends TransportOptions.Builder { + Builder addHeader(String name, String value); + + Builder setParameter(String name, String value); + + Builder onWarnings(Function, Boolean> listener); + + Builder setCredentials(AwsCredentialsProvider credentials); + + Builder setRequestCompressionSize(Integer size); + + Builder setResponseCompression(Boolean enabled); + + Builder setMapper(JsonpMapper mapper); + + AwsSdk2TransportOptions build(); + } + + class BuilderImpl extends TransportOptions.BuilderImpl implements Builder { + + protected AwsCredentialsProvider credentials; + protected Integer requestCompressionSize; + protected Boolean responseCompression; + protected JsonpMapper mapper; + + public BuilderImpl() { + } + + public BuilderImpl(AwsSdk2TransportOptions src) { + super(src); + credentials = src.credentials(); + requestCompressionSize = src.requestCompressionSize(); + responseCompression = src.responseCompression(); + mapper = src.mapper(); + } + + @Override + public Builder addHeader(String name, String value) { + super.addHeader(name, value); + return this; + } + + @Override + public Builder setParameter(String name, String value) { + super.setParameter(name, value); + return this; + } + + @Override + public Builder onWarnings(Function, Boolean> listener) { + super.onWarnings(listener); + return this; + } + + @Override + public Builder setCredentials(AwsCredentialsProvider credentials) { + this.credentials = credentials; + return this; + } + + @Override + public Builder setRequestCompressionSize(Integer size) { + this.requestCompressionSize = size; + return this; + } + + @Override + public Builder setMapper(JsonpMapper mapper) { + this.mapper = mapper; + return this; + } + + @Override + public Builder setResponseCompression(Boolean enabled) { + this.responseCompression = enabled; + return this; + } + + @Override + public AwsSdk2TransportOptions build() { + return new DefaultImpl(this); + } + } + + class DefaultImpl extends TransportOptions.DefaultImpl implements AwsSdk2TransportOptions { + + private AwsCredentialsProvider credentials; + private Integer requestCompressionSize; + private Boolean responseCompression; + private JsonpMapper mapper; + + DefaultImpl(AwsSdk2TransportOptions.BuilderImpl builder) { + super(builder); + credentials = builder.credentials; + requestCompressionSize = builder.requestCompressionSize; + mapper = builder.mapper; + } + + @Override + public AwsCredentialsProvider credentials() { + return credentials; + } + + @Override + public Integer requestCompressionSize() { + return requestCompressionSize; + } + + @Override + public Boolean responseCompression() { + return responseCompression; + } + + @Override + public JsonpMapper mapper() { + return mapper; + } + + @Override + public AwsSdk2TransportOptions.Builder toBuilder() { + return new AwsSdk2TransportOptions.BuilderImpl(this); + } + } +} diff --git a/java-client/src/main/java/org/opensearch/client/transport/rest_client/RestClientOptions.java b/java-client/src/main/java/org/opensearch/client/transport/rest_client/RestClientOptions.java index 9e746f3863..3015189421 100644 --- a/java-client/src/main/java/org/opensearch/client/transport/rest_client/RestClientOptions.java +++ b/java-client/src/main/java/org/opensearch/client/transport/rest_client/RestClientOptions.java @@ -66,6 +66,10 @@ public RestClientOptions(RequestOptions options) { this.options = options; } + public static RestClientOptions.Builder builder() { + return new Builder(RequestOptions.DEFAULT.toBuilder()); + } + /** * Get the wrapped Rest Client request options */ diff --git a/java-client/src/main/java/org/opensearch/client/util/OpenSearchRequestBodyBuffer.java b/java-client/src/main/java/org/opensearch/client/util/OpenSearchRequestBodyBuffer.java new file mode 100644 index 0000000000..dc64148451 --- /dev/null +++ b/java-client/src/main/java/org/opensearch/client/util/OpenSearchRequestBodyBuffer.java @@ -0,0 +1,319 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. 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. + */ + +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.client.util; + +import jakarta.json.stream.JsonGenerator; +import org.opensearch.client.json.JsonpMapper; +import org.opensearch.client.json.NdJsonpSerializable; +import org.opensearch.client.transport.OpenSearchTransport; + +import javax.annotation.CheckForNull; +import javax.annotation.Nonnull; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Iterator; +import java.util.zip.GZIPOutputStream; + +/** + * Serializes and captures an OpenSearch request body, and then provides access to it in convenient + * forms for HTTP requests. This is a utility class for use by various {@link OpenSearchTransport} + * implementations. + *

+ * Request bodies can be automatically compressed when they exceed a given size. + *

+ */ +public class OpenSearchRequestBodyBuffer { + private static final byte[] NO_BYTES = new byte[0]; + private final OutputBuffer outputBuffer; + private final CompressingOutputBuffer captureBuffer; + private final JsonpMapper mapper; + private final JsonGenerator jsonGenerator; + private boolean hasContent = false; + private boolean isMulti = false; + private boolean isClosed = false; + private byte[] arrayMemo = null; + + /** + * Create a request body buffer + * + * @param mapper mapper used to serialize the content + * @param requestCompressionSize When the captured data exceeds this size, it will be automatically + * compressed. Pass Integer.MAX_VALUE to prevent compression + */ + public OpenSearchRequestBodyBuffer(JsonpMapper mapper, int requestCompressionSize) { + this.outputBuffer = new OutputBuffer(); + this.captureBuffer = new CompressingOutputBuffer(this.outputBuffer, requestCompressionSize); + this.mapper = mapper; + jsonGenerator = mapper.jsonProvider().createGenerator(this.captureBuffer); + } + + /** + * Add some content to the buffer. If the buffer already contains some data, or if the provided + * object implements {@link NdJsonpSerializable}, then the buffer will contain multiple objects + * in newline-delimited JSON format. + * + * @param content The new content object to add + */ + public void addContent(Object content) throws IOException { + if (hasContent && !isMulti) { + captureBuffer.write((byte) '\n'); + isMulti = true; + } + hasContent = true; + if (content instanceof NdJsonpSerializable) { + isMulti = true; + addNdJson(((NdJsonpSerializable) content)); + } else { + mapper.serialize(content, jsonGenerator); + jsonGenerator.flush(); + if (isMulti) { + captureBuffer.write((byte) '\n'); + } + } + } + + private void addNdJson(NdJsonpSerializable content) throws IOException { + Iterator values = content._serializables(); + while (values.hasNext()) { + Object value = values.next(); + if (value instanceof NdJsonpSerializable && value != content) { + addNdJson((NdJsonpSerializable) value); + } else { + hasContent = true; + mapper.serialize(value, jsonGenerator); + jsonGenerator.flush(); + captureBuffer.write((byte) '\n'); + } + } + } + + /** + * @return true if the content has been compressed + */ + public boolean isCompressed() { + return captureBuffer.isCompressed(); + } + + /** + * @return true if this buffer contains multiple newline-delimited objects. + */ + public boolean isNdJson() { + return isMulti; + } + + /** + * Get the value of the Content-Encoding header that should be sent along with this buffer, + * or null if there shouldn't be one. + */ + @CheckForNull + public String getContentEncoding() { + if (captureBuffer.isCompressed()) { + return "gzip"; + } + return null; + } + + /** + * Get the value of the Content-Type header that should be sent along with this buffer. + */ + @Nonnull + public String getContentType() { + return "application/json"; + } + + /** + * Get the value of the Content-Length header that should be sent along with this buffer. + *

+ * This call finalizes the buffer. After this call, any attempt to add more content + * will throw an IOException. + *

+ * + * @return The length of the buffered content + */ + public long getContentLength() { + ensureClosed(); + return outputBuffer.size(); + } + + /** + * Get the contents of this buffer as a byte array. + *

+ * This call finalizes the buffer. After this call, any attempt to add more content + * will throw an IOException. + *

+ * + * @return The buffered data + */ + public byte[] getByteArray() { + if (arrayMemo == null) { + ensureClosed(); + arrayMemo = outputBuffer.size() <= 0 ? NO_BYTES : outputBuffer.toByteArray(); + } + return arrayMemo; + } + + /** + * Get the contents of this buffer as a new InputStream. + *

+ * Calls to this method are cheap, since all the new streams will share the same + * underlying array + *

+ *

+ * This call finalizes the buffer. After this call, any attempt to add more content + * will throw an IOException. + *

+ * + * @return The buffered data + */ + public InputStream getInputStream() { + ensureClosed(); + if (outputBuffer.size() <= 0) { + return new ByteArrayInputStream(NO_BYTES); + } else { + return outputBuffer.toInputStream(); + } + } + + /** + * This call finalizes the buffer. After this call, any attempt to add more content + * will throw an IOException. + * + * @throws IOException + */ + public void close() throws IOException { + if (!isClosed) { + isClosed = true; + jsonGenerator.close(); + captureBuffer.close(); + } + } + + private void ensureClosed() { + try { + close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private static class OutputBuffer extends ByteArrayOutputStream { + InputStream toInputStream() { + return new ByteArrayInputStream(this.buf, 0, this.count); + } + } + + private static class ClosedOutputBuffer extends OutputStream { + static final ClosedOutputBuffer INSTANCE = new ClosedOutputBuffer(); + + @Override + public void write(int b) throws IOException { + throw new IOException("write to closed stream"); + } + + @Override + public void close() throws IOException { + } + } + + private static class CompressingOutputBuffer extends OutputStream { + private final OutputBuffer outputBuffer; + private final int requestCompressionSize; + private OutputStream delegate; + private int bytesUntilCompression; + private boolean isCompressed; + + private CompressingOutputBuffer(OutputBuffer outputBuffer, int requestCompressionSize) { + this.outputBuffer = outputBuffer; + this.delegate = outputBuffer; + this.requestCompressionSize = requestCompressionSize; + this.bytesUntilCompression = requestCompressionSize; + this.isCompressed = false; + } + + public boolean isCompressed() { + return isCompressed; + } + + @Override + public void write(byte[] b) throws IOException { + if ((bytesUntilCompression -= b.length) < 0) { + checkCompress(); + } + delegate.write(b); + } + + @Override + public void write(byte[] b, int off, int len) throws IOException { + if ((bytesUntilCompression -= len) < 0) { + checkCompress(); + } + delegate.write(b, off, len); + } + + @Override + public void write(int b) throws IOException { + if (--bytesUntilCompression < 0) { + checkCompress(); + } + delegate.write(b); + } + + private void checkCompress() throws IOException { + if (delegate == outputBuffer && requestCompressionSize < Integer.MAX_VALUE) { + // prevent future checks + this.bytesUntilCompression = Integer.MAX_VALUE; + byte[] uncompressed = outputBuffer.toByteArray(); + outputBuffer.reset(); + delegate = new GZIPOutputStream(outputBuffer); + if (uncompressed.length > 0) { + delegate.write(uncompressed); + } + isCompressed = true; + } + } + + @Override + public void flush() throws IOException { + delegate.flush(); + } + + @Override + public void close() throws IOException { + delegate.close(); + delegate = ClosedOutputBuffer.INSTANCE; + } + } +} diff --git a/java-client/src/test/java/org/opensearch/client/opensearch/integTest/aws/AwsSdk2BulkRequestIT.java b/java-client/src/test/java/org/opensearch/client/opensearch/integTest/aws/AwsSdk2BulkRequestIT.java new file mode 100644 index 0000000000..85fcb75a53 --- /dev/null +++ b/java-client/src/test/java/org/opensearch/client/opensearch/integTest/aws/AwsSdk2BulkRequestIT.java @@ -0,0 +1,65 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.client.opensearch.integTest.aws; + +import org.junit.Test; +import org.locationtech.jts.util.Assert; +import org.opensearch.client.opensearch.OpenSearchClient; +import org.opensearch.client.opensearch._types.Refresh; +import org.opensearch.client.opensearch._types.query_dsl.Query; +import org.opensearch.client.opensearch.core.BulkRequest; +import org.opensearch.client.opensearch.core.BulkResponse; +import org.opensearch.client.opensearch.core.SearchRequest; +import org.opensearch.client.opensearch.core.SearchResponse; +import org.opensearch.client.opensearch.core.bulk.BulkOperation; +import org.opensearch.client.opensearch.core.bulk.IndexOperation; + +import java.util.ArrayList; +import java.util.List; + +public class AwsSdk2BulkRequestIT extends AwsSdk2TransportTestCase { + + @Test + public void testBulkRequest() throws Exception { + resetTestIndex(false); + final OpenSearchClient client = getClient(false, null, null); + + ArrayList ops = new ArrayList<>(); + SimplePojo doc1 = new SimplePojo("Document 1", "The text of document 1"); + ops.add(new BulkOperation.Builder().index( + IndexOperation.of(io -> io.index(TEST_INDEX).id("id1").document(doc1)) + ).build()); + SimplePojo doc2 = new SimplePojo("Document 2", "The text of document 2"); + ops.add(new BulkOperation.Builder().index( + IndexOperation.of(io -> io.index(TEST_INDEX).id("id2").document(doc2)) + ).build()); + SimplePojo doc3 = getLongDoc("Long Document 3", 100000); + ops.add(new BulkOperation.Builder().index( + IndexOperation.of(io -> io.index(TEST_INDEX).id("id3").document(doc3)) + ).build()); + + BulkRequest.Builder bulkReq = new BulkRequest.Builder() + .index(TEST_INDEX) + .operations(ops) + .refresh(Refresh.WaitFor); + BulkResponse bulkResponse = client.bulk(bulkReq.build()); + Assert.equals(3, bulkResponse.items().size()); + + Query query = Query.of(qb -> qb.match(mb -> mb.field("title").query(fv -> fv.stringValue("Document")))); + final SearchRequest.Builder searchReq = new SearchRequest.Builder() + .allowPartialSearchResults(false) + .index(List.of(TEST_INDEX)) + .size(10) + .source(sc -> sc.fetch(false)) + .ignoreThrottled(false) + .query(query); + SearchResponse searchResponse = client.search(searchReq.build(), SimplePojo.class); + Assert.equals(3, searchResponse.hits().hits().size()); + } +} diff --git a/java-client/src/test/java/org/opensearch/client/opensearch/integTest/aws/AwsSdk2SearchIT.java b/java-client/src/test/java/org/opensearch/client/opensearch/integTest/aws/AwsSdk2SearchIT.java new file mode 100644 index 0000000000..f81c84ccbd --- /dev/null +++ b/java-client/src/test/java/org/opensearch/client/opensearch/integTest/aws/AwsSdk2SearchIT.java @@ -0,0 +1,136 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.client.opensearch.integTest.aws; + +import org.junit.Test; +import org.locationtech.jts.util.Assert; +import org.opensearch.client.opensearch.OpenSearchAsyncClient; +import org.opensearch.client.opensearch.OpenSearchClient; +import org.opensearch.client.opensearch._types.OpType; +import org.opensearch.client.opensearch._types.Refresh; +import org.opensearch.client.opensearch.core.IndexRequest; +import org.opensearch.client.opensearch.core.IndexResponse; +import org.opensearch.client.opensearch.core.SearchResponse; + +import java.util.List; +import java.util.concurrent.CompletableFuture; + +public class AwsSdk2SearchIT extends AwsSdk2TransportTestCase { + + @Test + public void testSyncClient() throws Exception { + testClient(false); + } + + @Test + public void testAsyncClient() throws Exception { + testClient(true); + } + + @Test + public void testSyncAsyncClient() throws Exception { + testClientAsync(false); + } + + @Test + public void testAsyncAsyncClient() throws Exception { + testClientAsync(true); + } + + void testClient(boolean async) throws Exception { + resetTestIndex(async); + final OpenSearchClient client = getClient(async, null, null); + + SimplePojo doc1 = new SimplePojo("Document 1", "The text of document 1"); + addDoc(client, "id1", doc1, false); + SimplePojo doc2 = new SimplePojo("Document 2", "The text of document 2"); + addDoc(client, "id2", doc2, false); + SimplePojo doc3 = getLongDoc("Long Document 3", 1000000); + addDoc(client, "id3", doc3, true); + + SearchResponse response = query(client, "NotPresent", null); + Assert.equals(0, response.hits().hits().size()); + + response = query(client, "Document", null); + Assert.equals(3, response.hits().hits().size()); + + response = query(client, "1", null); + Assert.equals(1, response.hits().hits().size()); + Assert.equals(doc1, response.hits().hits().get(0).source()); + + response = query(client, null, "wait"); + Assert.equals(1, response.hits().hits().size()); + Assert.equals(doc3, response.hits().hits().get(0).source()); + } + + void testClientAsync(boolean async) throws Exception { + resetTestIndex(async); + final OpenSearchAsyncClient client = getAsyncClient(async, null, null); + + SimplePojo doc1 = new SimplePojo("Document 1", "The text of document 1"); + CompletableFuture add1 = addDoc(client, "id1", doc1, false); + SimplePojo doc2 = new SimplePojo("Document 2", "The text of document 2"); + CompletableFuture add2 = addDoc(client, "id2", doc2, false); + SimplePojo doc3 = getLongDoc("Long Document 3", 1000000); + CompletableFuture add3 = CompletableFuture.allOf(add1, add2).thenCompose( + unused -> addDoc(client, "id3", doc3, true)); + + List> results = add3.thenCompose(unused -> { + CompletableFuture> r1 = query(client, "NotPresent", null); + CompletableFuture> r2 = query(client, "Document", null); + CompletableFuture> r3 = query(client, "1", null); + return CompletableFuture.allOf(r1, r2, r3).thenApply(u2 -> List.of( + r1.getNow(null), + r2.getNow(null), + r3.getNow(null)) + ); + }).get(); + + SearchResponse response = results.get(0); + Assert.equals(0, response.hits().hits().size()); + + response = results.get(1); + Assert.equals(3, response.hits().hits().size()); + + response = results.get(2); + Assert.equals(1, response.hits().hits().size()); + Assert.equals(doc1, response.hits().hits().get(0).source()); + } + + + private void addDoc(OpenSearchClient client, String id, SimplePojo doc, boolean wait) throws Exception { + IndexRequest.Builder req = new IndexRequest.Builder() + .index(TEST_INDEX) + .document(doc) + .id(id) + .opType(OpType.Index); + if (wait) { + req.refresh(Refresh.WaitFor); + } + client.index(req.build()); + } + + private CompletableFuture addDoc( + OpenSearchAsyncClient client, String id, SimplePojo doc, boolean wait + ) { + IndexRequest.Builder req = new IndexRequest.Builder() + .index(TEST_INDEX) + .document(doc) + .id(id) + .opType(OpType.Index); + if (wait) { + req.refresh(Refresh.WaitFor); + } + try { + return client.index(req.build()); + } catch (Exception e) { + return CompletableFuture.failedFuture(e); + } + } +} diff --git a/java-client/src/test/java/org/opensearch/client/opensearch/integTest/aws/AwsSdk2TransportTestCase.java b/java-client/src/test/java/org/opensearch/client/opensearch/integTest/aws/AwsSdk2TransportTestCase.java new file mode 100644 index 0000000000..05421559d1 --- /dev/null +++ b/java-client/src/test/java/org/opensearch/client/opensearch/integTest/aws/AwsSdk2TransportTestCase.java @@ -0,0 +1,304 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.client.opensearch.integTest.aws; + + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import org.junit.AfterClass; +import org.junit.Assume; +import org.junit.Before; +import org.opensearch.client.json.JsonpMapper; +import org.opensearch.client.opensearch.OpenSearchAsyncClient; +import org.opensearch.client.opensearch.OpenSearchClient; +import org.opensearch.client.opensearch._types.OpenSearchException; +import org.opensearch.client.opensearch._types.SortOptions; +import org.opensearch.client.opensearch._types.SortOrder; +import org.opensearch.client.opensearch._types.query_dsl.Query; +import org.opensearch.client.opensearch.core.SearchRequest; +import org.opensearch.client.opensearch.core.SearchResponse; +import org.opensearch.client.opensearch.indices.CreateIndexRequest; +import org.opensearch.client.opensearch.indices.IndexState; +import org.opensearch.client.opensearch.indices.OpenSearchIndicesClient; +import org.opensearch.client.transport.TransportOptions; +import org.opensearch.client.transport.aws.AwsSdk2Transport; +import org.opensearch.client.transport.aws.AwsSdk2TransportOptions; +import software.amazon.awssdk.http.SdkHttpClient; +import software.amazon.awssdk.http.apache.ApacheHttpClient; +import software.amazon.awssdk.http.async.SdkAsyncHttpClient; +import software.amazon.awssdk.http.crt.AwsCrtAsyncHttpClient; +import software.amazon.awssdk.regions.Region; + +import javax.annotation.CheckForNull; +import java.io.IOException; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.CompletableFuture; + + +public abstract class AwsSdk2TransportTestCase { + public static final String TEST_INDEX = "opensearch-java-integtest"; + + private static SdkAsyncHttpClient asyncHttpClient; + private static SdkHttpClient httpClient; + + @Before + public void checkConfig() { + String host = getTestClusterHost(); + Assume.assumeTrue("AWS test cluster not configured", host != null && !host.isEmpty()); + } + + protected SdkAsyncHttpClient getAsyncHttpClient() { + if (asyncHttpClient == null) { + asyncHttpClient = AwsCrtAsyncHttpClient.create(); + } + return asyncHttpClient; + } + + protected SdkHttpClient getHttpClient() { + if (httpClient == null) { + httpClient = ApacheHttpClient.create(); + } + return httpClient; + } + + protected AwsSdk2TransportOptions.Builder getTransportOptions() { + return AwsSdk2TransportOptions.builder(); + } + + protected OpenSearchClient getClient( + boolean async, + @CheckForNull JsonpMapper mapper, + @CheckForNull TransportOptions options) { + + AwsSdk2Transport transport; + if (async) { + transport = new AwsSdk2Transport( + getAsyncHttpClient(), + getTestClusterHost(), + getTestClusterRegion(), + getTransportOptions().build() + ); + } else { + transport = new AwsSdk2Transport( + getHttpClient(), + getTestClusterHost(), + getTestClusterRegion(), + getTransportOptions().build() + ); + } + return new OpenSearchClient(transport); + } + + protected OpenSearchAsyncClient getAsyncClient( + boolean async, + @CheckForNull JsonpMapper mapper, + @CheckForNull TransportOptions options) { + + AwsSdk2Transport transport; + if (async) { + transport = new AwsSdk2Transport( + getAsyncHttpClient(), + getTestClusterHost(), + getTestClusterRegion(), + getTransportOptions().build() + ); + } else { + transport = new AwsSdk2Transport( + getHttpClient(), + getTestClusterHost(), + getTestClusterRegion(), + getTransportOptions().build() + ); + } + return new OpenSearchAsyncClient(transport); + } + + protected OpenSearchIndicesClient getIndexesClient( + boolean async, + @CheckForNull JsonpMapper mapper, + @CheckForNull TransportOptions options) { + + AwsSdk2Transport transport; + if (async) { + transport = new AwsSdk2Transport( + getAsyncHttpClient(), + getTestClusterHost(), + getTestClusterRegion(), + getTransportOptions().build() + ); + } else { + transport = new AwsSdk2Transport( + getHttpClient(), + getTestClusterHost(), + getTestClusterRegion(), + getTransportOptions().build() + ); + } + return new OpenSearchIndicesClient(transport); + } + + protected String getTestClusterHost() { + String cluster = System.getProperty("tests.awsSdk2support.domainHost"); + return cluster; + } + + protected Region getTestClusterRegion() { + String region = System.getProperty("tests.awsSdk2support.domainRegion"); + return region != null ? Region.of(region) : Region.US_EAST_1; + } + + @AfterClass + public static void cleanupClients() throws IOException { + if (httpClient != null) { + try { + httpClient.close(); + } catch (Throwable e) { + // Not our problem + } + } + if (asyncHttpClient != null) { + try { + asyncHttpClient.close(); + } catch (Throwable e) { + // Not our problem + } + } + } + + public SimplePojo getLongDoc(String title, int minSize) { + StringBuilder sb = new StringBuilder(); + while (sb.length() < minSize) { + int len = sb.length(); + if (len < 1) { + sb.append("The length of this content is " + len + "...\n"); + } else { + sb.append("Oh, wait... now it's " + len + "...\n"); + } + } + return new SimplePojo(title, sb.toString()); + } + + public void resetTestIndex(boolean async) throws Exception { + OpenSearchIndicesClient client = getIndexesClient(async, null, null); + boolean indexExists = false; + try { + IndexState indexInfo = client.get(b -> b.index(TEST_INDEX)).get(TEST_INDEX); + if (indexInfo != null) { + indexExists = true; + + } + } catch ( + OpenSearchException e) { + if (e.status() != 404) { + throw e; + } + } + if (indexExists) { + client.delete(b -> b.index(List.of(TEST_INDEX))); + } + var req = new CreateIndexRequest.Builder() + .index(TEST_INDEX); + client.create(req.build()); + } + + protected SearchResponse query(OpenSearchClient client, String title, String text) throws Exception { + var query = Query.of(qb -> { + if (title != null) { + qb.match(mb -> mb.field("title").query(vb -> vb.stringValue(title))); + } + if (text != null) { + qb.match(mb -> mb.field("text").query(vb -> vb.stringValue(text))); + } + return qb; + }); + final SearchRequest.Builder req = new SearchRequest.Builder() + .allowPartialSearchResults(false) + .index(List.of(TEST_INDEX)) + .size(10) + .ignoreThrottled(false) + .sort( + new SortOptions.Builder().score(o -> o.order(SortOrder.Desc)).build(), + new SortOptions.Builder().doc(o -> o.order(SortOrder.Desc)).build() + ) + .query(query); + + + return client.search(req.build(), SimplePojo.class); + } + + protected CompletableFuture> query( + OpenSearchAsyncClient client, String title, String text + ) { + var query = Query.of(qb -> { + if (title != null) { + qb.match(mb -> mb.field("title").query(vb -> vb.stringValue(title))); + } + if (text != null) { + qb.match(mb -> mb.field("text").query(vb -> vb.stringValue(text))); + } + return qb; + }); + final SearchRequest.Builder req = new SearchRequest.Builder() + .allowPartialSearchResults(false) + .index(List.of(TEST_INDEX)) + .size(10) + .ignoreThrottled(false) + .sort( + new SortOptions.Builder().score(o -> o.order(SortOrder.Desc)).build(), + new SortOptions.Builder().doc(o -> o.order(SortOrder.Desc)).build() + ) + .query(query); + + try { + return client.search(req.build(), SimplePojo.class); + } catch (Exception e) { + return CompletableFuture.failedFuture(e); + } + } + + @JsonInclude(JsonInclude.Include.NON_NULL) + @JsonIgnoreProperties(ignoreUnknown = true) + public static class SimplePojo { + final String title; + final String text; + + public SimplePojo( + @JsonProperty("title") String title, + @JsonProperty("text") String text) { + this.title = title; + this.text = text; + } + + @Override + public int hashCode() { + return Objects.hash(getClass(), title, text); + } + + @Override + public boolean equals(Object obj) { + if (obj.getClass() != getClass()) { + return false; + } + SimplePojo other = (SimplePojo) obj; + return Objects.equals(title, other.title) && Objects.equals(text, other.text); + } + + @JsonProperty("title") + public String getTitle() { + return title; + } + + @JsonProperty("text") + public String getText() { + return text; + } + } +} From cc8e635707a530040fceccc8dd7202b550c6fd72 Mon Sep 17 00:00:00 2001 From: Matt Timmermans Date: Tue, 12 Jul 2022 16:22:10 -0400 Subject: [PATCH 2/4] Fix license headers on new files Signed-off-by: Matt Timmermans --- .../aws/AsyncByteArrayContentPublisher.java | 24 ------------------- .../aws/AsyncCapturingResponseHandler.java | 24 ------------------- .../aws/AsyncCapturingSubscriber.java | 24 ------------------- .../transport/aws/AwsSdk2Transport.java | 24 ------------------- .../aws/AwsSdk2TransportOptions.java | 24 ------------------- .../util/OpenSearchRequestBodyBuffer.java | 24 ------------------- 6 files changed, 144 deletions(-) diff --git a/java-client/src/main/java/org/opensearch/client/transport/aws/AsyncByteArrayContentPublisher.java b/java-client/src/main/java/org/opensearch/client/transport/aws/AsyncByteArrayContentPublisher.java index a61f1e2807..5fd975c836 100644 --- a/java-client/src/main/java/org/opensearch/client/transport/aws/AsyncByteArrayContentPublisher.java +++ b/java-client/src/main/java/org/opensearch/client/transport/aws/AsyncByteArrayContentPublisher.java @@ -6,30 +6,6 @@ * compatible open source license. */ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. 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. - */ - -/* - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - package org.opensearch.client.transport.aws; import org.reactivestreams.Subscriber; diff --git a/java-client/src/main/java/org/opensearch/client/transport/aws/AsyncCapturingResponseHandler.java b/java-client/src/main/java/org/opensearch/client/transport/aws/AsyncCapturingResponseHandler.java index 08ddf70eb6..5e9589f131 100644 --- a/java-client/src/main/java/org/opensearch/client/transport/aws/AsyncCapturingResponseHandler.java +++ b/java-client/src/main/java/org/opensearch/client/transport/aws/AsyncCapturingResponseHandler.java @@ -6,30 +6,6 @@ * compatible open source license. */ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. 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. - */ - -/* - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - package org.opensearch.client.transport.aws; import org.reactivestreams.Publisher; diff --git a/java-client/src/main/java/org/opensearch/client/transport/aws/AsyncCapturingSubscriber.java b/java-client/src/main/java/org/opensearch/client/transport/aws/AsyncCapturingSubscriber.java index 9857ec5c9c..c84da81c13 100644 --- a/java-client/src/main/java/org/opensearch/client/transport/aws/AsyncCapturingSubscriber.java +++ b/java-client/src/main/java/org/opensearch/client/transport/aws/AsyncCapturingSubscriber.java @@ -6,30 +6,6 @@ * compatible open source license. */ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. 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. - */ - -/* - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - package org.opensearch.client.transport.aws; import org.reactivestreams.Subscriber; diff --git a/java-client/src/main/java/org/opensearch/client/transport/aws/AwsSdk2Transport.java b/java-client/src/main/java/org/opensearch/client/transport/aws/AwsSdk2Transport.java index 5e6eeefaed..1069cf7832 100644 --- a/java-client/src/main/java/org/opensearch/client/transport/aws/AwsSdk2Transport.java +++ b/java-client/src/main/java/org/opensearch/client/transport/aws/AwsSdk2Transport.java @@ -6,30 +6,6 @@ * compatible open source license. */ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. 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. - */ - -/* - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - package org.opensearch.client.transport.aws; import jakarta.json.JsonObject; diff --git a/java-client/src/main/java/org/opensearch/client/transport/aws/AwsSdk2TransportOptions.java b/java-client/src/main/java/org/opensearch/client/transport/aws/AwsSdk2TransportOptions.java index 76046d7bde..ef8eb0a738 100644 --- a/java-client/src/main/java/org/opensearch/client/transport/aws/AwsSdk2TransportOptions.java +++ b/java-client/src/main/java/org/opensearch/client/transport/aws/AwsSdk2TransportOptions.java @@ -6,30 +6,6 @@ * compatible open source license. */ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. 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. - */ - -/* - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - package org.opensearch.client.transport.aws; import org.opensearch.client.json.JsonpMapper; diff --git a/java-client/src/main/java/org/opensearch/client/util/OpenSearchRequestBodyBuffer.java b/java-client/src/main/java/org/opensearch/client/util/OpenSearchRequestBodyBuffer.java index dc64148451..8302ccd148 100644 --- a/java-client/src/main/java/org/opensearch/client/util/OpenSearchRequestBodyBuffer.java +++ b/java-client/src/main/java/org/opensearch/client/util/OpenSearchRequestBodyBuffer.java @@ -6,30 +6,6 @@ * compatible open source license. */ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. 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. - */ - -/* - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - package org.opensearch.client.util; import jakarta.json.stream.JsonGenerator; From e80ed3afb3c43fd13294dcc1328fc848ba08578e Mon Sep 17 00:00:00 2001 From: Matt Timmermans Date: Tue, 12 Jul 2022 16:34:37 -0400 Subject: [PATCH 3/4] New AWS sdk release isn't all in the cache yet Signed-off-by: Matt Timmermans --- java-client/build.gradle.kts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/java-client/build.gradle.kts b/java-client/build.gradle.kts index 04afeacebb..b2f94cac40 100644 --- a/java-client/build.gradle.kts +++ b/java-client/build.gradle.kts @@ -165,11 +165,11 @@ dependencies { // For AwsSdk2Transport "awsSdk2SupportImplementation"("software.amazon.awssdk","sdk-core","[2.15,3.0)") "awsSdk2SupportImplementation"("software.amazon.awssdk","auth","[2.15,3.0)") - testImplementation("software.amazon.awssdk","sdk-core","[2.15,3.0)") - testImplementation("software.amazon.awssdk","auth","[2.15,3.0)") - testImplementation("software.amazon.awssdk","aws-crt-client","[2.15,3.0)") - testImplementation("software.amazon.awssdk","apache-client","[2.15,3.0)") - testImplementation("software.amazon.awssdk","sts","[2.15,3.0)") + testImplementation("software.amazon.awssdk","sdk-core","[2.15,2.17.230)") + testImplementation("software.amazon.awssdk","auth","[2.15,2.17.230)") + testImplementation("software.amazon.awssdk","aws-crt-client","[2.15,2.17.230)") + testImplementation("software.amazon.awssdk","apache-client","[2.15,2.17.230)") + testImplementation("software.amazon.awssdk","sts","[2.15,2.17.230)") // EPL-2.0 OR BSD-3-Clause // https://eclipse-ee4j.github.io/yasson/ From 9b17175a90ad8f58abce7b2cd95a44f55ffbbd9d Mon Sep 17 00:00:00 2001 From: Matt Timmermans Date: Tue, 12 Jul 2022 16:45:46 -0400 Subject: [PATCH 4/4] Fix license headers in new file Signed-off-by: Matt Timmermans --- java-client/build.gradle.kts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/java-client/build.gradle.kts b/java-client/build.gradle.kts index b2f94cac40..04afeacebb 100644 --- a/java-client/build.gradle.kts +++ b/java-client/build.gradle.kts @@ -165,11 +165,11 @@ dependencies { // For AwsSdk2Transport "awsSdk2SupportImplementation"("software.amazon.awssdk","sdk-core","[2.15,3.0)") "awsSdk2SupportImplementation"("software.amazon.awssdk","auth","[2.15,3.0)") - testImplementation("software.amazon.awssdk","sdk-core","[2.15,2.17.230)") - testImplementation("software.amazon.awssdk","auth","[2.15,2.17.230)") - testImplementation("software.amazon.awssdk","aws-crt-client","[2.15,2.17.230)") - testImplementation("software.amazon.awssdk","apache-client","[2.15,2.17.230)") - testImplementation("software.amazon.awssdk","sts","[2.15,2.17.230)") + testImplementation("software.amazon.awssdk","sdk-core","[2.15,3.0)") + testImplementation("software.amazon.awssdk","auth","[2.15,3.0)") + testImplementation("software.amazon.awssdk","aws-crt-client","[2.15,3.0)") + testImplementation("software.amazon.awssdk","apache-client","[2.15,3.0)") + testImplementation("software.amazon.awssdk","sts","[2.15,3.0)") // EPL-2.0 OR BSD-3-Clause // https://eclipse-ee4j.github.io/yasson/