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); + } + } +}