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