This is an automated email from the ASF dual-hosted git repository. dsmiley pushed a commit to branch branch_10x in repository https://gitbox.apache.org/repos/asf/solr.git
commit c345aa6bb796af647f31641d2618e28c3bab7610 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. (cherry picked from commit 553da8f55e11189e1fe0bdb0668b93014d702ce6) --- .../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(); + } +}
