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

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


The following commit(s) were added to refs/heads/main by this push:
     new caf5f5ee05e SOLR-17943: ClusterStateProvider to support 
HttpJdkSolrClient (#3730)
caf5f5ee05e is described below

commit caf5f5ee05e42b3346727dd2b94df82f3e6177c5
Author: James Dyer <[email protected]>
AuthorDate: Mon Oct 6 12:37:25 2025 -0500

    SOLR-17943: ClusterStateProvider to support HttpJdkSolrClient (#3730)
---
 solr/CHANGES.txt                                   |   2 +
 .../client/solrj/impl/CloudHttp2SolrClient.java    |   2 +-
 .../solrj/impl/Http2ClusterStateProvider.java      |  34 +++---
 .../solr/client/solrj/impl/Http2SolrClient.java    |  26 ++---
 .../solr/client/solrj/impl/HttpJdkSolrClient.java  |  21 +++-
 .../solr/client/solrj/impl/HttpSolrClientBase.java |   2 +
 .../solrj/impl/HttpSolrClientBuilderBase.java      |  31 ++++++
 .../solrj/impl/ClusterStateProviderTest.java       | 118 ++++++++++++++++-----
 8 files changed, 173 insertions(+), 63 deletions(-)

diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index a9031067e4e..5b91accaedc 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -80,6 +80,8 @@ Improvements
   always create and manage these internal clients.  The ability for callers to 
provide a pre-built client is removed.  Callers may specify the internal client
   details by providing an instance of either `Http2SolrClient.Builder` or 
`HttpJdkSolrClient.Builder`. (James Dyer)
 
+* SOLR-17943: `Http2ClusterStateProvider` now also can work with 
`HttpJdkSolrClient`. (James Dyer)
+
 
 Optimizations
 ---------------------
diff --git 
a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/CloudHttp2SolrClient.java
 
b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/CloudHttp2SolrClient.java
index ade1ebe433f..8849650c244 100644
--- 
a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/CloudHttp2SolrClient.java
+++ 
b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/CloudHttp2SolrClient.java
@@ -115,7 +115,7 @@ public class CloudHttp2SolrClient extends CloudSolrClient {
   private ClusterStateProvider createHttp2ClusterStateProvider(
       List<String> solrUrls, Http2SolrClient httpClient) {
     try {
-      return new Http2ClusterStateProvider(solrUrls, httpClient);
+      return new Http2ClusterStateProvider<>(solrUrls, httpClient);
     } catch (Exception e) {
       closeMyClientIfNeeded();
       throw new RuntimeException(
diff --git 
a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/Http2ClusterStateProvider.java
 
b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/Http2ClusterStateProvider.java
index cb4226c58d5..d6407514522 100644
--- 
a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/Http2ClusterStateProvider.java
+++ 
b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/Http2ClusterStateProvider.java
@@ -19,33 +19,39 @@ package org.apache.solr.client.solrj.impl;
 
 import java.io.IOException;
 import java.util.List;
-import org.apache.solr.client.solrj.SolrClient;
 
-public class Http2ClusterStateProvider extends BaseHttpClusterStateProvider {
-  final Http2SolrClient httpClient;
-  final boolean closeClient;
+public class Http2ClusterStateProvider<C extends HttpSolrClientBase>
+    extends BaseHttpClusterStateProvider {
+  final C httpClient;
 
-  public Http2ClusterStateProvider(List<String> solrUrls, Http2SolrClient 
httpClient)
-      throws Exception {
-    this.httpClient = httpClient == null ? new 
Http2SolrClient.Builder().build() : httpClient;
-    this.closeClient = httpClient == null;
+  /**
+   * Provide the solr urls and a solr http client for this cluster state 
provider to use. It is the
+   * caller's responsibiity to close the client.
+   *
+   * @param solrUrls root path solr urls
+   * @param httpClient an instance of HttpSolrClientBase
+   * @throws Exception if a problem with initialization occurs
+   */
+  public Http2ClusterStateProvider(List<String> solrUrls, C httpClient) throws 
Exception {
+    if (httpClient == null) {
+      throw new IllegalArgumentException("You must provide an Http client.");
+    }
+    this.httpClient = httpClient;
     initConfiguredNodes(solrUrls);
   }
 
   @Override
   public void close() throws IOException {
-    if (this.closeClient && this.httpClient != null) {
-      httpClient.close();
-    }
     super.close();
   }
 
   @Override
-  protected SolrClient getSolrClient(String baseUrl) {
-    return new 
Http2SolrClient.Builder(baseUrl).withHttpClient(httpClient).build();
+  @SuppressWarnings("unchecked")
+  protected C getSolrClient(String baseUrl) {
+    return (C) httpClient.builder().withBaseSolrUrl(baseUrl).build();
   }
 
-  public Http2SolrClient getHttpClient() {
+  public C getHttpClient() {
     return httpClient;
   }
 }
diff --git 
a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/Http2SolrClient.java 
b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/Http2SolrClient.java
index d291283123a..6cb95e9e3e5 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/Http2SolrClient.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/Http2SolrClient.java
@@ -643,6 +643,11 @@ public class Http2SolrClient extends HttpSolrClientBase {
     }
   }
 
+  @Override
+  public HttpSolrClientBuilderBase<?, ?> builder() {
+    return new Http2SolrClient.Builder().withHttpClient(this);
+  }
+
   private NamedList<Object> processErrorsAndResponse(
       SolrRequest<?> solrRequest, Response response, InputStream is, String 
urlExceptionMessage)
       throws SolrServerException {
@@ -1102,31 +1107,14 @@ public class Http2SolrClient extends HttpSolrClientBase 
{
       return new Http2SolrClient(baseSolrUrl, this);
     }
 
-    /**
-     * Provide a seed Http2SolrClient for the builder values, values can still 
be overridden by
-     * using builder methods
-     */
+    @Override
     public Builder withHttpClient(Http2SolrClient http2SolrClient) {
+      super.withHttpClient(http2SolrClient);
       this.httpClient = http2SolrClient.httpClient;
 
-      if (this.basicAuthAuthorizationStr == null) {
-        this.basicAuthAuthorizationStr = 
http2SolrClient.basicAuthAuthorizationStr;
-      }
       if (this.idleTimeoutMillis == null) {
         this.idleTimeoutMillis = http2SolrClient.idleTimeoutMillis;
       }
-      if (this.requestTimeoutMillis == null) {
-        this.requestTimeoutMillis = http2SolrClient.requestTimeoutMillis;
-      }
-      if (this.requestWriter == null) {
-        this.requestWriter = http2SolrClient.requestWriter;
-      }
-      if (this.responseParser == null) {
-        this.responseParser = http2SolrClient.parser;
-      }
-      if (this.urlParamNames == null) {
-        this.urlParamNames = http2SolrClient.urlParamNames;
-      }
       if (this.listenerFactories == null) {
         this.listenerFactories = http2SolrClient.listenerFactory;
       }
diff --git 
a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpJdkSolrClient.java 
b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpJdkSolrClient.java
index 0bcecb2d89d..15e686d5773 100644
--- 
a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpJdkSolrClient.java
+++ 
b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpJdkSolrClient.java
@@ -205,7 +205,7 @@ public class HttpJdkSolrClient extends HttpSolrClientBase {
     return requestWithBaseUrl(null, solrRequest, collection);
   }
 
-  private PreparedRequest prepareRequest(
+  protected PreparedRequest prepareRequest(
       SolrRequest<?> solrRequest, String collection, String overrideBaseUrl)
       throws SolrServerException, IOException {
     checkClosed();
@@ -342,7 +342,7 @@ public class HttpJdkSolrClient extends HttpSolrClientBase {
     return new PreparedRequest(reqb, contentWritingFuture);
   }
 
-  private static class PreparedRequest {
+  protected static class PreparedRequest {
     Future<?> contentWritingFuture;
     HttpRequest.Builder reqb;
 
@@ -537,6 +537,11 @@ public class HttpJdkSolrClient extends HttpSolrClientBase {
         .collect(Collectors.joining(", "));
   }
 
+  @Override
+  public HttpSolrClientBuilderBase<?, ?> builder() {
+    return new HttpJdkSolrClient.Builder().withHttpClient(this);
+  }
+
   public static class Builder
       extends HttpSolrClientBuilderBase<HttpJdkSolrClient.Builder, 
HttpJdkSolrClient> {
 
@@ -558,6 +563,18 @@ public class HttpJdkSolrClient extends HttpSolrClientBase {
       return new HttpJdkSolrClient(baseSolrUrl, this);
     }
 
+    @Override
+    public Builder withHttpClient(HttpJdkSolrClient httpSolrClient) {
+      super.withHttpClient(httpSolrClient);
+      if (this.executor == null) {
+        this.executor = httpSolrClient.executor;
+      }
+      if (this.sslContext == null) {
+        this.sslContext = httpSolrClient.httpClient.sslContext();
+      }
+      return this;
+    }
+
     /**
      * Use the provided SSLContext. See {@link
      * java.net.http.HttpClient.Builder#sslContext(SSLContext)}.
diff --git 
a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpSolrClientBase.java 
b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpSolrClientBase.java
index 987342e9374..55a4f8de0f5 100644
--- 
a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpSolrClientBase.java
+++ 
b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpSolrClientBase.java
@@ -99,6 +99,8 @@ public abstract class HttpSolrClientBase extends SolrClient {
     }
   }
 
+  public abstract HttpSolrClientBuilderBase<?, ?> builder();
+
   protected String getRequestUrl(SolrRequest<?> solrRequest, String collection)
       throws MalformedURLException {
     return ClientUtils.buildRequestUrl(solrRequest, serverBaseUrl, collection);
diff --git 
a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpSolrClientBuilderBase.java
 
b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpSolrClientBuilderBase.java
index b2b2d4a6900..2821ebc4cae 100644
--- 
a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpSolrClientBuilderBase.java
+++ 
b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpSolrClientBuilderBase.java
@@ -47,6 +47,37 @@ public abstract class HttpSolrClientBuilderBase<
 
   public abstract C build();
 
+  /**
+   * Provide a seed HttpSolrClient for the builder values, values can still be 
overridden by using
+   * builder methods
+   */
+  @SuppressWarnings("unchecked")
+  public B withHttpClient(C httpSolrClient) {
+    if (this.basicAuthAuthorizationStr == null) {
+      this.basicAuthAuthorizationStr = 
httpSolrClient.basicAuthAuthorizationStr;
+    }
+    if (this.requestTimeoutMillis == null) {
+      this.requestTimeoutMillis = httpSolrClient.requestTimeoutMillis;
+    }
+    if (this.requestWriter == null) {
+      this.requestWriter = httpSolrClient.requestWriter;
+    }
+    if (this.responseParser == null) {
+      this.responseParser = httpSolrClient.parser;
+    }
+    if (this.urlParamNames == null) {
+      this.urlParamNames = httpSolrClient.urlParamNames;
+    }
+    return (B) (this);
+  }
+
+  /** Provides the Base Solr Url. */
+  @SuppressWarnings("unchecked")
+  public B withBaseSolrUrl(String baseSolrUrl) {
+    this.baseSolrUrl = baseSolrUrl;
+    return (B) this;
+  }
+
   /** Provides a {@link RequestWriter} for created clients to use when handing 
requests. */
   @SuppressWarnings("unchecked")
   public B withRequestWriter(RequestWriter requestWriter) {
diff --git 
a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/ClusterStateProviderTest.java
 
b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/ClusterStateProviderTest.java
index 57479c1c263..18f990b41c7 100644
--- 
a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/ClusterStateProviderTest.java
+++ 
b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/ClusterStateProviderTest.java
@@ -32,6 +32,7 @@ import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
 import java.util.function.Supplier;
+import org.apache.solr.client.solrj.SolrRequest;
 import org.apache.solr.client.solrj.SolrServerException;
 import org.apache.solr.client.solrj.request.CollectionAdminRequest;
 import org.apache.solr.client.solrj.response.CollectionAdminResponse;
@@ -45,9 +46,32 @@ import org.hamcrest.Matchers;
 import org.junit.After;
 import org.junit.BeforeClass;
 import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 public class ClusterStateProviderTest extends SolrCloudTestCase {
 
+  private static final Logger log = 
LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+  static class UserAgentChangingJdkClient extends HttpJdkSolrClient {
+
+    final String userAgent;
+
+    protected UserAgentChangingJdkClient(Builder builder, String userAgent) {
+      super(null, builder);
+      this.userAgent = userAgent;
+    }
+
+    @Override
+    protected PreparedRequest prepareRequest(
+        SolrRequest<?> solrRequest, String collection, String overrideBaseUrl)
+        throws SolrServerException, IOException {
+      var pr = super.prepareRequest(solrRequest, collection, overrideBaseUrl);
+      pr.reqb.header("User-Agent", userAgent);
+      return pr;
+    }
+  }
+
   @BeforeClass
   public static void setupCluster() throws Exception {
     configureCluster(2)
@@ -76,18 +100,70 @@ public class ClusterStateProviderTest extends 
SolrCloudTestCase {
         new String[] {"http2ClusterStateProvider"}, new String[] 
{"zkClientClusterStateProvider"});
   }
 
-  private static Http2ClusterStateProvider http2ClusterStateProvider() {
+  static class ClosingHttp2ClusterStateProvider
+      extends Http2ClusterStateProvider<HttpSolrClientBase> {
+    public ClosingHttp2ClusterStateProvider(List<String> solrUrls, 
HttpSolrClientBase httpClient)
+        throws Exception {
+      super(solrUrls, httpClient);
+    }
+
+    @Override
+    public void close() throws IOException {
+      super.close();
+      try {
+        httpClient.close();
+      } catch (IOException e) {
+        log.error("error closing the client.", e);
+      }
+    }
+  }
+
+  private static Http2ClusterStateProvider<?> http2ClusterStateProvider(String 
userAgent) {
     try {
-      return new Http2ClusterStateProvider(
-          List.of(
-              cluster.getJettySolrRunner(0).getBaseUrl().toString(),
-              cluster.getJettySolrRunner(1).getBaseUrl().toString()),
-          null);
+      var useJdkProvider = random().nextBoolean();
+      HttpSolrClientBase client;
+
+      if (userAgent != null) {
+        if (useJdkProvider) {
+          client =
+              new UserAgentChangingJdkClient(
+                  new HttpJdkSolrClient.Builder()
+                      
.withSSLContext(MockTrustManager.ALL_TRUSTING_SSL_CONTEXT),
+                  userAgent);
+        } else {
+          var http2SolrClient = new Http2SolrClient.Builder().build();
+          http2SolrClient
+              .getHttpClient()
+              .setUserAgentField(new HttpField(HttpHeader.USER_AGENT, 
userAgent));
+          client = http2SolrClient;
+        }
+      } else {
+        client =
+            useJdkProvider
+                ? new HttpJdkSolrClient.Builder()
+                    .withSSLContext(MockTrustManager.ALL_TRUSTING_SSL_CONTEXT)
+                    .build()
+                : new Http2SolrClient.Builder().build();
+      }
+      var clientClassName = client.getClass().getName();
+      log.info("Using Http client implementation: {}", clientClassName);
+
+      var csp =
+          new ClosingHttp2ClusterStateProvider(
+              List.of(
+                  cluster.getJettySolrRunner(0).getBaseUrl().toString(),
+                  cluster.getJettySolrRunner(1).getBaseUrl().toString()),
+              client);
+      return csp;
     } catch (Exception e) {
       throw new RuntimeException(e);
     }
   }
 
+  private static Http2ClusterStateProvider<?> http2ClusterStateProvider() {
+    return http2ClusterStateProvider(null);
+  }
+
   private static ClusterStateProvider zkClientClusterStateProvider() {
     return new ZkClientClusterStateProvider(cluster.getZkStateReader());
   }
@@ -209,15 +285,10 @@ public class ClusterStateProviderTest extends 
SolrCloudTestCase {
     createCollection("col2");
 
     try (var cspZk = zkClientClusterStateProvider();
-        var cspHttp = http2ClusterStateProvider()) {
-      // SolrJ < version 9.9.0 for non streamed response
-      cspHttp
-          .getHttpClient()
-          .getHttpClient()
-          .setUserAgentField(
-              new HttpField(
-                  HttpHeader.USER_AGENT,
-                  "Solr[" + MethodHandles.lookup().lookupClass().getName() + 
"] " + "9.8.0"));
+        // SolrJ < version 9.9.0 for non streamed response
+        var cspHttp =
+            http2ClusterStateProvider(
+                "Solr[" + MethodHandles.lookup().lookupClass().getName() + "] 
" + "9.8.0")) {
 
       assertThat(cspHttp.getCollection("col1"), 
equalTo(cspZk.getCollection("col1")));
 
@@ -237,15 +308,10 @@ public class ClusterStateProviderTest extends 
SolrCloudTestCase {
     }
 
     try (var cspZk = zkClientClusterStateProvider();
-        var cspHttp = http2ClusterStateProvider()) {
-      // Even older SolrJ versionsg for non streamed response
-      cspHttp
-          .getHttpClient()
-          .getHttpClient()
-          .setUserAgentField(
-              new HttpField(
-                  HttpHeader.USER_AGENT,
-                  "Solr[" + MethodHandles.lookup().lookupClass().getName() + 
"] " + "2.0"));
+        // Even older SolrJ versions for non streamed response
+        var cspHttp =
+            http2ClusterStateProvider(
+                "Solr[" + MethodHandles.lookup().lookupClass().getName() + "] 
" + "2.0")) {
 
       assertThat(cspHttp.getCollection("col1"), 
equalTo(cspZk.getCollection("col1")));
 
@@ -272,9 +338,7 @@ public class ClusterStateProviderTest extends 
SolrCloudTestCase {
     createCollection("col2");
 
     try (var cspZk = zkClientClusterStateProvider();
-        var cspHttp = http2ClusterStateProvider()) {
-
-      cspHttp.getHttpClient().getHttpClient().setUserAgentField(null);
+        var cspHttp = http2ClusterStateProvider("")) {
 
       assertThat(cspHttp.getCollection("col1"), 
equalTo(cspZk.getCollection("col1")));
 

Reply via email to