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

dsmiley 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 553da8f55e1 SOLR-18360: remove HttpJettySolrClient.addListenerFactory 
via a repointable wrapper (#4780)
553da8f55e1 is described below

commit 553da8f55e11189e1fe0bdb0668b93014d702ce6
Author: Serhiy Bzhezytskyy <[email protected]>
AuthorDate: Fri Aug 28 15:27:39 2026 +0300

    SOLR-18360: remove HttpJettySolrClient.addListenerFactory via a repointable 
wrapper (#4780)
    
    Removed the deprecated 
HttpJettySolrClient.addListenerFactory(HttpListenerFactory).  The 
@lucene.experimental HttpClientBuilderPlugin.setup() now takes a 
MutableListenerFactory -- a repointable HttpListenerFactory wrapper -- instead 
of the client itself.
---
 .../SOLR-18360-remove-addlistenerfactory.yml       | 11 ++++++
 .../apache/solr/core/HttpSolrClientProvider.java   |  9 ++++-
 .../handler/component/HttpShardHandlerFactory.java |  7 +++-
 .../solr/security/HttpClientBuilderPlugin.java     |  5 ++-
 .../solr/security/PKIAuthenticationPlugin.java     |  6 +--
 .../org/apache/solr/update/UpdateShardHandler.java | 11 ++++--
 .../client/solrj/jetty/HttpJettySolrClient.java    |  7 +---
 .../client/solrj/jetty/MutableListenerFactory.java | 46 ++++++++++++++++++++++
 8 files changed, 84 insertions(+), 18 deletions(-)

diff --git a/changelog/unreleased/SOLR-18360-remove-addlistenerfactory.yml 
b/changelog/unreleased/SOLR-18360-remove-addlistenerfactory.yml
new file mode 100644
index 00000000000..16b9040458f
--- /dev/null
+++ b/changelog/unreleased/SOLR-18360-remove-addlistenerfactory.yml
@@ -0,0 +1,11 @@
+title: >
+  Removed the deprecated 
HttpJettySolrClient.addListenerFactory(HttpListenerFactory). The
+  @lucene.experimental HttpClientBuilderPlugin.setup() now takes a 
MutableListenerFactory -- a
+  repointable HttpListenerFactory wrapper -- instead of the client itself.
+type: removed
+authors:
+  - name: Serhiy Bzhezytskyy
+  - name: David Smiley
+links:
+  - name: SOLR-18360
+    url: https://issues.apache.org/jira/browse/SOLR-18360
diff --git 
a/solr/core/src/java/org/apache/solr/core/HttpSolrClientProvider.java 
b/solr/core/src/java/org/apache/solr/core/HttpSolrClientProvider.java
index 8168ac420bc..a2098e3a63c 100644
--- a/solr/core/src/java/org/apache/solr/core/HttpSolrClientProvider.java
+++ b/solr/core/src/java/org/apache/solr/core/HttpSolrClientProvider.java
@@ -19,6 +19,7 @@ package org.apache.solr.core;
 import io.opentelemetry.api.common.Attributes;
 import java.util.concurrent.TimeUnit;
 import org.apache.solr.client.solrj.jetty.HttpJettySolrClient;
+import org.apache.solr.client.solrj.jetty.MutableListenerFactory;
 import org.apache.solr.common.util.IOUtils;
 import org.apache.solr.metrics.SolrMetricsContext;
 import org.apache.solr.security.HttpClientBuilderPlugin;
@@ -38,12 +39,16 @@ final class HttpSolrClientProvider implements AutoCloseable 
{
 
   private final InstrumentedHttpListenerFactory trackHttpSolrMetrics;
 
+  private final MutableListenerFactory securityListenerFactory = new 
MutableListenerFactory();
+
   HttpSolrClientProvider(UpdateShardHandlerConfig cfg, SolrMetricsContext 
parentContext) {
     trackHttpSolrMetrics = new 
InstrumentedHttpListenerFactory(getNameStrategy(cfg));
     initializeMetrics(parentContext);
 
     var httpClientBuilder =
-        new 
HttpJettySolrClient.Builder().addListenerFactory(trackHttpSolrMetrics);
+        new HttpJettySolrClient.Builder()
+            .addListenerFactory(trackHttpSolrMetrics)
+            .addListenerFactory(securityListenerFactory);
 
     if (cfg != null) {
       httpClientBuilder
@@ -73,7 +78,7 @@ final class HttpSolrClientProvider implements AutoCloseable {
   }
 
   void setSecurityBuilder(HttpClientBuilderPlugin builder) {
-    builder.setup(httpSolrClient);
+    builder.setup(securityListenerFactory);
   }
 
   @Override
diff --git 
a/solr/core/src/java/org/apache/solr/handler/component/HttpShardHandlerFactory.java
 
b/solr/core/src/java/org/apache/solr/handler/component/HttpShardHandlerFactory.java
index 8fb82603629..648488b78cf 100644
--- 
a/solr/core/src/java/org/apache/solr/handler/component/HttpShardHandlerFactory.java
+++ 
b/solr/core/src/java/org/apache/solr/handler/component/HttpShardHandlerFactory.java
@@ -37,6 +37,7 @@ import org.apache.solr.client.solrj.impl.LBSolrClient;
 import org.apache.solr.client.solrj.impl.SolrHttpConstants;
 import org.apache.solr.client.solrj.jetty.HttpJettySolrClient;
 import org.apache.solr.client.solrj.jetty.LBJettySolrClient;
+import org.apache.solr.client.solrj.jetty.MutableListenerFactory;
 import org.apache.solr.client.solrj.request.QueryRequest;
 import 
org.apache.solr.client.solrj.routing.AffinityReplicaListTransformerFactory;
 import org.apache.solr.client.solrj.routing.ReplicaListTransformer;
@@ -86,6 +87,7 @@ public class HttpShardHandlerFactory extends 
ShardHandlerFactory
 
   protected volatile HttpJettySolrClient defaultClient;
   protected InstrumentedHttpListenerFactory httpListenerFactory;
+  private final MutableListenerFactory securityListenerFactory = new 
MutableListenerFactory();
   protected LBAsyncSolrClient loadbalancer;
   private ObservableLongGauge asyncRequestsGauge;
 
@@ -309,8 +311,9 @@ public class HttpShardHandlerFactory extends 
ShardHandlerFactory
             .withIdleTimeout(soTimeout, TimeUnit.MILLISECONDS)
             .withExecutor(commExecutor)
             .withMaxConnectionsPerHost(maxConnectionsPerHost)
+            .addListenerFactory(this.httpListenerFactory)
+            .addListenerFactory(this.securityListenerFactory)
             .build();
-    this.defaultClient.addListenerFactory(this.httpListenerFactory);
     this.loadbalancer = new LBJettySolrClient.Builder(defaultClient).build();
 
     initReplicaListTransformers(getParameter(args, "replicaRouting", null, 
sb));
@@ -321,7 +324,7 @@ public class HttpShardHandlerFactory extends 
ShardHandlerFactory
   @Override
   public void setSecurityBuilder(HttpClientBuilderPlugin clientBuilderPlugin) {
     if (clientBuilderPlugin != null) {
-      clientBuilderPlugin.setup(defaultClient);
+      clientBuilderPlugin.setup(securityListenerFactory);
     }
   }
 
diff --git 
a/solr/core/src/java/org/apache/solr/security/HttpClientBuilderPlugin.java 
b/solr/core/src/java/org/apache/solr/security/HttpClientBuilderPlugin.java
index eb1ade431a8..d3f1e2206f3 100644
--- a/solr/core/src/java/org/apache/solr/security/HttpClientBuilderPlugin.java
+++ b/solr/core/src/java/org/apache/solr/security/HttpClientBuilderPlugin.java
@@ -16,7 +16,7 @@
  */
 package org.apache.solr.security;
 
-import org.apache.solr.client.solrj.jetty.HttpJettySolrClient;
+import org.apache.solr.client.solrj.jetty.MutableListenerFactory;
 
 /**
  * Plugin interface for configuring internal HttpClients. This relies on the 
internal HttpClient
@@ -26,5 +26,6 @@ import org.apache.solr.client.solrj.jetty.HttpJettySolrClient;
  */
 public interface HttpClientBuilderPlugin {
 
-  public default void setup(HttpJettySolrClient client) {}
+  /** May be called more than once; each call replaces the previous listener, 
not adds to it. */
+  public default void setup(MutableListenerFactory listenerFactory) {}
 }
diff --git 
a/solr/core/src/java/org/apache/solr/security/PKIAuthenticationPlugin.java 
b/solr/core/src/java/org/apache/solr/security/PKIAuthenticationPlugin.java
index aef49c122ff..b069218ee48 100644
--- a/solr/core/src/java/org/apache/solr/security/PKIAuthenticationPlugin.java
+++ b/solr/core/src/java/org/apache/solr/security/PKIAuthenticationPlugin.java
@@ -38,8 +38,8 @@ import java.util.Optional;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.TimeUnit;
 import java.util.function.BiConsumer;
-import org.apache.solr.client.solrj.jetty.HttpJettySolrClient;
 import org.apache.solr.client.solrj.jetty.HttpListenerFactory;
+import org.apache.solr.client.solrj.jetty.MutableListenerFactory;
 import org.apache.solr.client.solrj.request.GenericSolrRequest;
 import org.apache.solr.common.params.ModifiableSolrParams;
 import org.apache.solr.common.util.ExecutorUtil;
@@ -312,7 +312,7 @@ public class PKIAuthenticationPlugin extends 
AuthenticationPlugin
   }
 
   @Override
-  public void setup(HttpJettySolrClient client) {
+  public void setup(MutableListenerFactory listenerFactory) {
     final HttpListenerFactory.RequestResponseListener listener =
         new HttpListenerFactory.RequestResponseListener() {
           private static final String CACHED_REQUEST_USER_KEY = 
"cachedRequestUser";
@@ -363,7 +363,7 @@ public class PKIAuthenticationPlugin extends 
AuthenticationPlugin
                 (String) request.getAttributes().get(CACHED_REQUEST_USER_KEY));
           }
         };
-    client.addListenerFactory(() -> listener);
+    listenerFactory.setDelegate(() -> listener);
   }
 
   public boolean needsAuthorization(HttpServletRequest req) {
diff --git a/solr/core/src/java/org/apache/solr/update/UpdateShardHandler.java 
b/solr/core/src/java/org/apache/solr/update/UpdateShardHandler.java
index ed0f4f4e850..07bf1418c3a 100644
--- a/solr/core/src/java/org/apache/solr/update/UpdateShardHandler.java
+++ b/solr/core/src/java/org/apache/solr/update/UpdateShardHandler.java
@@ -26,6 +26,7 @@ import java.util.concurrent.ThreadFactory;
 import java.util.concurrent.TimeUnit;
 import org.apache.solr.client.solrj.impl.SolrHttpConstants;
 import org.apache.solr.client.solrj.jetty.HttpJettySolrClient;
+import org.apache.solr.client.solrj.jetty.MutableListenerFactory;
 import org.apache.solr.common.SolrException;
 import org.apache.solr.common.params.ModifiableSolrParams;
 import org.apache.solr.common.util.ExecutorUtil;
@@ -70,6 +71,8 @@ public class UpdateShardHandler implements SolrInfoBean {
 
   private final InstrumentedHttpListenerFactory trackHttpSolrMetrics;
 
+  private final MutableListenerFactory securityListenerFactory = new 
MutableListenerFactory();
+
   private SolrMetricsContext solrMetricsContext;
 
   private int socketTimeout = SolrHttpConstants.DEFAULT_SO_TIMEOUT;
@@ -115,9 +118,12 @@ public class UpdateShardHandler implements SolrInfoBean {
           .withMaxConnectionsPerHost(cfg.getMaxUpdateConnectionsPerHost());
     }
 
-    updateOnlyClientBuilder.withTheseParamNamesInTheUrl(urlParamNames);
+    updateOnlyClientBuilder
+        .withTheseParamNamesInTheUrl(urlParamNames)
+        .addListenerFactory(securityListenerFactory);
     updateOnlyClient = updateOnlyClientBuilder.build();
 
+    recoveryOnlyClientBuilder.addListenerFactory(securityListenerFactory);
     recoveryOnlyClient = recoveryOnlyClientBuilder.build();
 
     ThreadFactory recoveryThreadFactory = new 
SolrNamedThreadFactory("recoveryExecutor");
@@ -244,7 +250,6 @@ public class UpdateShardHandler implements SolrInfoBean {
   }
 
   public void setSecurityBuilder(HttpClientBuilderPlugin builder) {
-    builder.setup(updateOnlyClient);
-    builder.setup(recoveryOnlyClient);
+    builder.setup(securityListenerFactory);
   }
 }
diff --git 
a/solr/solrj-jetty/src/java/org/apache/solr/client/solrj/jetty/HttpJettySolrClient.java
 
b/solr/solrj-jetty/src/java/org/apache/solr/client/solrj/jetty/HttpJettySolrClient.java
index 77e1f7e3f1a..af159f58a6a 100644
--- 
a/solr/solrj-jetty/src/java/org/apache/solr/client/solrj/jetty/HttpJettySolrClient.java
+++ 
b/solr/solrj-jetty/src/java/org/apache/solr/client/solrj/jetty/HttpJettySolrClient.java
@@ -120,7 +120,7 @@ public class HttpJettySolrClient extends HttpSolrClient {
 
   private final long idleTimeoutMillis;
 
-  private List<HttpListenerFactory> listenerFactory;
+  private final List<HttpListenerFactory> listenerFactory;
   protected AsyncTracker asyncTracker = new AsyncTracker();
 
   private final boolean closeClient;
@@ -208,11 +208,6 @@ public class HttpJettySolrClient extends HttpSolrClient {
     }
   }
 
-  @Deprecated(since = "9.7")
-  public void addListenerFactory(HttpListenerFactory factory) {
-    this.listenerFactory.add(factory);
-  }
-
   /** internal use only */
   public HttpClient getHttpClient() {
     return httpClient;
diff --git 
a/solr/solrj-jetty/src/java/org/apache/solr/client/solrj/jetty/MutableListenerFactory.java
 
b/solr/solrj-jetty/src/java/org/apache/solr/client/solrj/jetty/MutableListenerFactory.java
new file mode 100644
index 00000000000..c895be9997e
--- /dev/null
+++ 
b/solr/solrj-jetty/src/java/org/apache/solr/client/solrj/jetty/MutableListenerFactory.java
@@ -0,0 +1,46 @@
+/*
+ * 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.solr.client.solrj.jetty;
+
+/**
+ * A {@link HttpListenerFactory} whose delegate can be repointed after 
construction, for the case
+ * where the actual listener isn't known yet when a {@link 
HttpJettySolrClient} is built (or may
+ * need to change later) but the client's identity is already relied on 
elsewhere, so it can't
+ * simply be rebuilt and swapped out. Register one instance via {@link
+ * HttpJettySolrClient.Builder#addListenerFactory} at construction time, then 
call {@link
+ * #setDelegate} on that same instance whenever the real listener becomes 
available -- the client
+ * itself stays immutable.
+ *
+ * <p>Repointing the delegate is a single volatile write, safe to call from a 
different thread than
+ * the ones invoking {@link #get()} concurrently, and replaces rather than 
accumulates: calling
+ * {@link #setDelegate} again (e.g. on a security.json reload) does not leave 
the previous listener
+ * still registered.
+ */
+public final class MutableListenerFactory implements HttpListenerFactory {
+  private static final RequestResponseListener NO_OP = new 
RequestResponseListener() {};
+
+  private volatile HttpListenerFactory delegate = () -> NO_OP;
+
+  public void setDelegate(HttpListenerFactory delegate) {
+    this.delegate = delegate;
+  }
+
+  @Override
+  public RequestResponseListener get() {
+    return delegate.get();
+  }
+}

Reply via email to