diff --git a/CHANGELOG.md b/CHANGELOG.md
index f97b8646de..e215e44868 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -18,7 +18,7 @@ This section is for maintaining a changelog for all breaking changes for the cli
- Document HTTP/2 support ([#330](https://github.com/opensearch-project/opensearch-java/pull/330))
- Expose HTTP status code through `ResponseException#status` ([#756](https://github.com/opensearch-project/opensearch-java/pull/756))
- Added missing WrapperQuery accessors and builder methods ([#806](https://github.com/opensearch-project/opensearch-java/pull/806))
-
+- Added a sample for creating OpenSearchClient via dynamic proxy ([#847](https://github.com/opensearch-project/opensearch-java/pull/847))
### Dependencies
diff --git a/samples/build.gradle.kts b/samples/build.gradle.kts
index 0d9577107e..a3dc7eb403 100644
--- a/samples/build.gradle.kts
+++ b/samples/build.gradle.kts
@@ -24,6 +24,7 @@ dependencies {
implementation("org.apache.logging.log4j", "log4j-slf4j2-impl","[2.17.1,3.0)")
implementation("commons-logging", "commons-logging", "1.2")
implementation("com.fasterxml.jackson.core", "jackson-databind", "2.15.2")
+ implementation("cglib", "cglib", "3.3.0")
}
spotless {
diff --git a/samples/src/main/java/org/opensearch/client/samples/OpenSearchClientProxy.java b/samples/src/main/java/org/opensearch/client/samples/OpenSearchClientProxy.java
new file mode 100644
index 0000000000..84e376de28
--- /dev/null
+++ b/samples/src/main/java/org/opensearch/client/samples/OpenSearchClientProxy.java
@@ -0,0 +1,58 @@
+/*
+ * 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.samples;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import net.sf.cglib.proxy.Enhancer;
+import net.sf.cglib.proxy.MethodInterceptor;
+import net.sf.cglib.proxy.MethodProxy;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.opensearch.client.opensearch.OpenSearchClient;
+import org.opensearch.client.transport.OpenSearchTransport;
+
+public class OpenSearchClientProxy implements MethodInterceptor {
+
+ private static final Logger LOGGER = LogManager.getLogger(OpenSearchClientProxy.class);
+
+ public static OpenSearchClient create(OpenSearchTransport transport) throws Exception {
+ if (transport == null) {
+ throw new Exception("Cannot build OpenSearchClient without a transport.");
+ }
+
+ LOGGER.info("Using {} transport", transport.getClass().getName());
+
+ final var interceptor = new OpenSearchClientProxy();
+ final var enhancer = new Enhancer();
+
+ enhancer.setSuperclass(OpenSearchClient.class);
+ enhancer.setCallback(interceptor);
+
+ final var argTypes = new Class[] { OpenSearchTransport.class };
+ final var args = new Object[] { transport };
+ return (OpenSearchClient) enhancer.create(argTypes, args);
+ }
+
+ @Override
+ public Object intercept(final Object obj, final Method method, final Object[] args, final MethodProxy proxy) throws Throwable {
+ LOGGER.info("Invoking method: {}", method.getName());
+ final Object result;
+
+ try {
+ LOGGER.info("Entering method: {}", method.getName());
+ result = proxy.invokeSuper(obj, args);
+ } catch (final InvocationTargetException e) {
+ throw e.getTargetException();
+ } finally {
+ LOGGER.info("Exiting method: {}", method.getName());
+ }
+ return result;
+ }
+}
diff --git a/samples/src/main/java/org/opensearch/client/samples/SampleClient.java b/samples/src/main/java/org/opensearch/client/samples/SampleClient.java
index b7b735037e..0426a84baf 100644
--- a/samples/src/main/java/org/opensearch/client/samples/SampleClient.java
+++ b/samples/src/main/java/org/opensearch/client/samples/SampleClient.java
@@ -21,10 +21,12 @@
import org.apache.hc.core5.ssl.SSLContextBuilder;
import org.opensearch.client.json.jackson.JacksonJsonpMapper;
import org.opensearch.client.opensearch.OpenSearchClient;
+import org.opensearch.client.transport.OpenSearchTransport;
import org.opensearch.client.transport.httpclient5.ApacheHttpClient5TransportBuilder;
public class SampleClient {
- public static OpenSearchClient create() throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
+
+ public static OpenSearchTransport createTransport() throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
var env = System.getenv();
var https = Boolean.parseBoolean(env.getOrDefault("HTTPS", "true"));
var hostname = env.getOrDefault("HOST", "localhost");
@@ -36,7 +38,7 @@ public static OpenSearchClient create() throws NoSuchAlgorithmException, KeyStor
final var sslContext = SSLContextBuilder.create().loadTrustMaterial(null, (chains, authType) -> true).build();
- final var transport = ApacheHttpClient5TransportBuilder.builder(hosts)
+ return ApacheHttpClient5TransportBuilder.builder(hosts)
.setMapper(new JacksonJsonpMapper())
.setHttpClientConfigCallback(httpClientBuilder -> {
final var credentialsProvider = new BasicCredentialsProvider();
@@ -55,6 +57,9 @@ public static OpenSearchClient create() throws NoSuchAlgorithmException, KeyStor
return httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider).setConnectionManager(connectionManager);
})
.build();
- return new OpenSearchClient(transport);
+ }
+
+ public static OpenSearchClient create() throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
+ return new OpenSearchClient(createTransport());
}
}
diff --git a/samples/src/main/java/org/opensearch/client/samples/SampleProxyClient.java b/samples/src/main/java/org/opensearch/client/samples/SampleProxyClient.java
new file mode 100644
index 0000000000..5957a98aca
--- /dev/null
+++ b/samples/src/main/java/org/opensearch/client/samples/SampleProxyClient.java
@@ -0,0 +1,73 @@
+package org.opensearch.client.samples;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.opensearch.client.opensearch._types.mapping.IntegerNumberProperty;
+import org.opensearch.client.opensearch._types.mapping.Property;
+import org.opensearch.client.opensearch._types.mapping.TypeMapping;
+import org.opensearch.client.opensearch.core.IndexRequest;
+import org.opensearch.client.opensearch.core.SearchResponse;
+import org.opensearch.client.opensearch.indices.CreateIndexRequest;
+import org.opensearch.client.opensearch.indices.DeleteIndexRequest;
+import org.opensearch.client.opensearch.indices.IndexSettings;
+import org.opensearch.client.samples.util.IndexData;
+
+/**
+ * Run with: ./gradlew :samples:run -Dsamples.mainClass=SampleProxyClient
+ */
+public class SampleProxyClient {
+
+ private static final Logger LOGGER = LogManager.getLogger(SampleProxyClient.class);
+
+ public static void main(String[] args) {
+ try {
+ var transport = SampleClient.createTransport();
+ var client = OpenSearchClientProxy.create(transport);
+
+ final var indexName = "my-index";
+
+ if (!client.indices().exists(r -> r.index(indexName)).value()) {
+ LOGGER.info("Creating index {}", indexName);
+ IndexSettings settings = new IndexSettings.Builder().numberOfShards("2").numberOfReplicas("1").build();
+ TypeMapping mapping = new TypeMapping.Builder().properties(
+ "age",
+ new Property.Builder().integer(new IntegerNumberProperty.Builder().build()).build()
+ ).build();
+ CreateIndexRequest createIndexRequest = new CreateIndexRequest.Builder().index(indexName)
+ .settings(settings)
+ .mappings(mapping)
+ .build();
+ client.indices().create(createIndexRequest);
+ }
+
+ LOGGER.info("Indexing documents");
+ IndexData indexData = new IndexData("Document 1", "Text for document 1");
+ IndexRequest indexRequest = new IndexRequest.Builder().index(indexName)
+ .id("1")
+ .document(indexData)
+ .build();
+ client.index(indexRequest);
+
+ indexData = new IndexData("Document 2", "Text for document 2");
+ indexRequest = new IndexRequest.Builder().index(indexName).id("2").document(indexData).build();
+ client.index(indexRequest);
+
+ // wait for the document to index
+ Thread.sleep(3000);
+
+ SearchResponse searchResponse = client.search(s -> s.index(indexName), IndexData.class);
+ for (var hit : searchResponse.hits().hits()) {
+ LOGGER.info("Found {} with score {}", hit.source(), hit.score());
+ }
+
+ LOGGER.info("Deleting document with id 1");
+ client.delete(d -> d.index(indexName).id("1"));
+
+ LOGGER.info("Deleting index {}", indexName);
+ DeleteIndexRequest deleteIndexRequest = new DeleteIndexRequest.Builder().index(indexName).build();
+ client.indices().delete(deleteIndexRequest);
+ } catch (Exception e) {
+ LOGGER.error("Unexpected exception", e);
+ }
+ }
+}