This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/main by this push:
     new 8fbaf0869f61 CAMEL-26556: Allow configuring OpenSearch clients per 
endpoint (#29117)
8fbaf0869f61 is described below

commit 8fbaf0869f61a29f1993d27c8db5229985d2115b
Author: Bartosz Popiela <[email protected]>
AuthorDate: Wed Nov 4 09:14:18 2026 +0100

    CAMEL-26556: Allow configuring OpenSearch clients per endpoint (#29117)
---
 .../camel/catalog/components/opensearch.json       |  11 +-
 .../camel/catalog/docs/opensearch-component.adoc   |  25 ++++
 .../opensearch/OpensearchEndpointConfigurer.java   |  11 ++
 .../opensearch/OpensearchEndpointUriFactory.java   |   3 +-
 .../camel/component/opensearch/opensearch.json     |  11 +-
 .../src/main/docs/opensearch-component.adoc        |  25 ++++
 .../component/opensearch/OpensearchEndpoint.java   |   8 +
 .../opensearch/OpensearchEndpointClientTest.java   | 164 +++++++++++++++++++++
 .../dsl/OpensearchEndpointBuilderFactory.java      |  36 +++++
 9 files changed, 283 insertions(+), 11 deletions(-)

diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/opensearch.json
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/opensearch.json
index 146664596a51..911d1cd63484 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/opensearch.json
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/opensearch.json
@@ -74,10 +74,11 @@
     "documentClass": { "index": 14, "kind": "parameter", "displayName": 
"Document Class", "group": "advanced", "label": "advanced", "required": false, 
"type": "string", "javaType": "java.lang.Class<java.lang.Object>", 
"deprecated": false, "autowired": false, "secret": false, "defaultValue": 
"ObjectNode", "configurationClass": 
"org.apache.camel.component.opensearch.OpensearchConfiguration", 
"configurationField": "configuration", "description": "The class to use when 
deserializing the docu [...]
     "enableSniffer": { "index": 15, "kind": "parameter", "displayName": 
"Enable Sniffer", "group": "advanced", "label": "advanced", "required": false, 
"type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": 
false, "secret": false, "defaultValue": false, "configurationClass": 
"org.apache.camel.component.opensearch.OpensearchConfiguration", 
"configurationField": "configuration", "description": "Enable automatically 
discover nodes from a running OpenSearch cluster. If th [...]
     "hostnameVerifier": { "index": 16, "kind": "parameter", "displayName": 
"Hostname Verifier", "group": "advanced", "label": "advanced", "required": 
false, "type": "object", "javaType": "javax.net.ssl.HostnameVerifier", 
"deprecated": false, "autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.opensearch.OpensearchConfiguration", 
"configurationField": "configuration", "description": "The class to use as 
HostnameVerifier. By default there is no HostnameVe [...]
-    "sniffAfterFailureDelay": { "index": 17, "kind": "parameter", 
"displayName": "Sniff After Failure Delay", "group": "advanced", "label": 
"advanced", "required": false, "type": "integer", "javaType": "int", 
"deprecated": false, "autowired": false, "secret": false, "defaultValue": 
60000, "configurationClass": 
"org.apache.camel.component.opensearch.OpensearchConfiguration", 
"configurationField": "configuration", "description": "The delay of a sniff 
execution scheduled after a failure (in [...]
-    "snifferInterval": { "index": 18, "kind": "parameter", "displayName": 
"Sniffer Interval", "group": "advanced", "label": "advanced", "required": 
false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": 
false, "secret": false, "defaultValue": 300000, "configurationClass": 
"org.apache.camel.component.opensearch.OpensearchConfiguration", 
"configurationField": "configuration", "description": "The interval between 
consecutive ordinary sniff executions in milliseconds. [...]
-    "certificatePath": { "index": 19, "kind": "parameter", "displayName": 
"Certificate Path", "group": "security", "label": "security", "required": 
false, "type": "string", "javaType": "java.lang.String", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, 
"supportFileReference": true, "configurationClass": 
"org.apache.camel.component.opensearch.OpensearchConfiguration", 
"configurationField": "configuration", "description": "The certificate that can 
be used t [...]
-    "enableSSL": { "index": 20, "kind": "parameter", "displayName": "Enable 
SSL", "group": "security", "label": "security", "required": false, "type": 
"boolean", "javaType": "boolean", "deprecated": false, "autowired": false, 
"secret": false, "defaultValue": false, "configurationClass": 
"org.apache.camel.component.opensearch.OpensearchConfiguration", 
"configurationField": "configuration", "description": "Enable SSL" },
-    "sslContextParameters": { "index": 21, "kind": "parameter", "displayName": 
"Ssl Context Parameters", "group": "security", "label": "security", "required": 
false, "type": "object", "javaType": 
"org.apache.camel.support.jsse.SSLContextParameters", "deprecated": false, 
"autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.opensearch.OpensearchConfiguration", 
"configurationField": "configuration", "description": "To configure security 
using SSLContextPara [...]
+    "openSearchClient": { "index": 17, "kind": "parameter", "displayName": 
"Open Search Client", "group": "advanced", "label": "advanced", "required": 
false, "type": "object", "javaType": 
"org.opensearch.client.opensearch.OpenSearchClient", "deprecated": false, 
"deprecationNote": "", "autowired": true, "secret": false, "description": "To 
use a custom configured OpenSearchClient instance for this endpoint. This takes 
precedence over the component's OpenSearchClient and RestClient options. [...]
+    "sniffAfterFailureDelay": { "index": 18, "kind": "parameter", 
"displayName": "Sniff After Failure Delay", "group": "advanced", "label": 
"advanced", "required": false, "type": "integer", "javaType": "int", 
"deprecated": false, "autowired": false, "secret": false, "defaultValue": 
60000, "configurationClass": 
"org.apache.camel.component.opensearch.OpensearchConfiguration", 
"configurationField": "configuration", "description": "The delay of a sniff 
execution scheduled after a failure (in [...]
+    "snifferInterval": { "index": 19, "kind": "parameter", "displayName": 
"Sniffer Interval", "group": "advanced", "label": "advanced", "required": 
false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": 
false, "secret": false, "defaultValue": 300000, "configurationClass": 
"org.apache.camel.component.opensearch.OpensearchConfiguration", 
"configurationField": "configuration", "description": "The interval between 
consecutive ordinary sniff executions in milliseconds. [...]
+    "certificatePath": { "index": 20, "kind": "parameter", "displayName": 
"Certificate Path", "group": "security", "label": "security", "required": 
false, "type": "string", "javaType": "java.lang.String", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, 
"supportFileReference": true, "configurationClass": 
"org.apache.camel.component.opensearch.OpensearchConfiguration", 
"configurationField": "configuration", "description": "The certificate that can 
be used t [...]
+    "enableSSL": { "index": 21, "kind": "parameter", "displayName": "Enable 
SSL", "group": "security", "label": "security", "required": false, "type": 
"boolean", "javaType": "boolean", "deprecated": false, "autowired": false, 
"secret": false, "defaultValue": false, "configurationClass": 
"org.apache.camel.component.opensearch.OpensearchConfiguration", 
"configurationField": "configuration", "description": "Enable SSL" },
+    "sslContextParameters": { "index": 22, "kind": "parameter", "displayName": 
"Ssl Context Parameters", "group": "security", "label": "security", "required": 
false, "type": "object", "javaType": 
"org.apache.camel.support.jsse.SSLContextParameters", "deprecated": false, 
"autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.opensearch.OpensearchConfiguration", 
"configurationField": "configuration", "description": "To configure security 
using SSLContextPara [...]
   }
 }
diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/opensearch-component.adoc
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/opensearch-component.adoc
index 0059906dcada..de0f28e29688 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/opensearch-component.adoc
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/opensearch-component.adoc
@@ -131,6 +131,31 @@ camelContext.addComponent("opensearch", 
opensearchComponent);
 ----
 
 
+=== Custom clients per endpoint
+
+Use the `openSearchClient` endpoint option to reference an
+`org.opensearch.client.opensearch.OpenSearchClient` in the Camel registry.
+Each endpoint can use a different client, allowing a single route to connect to
+different OpenSearch services with their own authentication and transport 
settings.
+
+[source,java]
+----
+camelContext.getRegistry().bind("firstClient", firstClient);
+camelContext.getRegistry().bind("secondClient", secondClient);
+
+from("direct:ping")
+    .to("opensearch:first?operation=Ping&openSearchClient=#bean:firstClient")
+    
.to("opensearch:second?operation=Ping&openSearchClient=#bean:secondClient");
+----
+
+The endpoint client takes precedence over the component's `openSearchClient` 
and
+`client` options. Without an endpoint override, the component's client 
configuration
+continues to apply. Configure connection and authentication settings on the 
supplied
+client; `hostAddresses` is not required when supplying a client.
+
+The application owns supplied clients and must close them when they are no 
longer
+needed. Camel does not close them when a route stops or when `disconnect=true`.
+
 === Document type
 
 For all the search operations, it is possible to indicate the type of document 
to retrieve to get the result already unmarshalled with the expected type.
diff --git 
a/components/camel-opensearch/src/generated/java/org/apache/camel/component/opensearch/OpensearchEndpointConfigurer.java
 
b/components/camel-opensearch/src/generated/java/org/apache/camel/component/opensearch/OpensearchEndpointConfigurer.java
index 375574892d52..777bc2eb6b5d 100644
--- 
a/components/camel-opensearch/src/generated/java/org/apache/camel/component/opensearch/OpensearchEndpointConfigurer.java
+++ 
b/components/camel-opensearch/src/generated/java/org/apache/camel/component/opensearch/OpensearchEndpointConfigurer.java
@@ -46,6 +46,8 @@ public class OpensearchEndpointConfigurer extends 
PropertyConfigurerSupport impl
         case "lazyStartProducer": 
target.setLazyStartProducer(property(camelContext, boolean.class, value)); 
return true;
         case "maxretrytimeout":
         case "maxRetryTimeout": 
target.getConfiguration().setMaxRetryTimeout(property(camelContext, int.class, 
value)); return true;
+        case "opensearchclient":
+        case "openSearchClient": 
target.setOpenSearchClient(property(camelContext, 
org.opensearch.client.opensearch.OpenSearchClient.class, value)); return true;
         case "operation": 
target.getConfiguration().setOperation(property(camelContext, 
org.apache.camel.component.opensearch.OpensearchOperation.class, value)); 
return true;
         case "scrollkeepalivems":
         case "scrollKeepAliveMs": 
target.getConfiguration().setScrollKeepAliveMs(property(camelContext, 
int.class, value)); return true;
@@ -66,6 +68,11 @@ public class OpensearchEndpointConfigurer extends 
PropertyConfigurerSupport impl
         }
     }
 
+    @Override
+    public String[] getAutowiredNames() {
+        return new String[]{"openSearchClient"};
+    }
+
     @Override
     public Class<?> getOptionType(String name, boolean ignoreCase) {
         switch (ignoreCase ? name.toLowerCase(Locale.ROOT) : name) {
@@ -91,6 +98,8 @@ public class OpensearchEndpointConfigurer extends 
PropertyConfigurerSupport impl
         case "lazyStartProducer": return boolean.class;
         case "maxretrytimeout":
         case "maxRetryTimeout": return int.class;
+        case "opensearchclient":
+        case "openSearchClient": return 
org.opensearch.client.opensearch.OpenSearchClient.class;
         case "operation": return 
org.apache.camel.component.opensearch.OpensearchOperation.class;
         case "scrollkeepalivems":
         case "scrollKeepAliveMs": return int.class;
@@ -137,6 +146,8 @@ public class OpensearchEndpointConfigurer extends 
PropertyConfigurerSupport impl
         case "lazyStartProducer": return target.isLazyStartProducer();
         case "maxretrytimeout":
         case "maxRetryTimeout": return 
target.getConfiguration().getMaxRetryTimeout();
+        case "opensearchclient":
+        case "openSearchClient": return target.getOpenSearchClient();
         case "operation": return target.getConfiguration().getOperation();
         case "scrollkeepalivems":
         case "scrollKeepAliveMs": return 
target.getConfiguration().getScrollKeepAliveMs();
diff --git 
a/components/camel-opensearch/src/generated/java/org/apache/camel/component/opensearch/OpensearchEndpointUriFactory.java
 
b/components/camel-opensearch/src/generated/java/org/apache/camel/component/opensearch/OpensearchEndpointUriFactory.java
index 0fa2f544082f..031e459a57c6 100644
--- 
a/components/camel-opensearch/src/generated/java/org/apache/camel/component/opensearch/OpensearchEndpointUriFactory.java
+++ 
b/components/camel-opensearch/src/generated/java/org/apache/camel/component/opensearch/OpensearchEndpointUriFactory.java
@@ -24,7 +24,7 @@ public class OpensearchEndpointUriFactory extends 
org.apache.camel.support.compo
     private static final Set<String> ENDPOINT_IDENTITY_PROPERTY_NAMES;
     private static final Map<String, String> MULTI_VALUE_PREFIXES;
     static {
-        Set<String> props = new HashSet<>(22);
+        Set<String> props = new HashSet<>(23);
         props.add("certificatePath");
         props.add("clusterName");
         props.add("connectionTimeout");
@@ -38,6 +38,7 @@ public class OpensearchEndpointUriFactory extends 
org.apache.camel.support.compo
         props.add("indexName");
         props.add("lazyStartProducer");
         props.add("maxRetryTimeout");
+        props.add("openSearchClient");
         props.add("operation");
         props.add("scrollKeepAliveMs");
         props.add("size");
diff --git 
a/components/camel-opensearch/src/generated/resources/META-INF/org/apache/camel/component/opensearch/opensearch.json
 
b/components/camel-opensearch/src/generated/resources/META-INF/org/apache/camel/component/opensearch/opensearch.json
index 146664596a51..911d1cd63484 100644
--- 
a/components/camel-opensearch/src/generated/resources/META-INF/org/apache/camel/component/opensearch/opensearch.json
+++ 
b/components/camel-opensearch/src/generated/resources/META-INF/org/apache/camel/component/opensearch/opensearch.json
@@ -74,10 +74,11 @@
     "documentClass": { "index": 14, "kind": "parameter", "displayName": 
"Document Class", "group": "advanced", "label": "advanced", "required": false, 
"type": "string", "javaType": "java.lang.Class<java.lang.Object>", 
"deprecated": false, "autowired": false, "secret": false, "defaultValue": 
"ObjectNode", "configurationClass": 
"org.apache.camel.component.opensearch.OpensearchConfiguration", 
"configurationField": "configuration", "description": "The class to use when 
deserializing the docu [...]
     "enableSniffer": { "index": 15, "kind": "parameter", "displayName": 
"Enable Sniffer", "group": "advanced", "label": "advanced", "required": false, 
"type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": 
false, "secret": false, "defaultValue": false, "configurationClass": 
"org.apache.camel.component.opensearch.OpensearchConfiguration", 
"configurationField": "configuration", "description": "Enable automatically 
discover nodes from a running OpenSearch cluster. If th [...]
     "hostnameVerifier": { "index": 16, "kind": "parameter", "displayName": 
"Hostname Verifier", "group": "advanced", "label": "advanced", "required": 
false, "type": "object", "javaType": "javax.net.ssl.HostnameVerifier", 
"deprecated": false, "autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.opensearch.OpensearchConfiguration", 
"configurationField": "configuration", "description": "The class to use as 
HostnameVerifier. By default there is no HostnameVe [...]
-    "sniffAfterFailureDelay": { "index": 17, "kind": "parameter", 
"displayName": "Sniff After Failure Delay", "group": "advanced", "label": 
"advanced", "required": false, "type": "integer", "javaType": "int", 
"deprecated": false, "autowired": false, "secret": false, "defaultValue": 
60000, "configurationClass": 
"org.apache.camel.component.opensearch.OpensearchConfiguration", 
"configurationField": "configuration", "description": "The delay of a sniff 
execution scheduled after a failure (in [...]
-    "snifferInterval": { "index": 18, "kind": "parameter", "displayName": 
"Sniffer Interval", "group": "advanced", "label": "advanced", "required": 
false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": 
false, "secret": false, "defaultValue": 300000, "configurationClass": 
"org.apache.camel.component.opensearch.OpensearchConfiguration", 
"configurationField": "configuration", "description": "The interval between 
consecutive ordinary sniff executions in milliseconds. [...]
-    "certificatePath": { "index": 19, "kind": "parameter", "displayName": 
"Certificate Path", "group": "security", "label": "security", "required": 
false, "type": "string", "javaType": "java.lang.String", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, 
"supportFileReference": true, "configurationClass": 
"org.apache.camel.component.opensearch.OpensearchConfiguration", 
"configurationField": "configuration", "description": "The certificate that can 
be used t [...]
-    "enableSSL": { "index": 20, "kind": "parameter", "displayName": "Enable 
SSL", "group": "security", "label": "security", "required": false, "type": 
"boolean", "javaType": "boolean", "deprecated": false, "autowired": false, 
"secret": false, "defaultValue": false, "configurationClass": 
"org.apache.camel.component.opensearch.OpensearchConfiguration", 
"configurationField": "configuration", "description": "Enable SSL" },
-    "sslContextParameters": { "index": 21, "kind": "parameter", "displayName": 
"Ssl Context Parameters", "group": "security", "label": "security", "required": 
false, "type": "object", "javaType": 
"org.apache.camel.support.jsse.SSLContextParameters", "deprecated": false, 
"autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.opensearch.OpensearchConfiguration", 
"configurationField": "configuration", "description": "To configure security 
using SSLContextPara [...]
+    "openSearchClient": { "index": 17, "kind": "parameter", "displayName": 
"Open Search Client", "group": "advanced", "label": "advanced", "required": 
false, "type": "object", "javaType": 
"org.opensearch.client.opensearch.OpenSearchClient", "deprecated": false, 
"deprecationNote": "", "autowired": true, "secret": false, "description": "To 
use a custom configured OpenSearchClient instance for this endpoint. This takes 
precedence over the component's OpenSearchClient and RestClient options. [...]
+    "sniffAfterFailureDelay": { "index": 18, "kind": "parameter", 
"displayName": "Sniff After Failure Delay", "group": "advanced", "label": 
"advanced", "required": false, "type": "integer", "javaType": "int", 
"deprecated": false, "autowired": false, "secret": false, "defaultValue": 
60000, "configurationClass": 
"org.apache.camel.component.opensearch.OpensearchConfiguration", 
"configurationField": "configuration", "description": "The delay of a sniff 
execution scheduled after a failure (in [...]
+    "snifferInterval": { "index": 19, "kind": "parameter", "displayName": 
"Sniffer Interval", "group": "advanced", "label": "advanced", "required": 
false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": 
false, "secret": false, "defaultValue": 300000, "configurationClass": 
"org.apache.camel.component.opensearch.OpensearchConfiguration", 
"configurationField": "configuration", "description": "The interval between 
consecutive ordinary sniff executions in milliseconds. [...]
+    "certificatePath": { "index": 20, "kind": "parameter", "displayName": 
"Certificate Path", "group": "security", "label": "security", "required": 
false, "type": "string", "javaType": "java.lang.String", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, 
"supportFileReference": true, "configurationClass": 
"org.apache.camel.component.opensearch.OpensearchConfiguration", 
"configurationField": "configuration", "description": "The certificate that can 
be used t [...]
+    "enableSSL": { "index": 21, "kind": "parameter", "displayName": "Enable 
SSL", "group": "security", "label": "security", "required": false, "type": 
"boolean", "javaType": "boolean", "deprecated": false, "autowired": false, 
"secret": false, "defaultValue": false, "configurationClass": 
"org.apache.camel.component.opensearch.OpensearchConfiguration", 
"configurationField": "configuration", "description": "Enable SSL" },
+    "sslContextParameters": { "index": 22, "kind": "parameter", "displayName": 
"Ssl Context Parameters", "group": "security", "label": "security", "required": 
false, "type": "object", "javaType": 
"org.apache.camel.support.jsse.SSLContextParameters", "deprecated": false, 
"autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.opensearch.OpensearchConfiguration", 
"configurationField": "configuration", "description": "To configure security 
using SSLContextPara [...]
   }
 }
diff --git 
a/components/camel-opensearch/src/main/docs/opensearch-component.adoc 
b/components/camel-opensearch/src/main/docs/opensearch-component.adoc
index 0059906dcada..de0f28e29688 100644
--- a/components/camel-opensearch/src/main/docs/opensearch-component.adoc
+++ b/components/camel-opensearch/src/main/docs/opensearch-component.adoc
@@ -131,6 +131,31 @@ camelContext.addComponent("opensearch", 
opensearchComponent);
 ----
 
 
+=== Custom clients per endpoint
+
+Use the `openSearchClient` endpoint option to reference an
+`org.opensearch.client.opensearch.OpenSearchClient` in the Camel registry.
+Each endpoint can use a different client, allowing a single route to connect to
+different OpenSearch services with their own authentication and transport 
settings.
+
+[source,java]
+----
+camelContext.getRegistry().bind("firstClient", firstClient);
+camelContext.getRegistry().bind("secondClient", secondClient);
+
+from("direct:ping")
+    .to("opensearch:first?operation=Ping&openSearchClient=#bean:firstClient")
+    
.to("opensearch:second?operation=Ping&openSearchClient=#bean:secondClient");
+----
+
+The endpoint client takes precedence over the component's `openSearchClient` 
and
+`client` options. Without an endpoint override, the component's client 
configuration
+continues to apply. Configure connection and authentication settings on the 
supplied
+client; `hostAddresses` is not required when supplying a client.
+
+The application owns supplied clients and must close them when they are no 
longer
+needed. Camel does not close them when a route stops or when `disconnect=true`.
+
 === Document type
 
 For all the search operations, it is possible to indicate the type of document 
to retrieve to get the result already unmarshalled with the expected type.
diff --git 
a/components/camel-opensearch/src/main/java/org/apache/camel/component/opensearch/OpensearchEndpoint.java
 
b/components/camel-opensearch/src/main/java/org/apache/camel/component/opensearch/OpensearchEndpoint.java
index 568d53dfae55..4649944f99da 100644
--- 
a/components/camel-opensearch/src/main/java/org/apache/camel/component/opensearch/OpensearchEndpoint.java
+++ 
b/components/camel-opensearch/src/main/java/org/apache/camel/component/opensearch/OpensearchEndpoint.java
@@ -24,6 +24,7 @@ import org.apache.camel.spi.BodyType;
 import org.apache.camel.spi.BodyType.Carrier;
 import org.apache.camel.spi.BodyTypes;
 import org.apache.camel.spi.EndpointServiceLocation;
+import org.apache.camel.spi.Metadata;
 import org.apache.camel.spi.UriEndpoint;
 import org.apache.camel.spi.UriParam;
 import org.apache.camel.support.DefaultEndpoint;
@@ -111,6 +112,9 @@ public class OpensearchEndpoint extends DefaultEndpoint 
implements EndpointServi
     private final OpensearchConfiguration configuration;
 
     private final RestClient client;
+
+    @UriParam(label = "advanced")
+    @Metadata(autowired = true)
     private OpenSearchClient openSearchClient;
 
     public OpensearchEndpoint(String uri, OpensearchComponent component, 
OpensearchConfiguration config,
@@ -143,6 +147,10 @@ public class OpensearchEndpoint extends DefaultEndpoint 
implements EndpointServi
         return openSearchClient;
     }
 
+    /**
+     * To use a custom configured OpenSearchClient instance for this endpoint. 
This takes precedence over the
+     * component's OpenSearchClient and RestClient options. The application is 
responsible for closing the client.
+     */
     public void setOpenSearchClient(OpenSearchClient openSearchClient) {
         this.openSearchClient = openSearchClient;
     }
diff --git 
a/components/camel-opensearch/src/test/java/org/apache/camel/component/opensearch/OpensearchEndpointClientTest.java
 
b/components/camel-opensearch/src/test/java/org/apache/camel/component/opensearch/OpensearchEndpointClientTest.java
new file mode 100644
index 000000000000..1921ca6a7f11
--- /dev/null
+++ 
b/components/camel-opensearch/src/test/java/org/apache/camel/component/opensearch/OpensearchEndpointClientTest.java
@@ -0,0 +1,164 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.opensearch;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import com.sun.net.httpserver.HttpServer;
+import org.apache.camel.CamelContext;
+import org.apache.camel.ProducerTemplate;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.impl.DefaultCamelContext;
+import org.apache.hc.core5.http.HttpHost;
+import org.apache.hc.core5.http.message.BasicHeader;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+import org.opensearch.client.RestClient;
+import org.opensearch.client.json.jackson.JacksonJsonpMapper;
+import org.opensearch.client.opensearch.OpenSearchClient;
+import org.opensearch.client.transport.rest_client.RestClientTransport;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class OpensearchEndpointClientTest {
+
+    private static final String BASIC_AUTH = "Basic dXNlcjpwYXNz";
+    private static final String BEARER_AUTH = "Bearer test-token";
+
+    private final AtomicInteger firstRequests = new AtomicInteger();
+    private final AtomicInteger secondRequests = new AtomicInteger();
+    private HttpServer firstServer;
+    private HttpServer secondServer;
+
+    @BeforeEach
+    void startServers() throws IOException {
+        firstServer = startServer(BASIC_AUTH, firstRequests);
+        secondServer = startServer(BEARER_AUTH, secondRequests);
+    }
+
+    @AfterEach
+    void stopServers() {
+        firstServer.stop(0);
+        secondServer.stop(0);
+    }
+
+    @Test
+    void endpointClientOverridesComponentClient() throws Exception {
+        try (RestClient firstRestClient = createClient(firstServer, 
BASIC_AUTH);
+             RestClient secondRestClient = createClient(secondServer, 
BEARER_AUTH);
+             CamelContext context = new DefaultCamelContext()) {
+            OpenSearchClient firstClient = openSearchClient(firstRestClient);
+            OpenSearchClient secondClient = openSearchClient(secondRestClient);
+            context.getRegistry().bind("firstClient", firstClient);
+            context.getRegistry().bind("secondClient", secondClient);
+            OpensearchComponent component = new OpensearchComponent();
+            component.setOpenSearchClient(firstClient);
+            context.addComponent("opensearch", component);
+
+            String firstUri = 
"opensearch:first?operation=Ping&openSearchClient=#bean:firstClient";
+            String secondUri = 
"opensearch:second?operation=Ping&openSearchClient=#bean:secondClient";
+            context.addRoutes(new RouteBuilder() {
+                @Override
+                public void configure() {
+                    from("direct:both").to(firstUri).to(secondUri);
+                    
from("direct:default").to("opensearch:default?operation=Ping");
+                }
+            });
+            context.start();
+            try (ProducerTemplate template = context.createProducerTemplate()) 
{
+                assertSame(firstClient, context.getEndpoint(firstUri, 
OpensearchEndpoint.class).getOpenSearchClient());
+                assertSame(secondClient, context.getEndpoint(secondUri, 
OpensearchEndpoint.class).getOpenSearchClient());
+                assertEquals(Boolean.TRUE, template.requestBody("direct:both", 
"", Boolean.class));
+                assertEquals(1, firstRequests.get());
+                assertEquals(1, secondRequests.get());
+
+                assertEquals(Boolean.TRUE, 
template.requestBody("direct:default", "", Boolean.class));
+                assertEquals(2, firstRequests.get());
+            }
+        }
+    }
+
+    @ParameterizedTest
+    @ValueSource(booleans = { false, true })
+    void camelCloseNotTheClientOnShutDown(boolean disconnect) throws Exception 
{
+        try (RestClient restClient = createClient(firstServer, BASIC_AUTH);
+             CamelContext context = new DefaultCamelContext()) {
+            OpenSearchClient client = openSearchClient(restClient);
+            context.getRegistry().bind("customClient", client);
+            OpensearchComponent component = new OpensearchComponent();
+            component.setClient(restClient);
+            context.addComponent("opensearch", component);
+            String uri = 
"opensearch:custom?operation=Ping&openSearchClient=#bean:customClient&disconnect="
 + disconnect;
+            context.addRoutes(new RouteBuilder() {
+                @Override
+                public void configure() {
+                    from("direct:ping").to(uri);
+                }
+            });
+            context.start();
+            assertTrue(restClient.isRunning());
+
+            context.stop();
+            assertTrue(restClient.isRunning());
+        }
+    }
+
+    @Test
+    void componentClientIsStillAutowiredWithoutEndpointOverride() throws 
Exception {
+        try (RestClient restClient = createClient(firstServer, BASIC_AUTH);
+             CamelContext context = new DefaultCamelContext()) {
+            OpenSearchClient client = openSearchClient(restClient);
+            context.getRegistry().bind("customClient", client);
+            context.start();
+            OpensearchEndpoint endpoint = 
context.getEndpoint("opensearch:default", OpensearchEndpoint.class);
+
+            assertSame(client, context.getComponent("opensearch", 
OpensearchComponent.class).getOpenSearchClient());
+            assertSame(client, endpoint.getOpenSearchClient());
+        }
+    }
+
+    private static HttpServer startServer(String authorization, AtomicInteger 
requests) throws IOException {
+        HttpServer server = HttpServer.create(new 
InetSocketAddress("127.0.0.1", 0), 0);
+        server.createContext("/", exchange -> {
+            boolean authorized = 
authorization.equals(exchange.getRequestHeaders().getFirst("Authorization"));
+            if (authorized) {
+                requests.incrementAndGet();
+            }
+            exchange.sendResponseHeaders(authorized ? 200 : 401, -1);
+            exchange.close();
+        });
+        server.start();
+        return server;
+    }
+
+    private static RestClient createClient(HttpServer server, String 
authorization) {
+        return RestClient.builder(new HttpHost("http", "127.0.0.1", 
server.getAddress().getPort()))
+                .setDefaultHeaders(new BasicHeader[] { new 
BasicHeader("Authorization", authorization) })
+                .build();
+    }
+
+    private static OpenSearchClient openSearchClient(RestClient client) {
+        return new OpenSearchClient(new RestClientTransport(client, new 
JacksonJsonpMapper()));
+    }
+}
diff --git 
a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/OpensearchEndpointBuilderFactory.java
 
b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/OpensearchEndpointBuilderFactory.java
index df3035d84f54..99907993fd8e 100644
--- 
a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/OpensearchEndpointBuilderFactory.java
+++ 
b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/OpensearchEndpointBuilderFactory.java
@@ -619,6 +619,42 @@ public interface OpensearchEndpointBuilderFactory {
             doSetProperty("hostnameVerifier", hostnameVerifier);
             return this;
         }
+        /**
+         * To use a custom configured OpenSearchClient instance for this
+         * endpoint. This takes precedence over the component's 
OpenSearchClient
+         * and RestClient options. The application is responsible for closing
+         * the client.
+         * 
+         * The option is a:
+         * <code>org.opensearch.client.opensearch.OpenSearchClient</code> type.
+         * 
+         * Group: advanced
+         * 
+         * @param openSearchClient the value to set
+         * @return the dsl builder
+         */
+        default AdvancedOpensearchEndpointBuilder 
openSearchClient(org.opensearch.client.opensearch.OpenSearchClient 
openSearchClient) {
+            doSetProperty("openSearchClient", openSearchClient);
+            return this;
+        }
+        /**
+         * To use a custom configured OpenSearchClient instance for this
+         * endpoint. This takes precedence over the component's 
OpenSearchClient
+         * and RestClient options. The application is responsible for closing
+         * the client.
+         * 
+         * The option will be converted to a
+         * <code>org.opensearch.client.opensearch.OpenSearchClient</code> type.
+         * 
+         * Group: advanced
+         * 
+         * @param openSearchClient the value to set
+         * @return the dsl builder
+         */
+        default AdvancedOpensearchEndpointBuilder openSearchClient(String 
openSearchClient) {
+            doSetProperty("openSearchClient", openSearchClient);
+            return this;
+        }
         /**
          * The delay of a sniff execution scheduled after a failure (in
          * milliseconds).

Reply via email to