You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@solr.apache.org by ds...@apache.org on 2026/03/28 04:54:10 UTC
(solr) branch branch_10x updated (b9181d42a9f -> 518455f3f36)
This is an automated email from the ASF dual-hosted git repository.
dsmiley pushed a change to branch branch_10x
in repository https://gitbox.apache.org/repos/asf/solr.git
from b9181d42a9f Solr Admin Ui: search with contains in solr core/collection drop downs (#4121)
new 87a1812f9ee SOLR-18048 Move authentication into a Servlet Filter (#4120)
new 8e1087354d4 Benchmark fixes (#4194)s
new bc8226f8a5c Refactor: move isFileForbiddenInConfigSets (#4193)
new a31be2e36a4 SOLR-18125 Make LukeRequestHandler::getFirstLiveDoc get the first live doc (#4157)
new 06bb1feed52 SOLR-18075: SolrJ-Zookeeper no longer depends on Jetty (#4062)
new 518455f3f36 SOLR-18112: SolrDispatchFilter is now SolrServlet (#4119)
The 6 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
.../unreleased/PR#4157-fix-getFirstLiveDoc.yml | 9 +
.../unreleased/{SOLR-18047.yml => SOLR-18048.yml} | 6 +-
.../SOLR-18075-zkNoJetty.yml} | 6 +-
.../SOLR-18112-SolrDispatchFilterServlet.yml | 7 +
changelog/unreleased/move-configset-methods.yml | 7 +
.../java/org/apache/solr/bench/SolrBenchState.java | 6 +
.../apache/solr/bench/generators/IntegersDSL.java | 17 +-
.../org/apache/solr/bench/search/FilterCache.java | 34 +-
.../solr/bench/search/QueryResponseWriters.java | 13 +-
.../apache/solr/bench/search/StreamingSearch.java | 16 +-
.../configs/cloud-minimal/conf/schema.xml | 2 +-
.../apache/solr/api/CoordinatorV2HttpSolrCall.java | 7 +-
.../src/java/org/apache/solr/api/V2HttpCall.java | 17 +-
.../org/apache/solr/cli/SolrProcessManager.java | 4 +-
.../java/org/apache/solr/cloud/SolrZkServer.java | 6 +-
.../org/apache/solr/cloud/ZkConfigSetService.java | 26 +-
.../org/apache/solr/core/ConfigSetService.java | 40 ++-
.../java/org/apache/solr/core/CoreContainer.java | 16 +
.../solr/core/FileSystemConfigSetService.java | 5 +-
.../src/java/org/apache/solr/core/NodeConfig.java | 8 +-
.../java/org/apache/solr/core/SolrXmlConfig.java | 15 +-
.../org/apache/solr/core/backup/BackupManager.java | 5 +-
.../solr/handler/admin/LukeRequestHandler.java | 4 +-
.../solr/handler/configsets/UploadConfigSet.java | 3 +-
.../org/apache/solr/request/SolrRequestInfo.java | 13 +-
.../org/apache/solr/rest/BaseSolrResource.java | 4 +-
.../apache/solr/security/AuthenticationPlugin.java | 6 +-
.../apache/solr/security/AuthorizationUtils.java | 32 +-
.../apache/solr/servlet/AuthenticationFilter.java | 203 ++++++++++++
.../solr/servlet/CoordinatorHttpSolrCall.java | 16 +-
.../apache/solr/servlet/CoreContainerProvider.java | 26 +-
.../java/org/apache/solr/servlet/HttpSolrCall.java | 108 +++----
.../apache/solr/servlet/LoadAdminUiServlet.java | 12 +-
.../solr/servlet/RequiredSolrRequestFilter.java | 11 +-
.../solr/servlet/SolrAuthenticationException.java | 19 --
.../apache/solr/servlet/SolrDispatchFilter.java | 356 ---------------------
.../apache/solr/servlet/SolrRequestParsers.java | 16 +-
.../java/org/apache/solr/servlet/SolrServlet.java | 166 ++++++++++
.../org/apache/solr/cloud/DeleteReplicaTest.java | 2 +-
.../solr/cloud/TestCloudSearcherWarming.java | 4 +-
.../org/apache/solr/cloud/TestConfigSetsAPI.java | 16 +-
.../org/apache/solr/core/TestCoreContainer.java | 9 +-
.../solr/handler/admin/LukeRequestHandlerTest.java | 55 ++++
.../apache/solr/servlet/HttpSolrCallCloudTest.java | 28 +-
.../solr/servlet/LoadAdminUiServletTest.java | 4 +
.../solr/configsets/_default/conf/solrconfig.xml | 8 +-
.../conf/solrconfig.xml | 6 -
solr/solrj-zookeeper/build.gradle | 1 -
solr/solrj-zookeeper/gradle.lockfile | 20 +-
.../solr/common/cloud/NodesSysPropsCacher.java | 8 +-
.../solr/common/cloud/ZkMaintenanceUtils.java | 43 +--
.../solr/common/cloud/TestZkConfigSetService.java | 4 +-
.../src/java/org/apache/solr/SolrTestCase.java | 14 +-
.../solr/client/solrj/apache/HttpSolrClient.java | 2 +-
.../org/apache/solr/embedded/JettySolrRunner.java | 71 ++--
.../src/java/org/apache/solr/util/TestHarness.java | 11 +-
solr/webapp/web/WEB-INF/web.xml | 20 +-
57 files changed, 840 insertions(+), 753 deletions(-)
create mode 100644 changelog/unreleased/PR#4157-fix-getFirstLiveDoc.yml
copy changelog/unreleased/{SOLR-18047.yml => SOLR-18048.yml} (57%)
copy changelog/{v10.0.0/solrj-class-moves.yml => unreleased/SOLR-18075-zkNoJetty.yml} (54%)
create mode 100644 changelog/unreleased/SOLR-18112-SolrDispatchFilterServlet.yml
create mode 100644 changelog/unreleased/move-configset-methods.yml
create mode 100644 solr/core/src/java/org/apache/solr/servlet/AuthenticationFilter.java
delete mode 100644 solr/core/src/java/org/apache/solr/servlet/SolrAuthenticationException.java
delete mode 100644 solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java
create mode 100644 solr/core/src/java/org/apache/solr/servlet/SolrServlet.java
(solr) 01/06: SOLR-18048 Move authentication into a Servlet Filter (#4120)
Posted by ds...@apache.org.
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 87a1812f9eecf36de304437b43ccd13b82958dc0
Author: Gus Heck <46...@users.noreply.github.com>
AuthorDate: Mon Mar 16 23:01:11 2026 -0400
SOLR-18048 Move authentication into a Servlet Filter (#4120)
---
changelog/unreleased/SOLR-18048.yml | 8 +
.../java/org/apache/solr/core/CoreContainer.java | 16 ++
.../apache/solr/security/AuthenticationPlugin.java | 6 +-
.../apache/solr/security/AuthorizationUtils.java | 32 +---
.../apache/solr/servlet/AuthenticationFilter.java | 203 +++++++++++++++++++++
.../java/org/apache/solr/servlet/HttpSolrCall.java | 64 ++-----
.../solr/servlet/RequiredSolrRequestFilter.java | 10 +-
.../solr/servlet/SolrAuthenticationException.java | 19 --
.../apache/solr/servlet/SolrDispatchFilter.java | 175 +-----------------
.../org/apache/solr/embedded/JettySolrRunner.java | 9 +-
solr/webapp/web/WEB-INF/web.xml | 13 +-
11 files changed, 290 insertions(+), 265 deletions(-)
diff --git a/changelog/unreleased/SOLR-18048.yml b/changelog/unreleased/SOLR-18048.yml
new file mode 100644
index 00000000000..834d1905d7e
--- /dev/null
+++ b/changelog/unreleased/SOLR-18048.yml
@@ -0,0 +1,8 @@
+# See https://github.com/apache/solr/blob/main/dev-docs/changelog.adoc
+title: Authentication checks have been moved to a Servlet Filter
+type: other # added, changed, fixed, deprecated, removed, dependency_update, security, other
+authors:
+ - name: Gus Heck
+links:
+ - name: SOLR-18048
+ url: https://issues.apache.org/jira/browse/SOLR-18048
diff --git a/solr/core/src/java/org/apache/solr/core/CoreContainer.java b/solr/core/src/java/org/apache/solr/core/CoreContainer.java
index 02d82f92f43..f1cc3266aef 100644
--- a/solr/core/src/java/org/apache/solr/core/CoreContainer.java
+++ b/solr/core/src/java/org/apache/solr/core/CoreContainer.java
@@ -146,6 +146,7 @@ import org.apache.solr.search.SolrCache;
import org.apache.solr.search.SolrFieldCacheBean;
import org.apache.solr.search.SolrIndexSearcher;
import org.apache.solr.security.AllowListUrlChecker;
+import org.apache.solr.security.AuditEvent;
import org.apache.solr.security.AuditLoggerPlugin;
import org.apache.solr.security.AuthenticationPlugin;
import org.apache.solr.security.AuthorizationPlugin;
@@ -2509,4 +2510,19 @@ public class CoreContainer {
}
});
}
+
+ /**
+ * Audit an event if our audit plugin is installed and wants to audit this type of event.
+ *
+ * @param event the event to audit.
+ * @param eventType a Supplier to defer event creation and avoid gc load when auditing is not
+ * enabled. Lambdas are preferred for this since they are easily inlined.
+ */
+ public void audit(Supplier<AuditEvent> event, AuditEvent.EventType eventType) {
+ if (getAuditLoggerPlugin() != null && getAuditLoggerPlugin().shouldLog(eventType)) {
+ // The lambda should get optimized out, and produce no GC load:
+ // https://medium.com/@reetesh043/how-lambda-expressions-work-internally-in-java-f2a6f0e0bc68
+ getAuditLoggerPlugin().doAudit(event.get());
+ }
+ }
}
diff --git a/solr/core/src/java/org/apache/solr/security/AuthenticationPlugin.java b/solr/core/src/java/org/apache/solr/security/AuthenticationPlugin.java
index 99c1b091eac..aaa24087ff8 100644
--- a/solr/core/src/java/org/apache/solr/security/AuthenticationPlugin.java
+++ b/solr/core/src/java/org/apache/solr/security/AuthenticationPlugin.java
@@ -69,8 +69,8 @@ public abstract class AuthenticationPlugin implements SolrInfoBean {
* @param request the http request
* @param response the http response
* @param filterChain the servlet filter chain
- * @return false if the request not be processed by Solr (not continue), i.e. the response and
- * status code have already been sent.
+ * @return false if the request should not be processed by Solr (not continue), i.e. the response
+ * and status code have already been sent.
* @throws Exception any exception thrown during the authentication, e.g.
* PrivilegedActionException
*/
@@ -79,7 +79,7 @@ public abstract class AuthenticationPlugin implements SolrInfoBean {
throws Exception;
/**
- * This method is called by SolrDispatchFilter in order to initiate authentication. It does some
+ * This method is called by AuthenticationFilter in order to initiate authentication. It does some
* standard metrics counting.
*/
public final boolean authenticate(
diff --git a/solr/core/src/java/org/apache/solr/security/AuthorizationUtils.java b/solr/core/src/java/org/apache/solr/security/AuthorizationUtils.java
index a316dbdf65d..3ba6b07c557 100644
--- a/solr/core/src/java/org/apache/solr/security/AuthorizationUtils.java
+++ b/solr/core/src/java/org/apache/solr/security/AuthorizationUtils.java
@@ -21,7 +21,9 @@ import static org.apache.solr.common.cloud.ZkStateReader.COLLECTION_PROP;
import static org.apache.solr.common.params.CollectionParams.CollectionAction.CREATE;
import static org.apache.solr.common.params.CollectionParams.CollectionAction.DELETE;
import static org.apache.solr.common.params.CollectionParams.CollectionAction.RELOAD;
-import static org.apache.solr.servlet.HttpSolrCall.shouldAudit;
+import static org.apache.solr.security.AuditEvent.EventType.ERROR;
+import static org.apache.solr.security.AuditEvent.EventType.REJECTED;
+import static org.apache.solr.security.AuditEvent.EventType.UNAUTHORIZED;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
@@ -73,11 +75,7 @@ public class AuthorizationUtils {
servletReq.getHeader("Authorization"),
servletReq.getUserPrincipal());
}
- if (shouldAudit(cores, AuditEvent.EventType.REJECTED)) {
- cores
- .getAuditLoggerPlugin()
- .doAudit(new AuditEvent(AuditEvent.EventType.REJECTED, servletReq, context));
- }
+ cores.audit(() -> new AuditEvent(REJECTED, servletReq, context), REJECTED);
return new AuthorizationFailure(
statusCode, "Authentication failed, Response code: " + statusCode);
}
@@ -89,32 +87,22 @@ public class AuthorizationUtils {
context,
authResponse.getMessage()); // nowarn
}
- if (shouldAudit(cores, AuditEvent.EventType.UNAUTHORIZED)) {
- cores
- .getAuditLoggerPlugin()
- .doAudit(new AuditEvent(AuditEvent.EventType.UNAUTHORIZED, servletReq, context));
- }
+ cores.audit(() -> new AuditEvent(UNAUTHORIZED, servletReq, context), UNAUTHORIZED);
return new AuthorizationFailure(
statusCode, "Unauthorized request, Response code: " + statusCode);
}
if (!(statusCode == HttpStatus.ACCEPTED_202) && !(statusCode == HttpStatus.OK_200)) {
log.warn(
"ERROR {} during authentication: {}", statusCode, authResponse.getMessage()); // nowarn
- if (shouldAudit(cores, AuditEvent.EventType.ERROR)) {
- cores
- .getAuditLoggerPlugin()
- .doAudit(new AuditEvent(AuditEvent.EventType.ERROR, servletReq, context));
- }
+ cores.audit(() -> new AuditEvent(ERROR, servletReq, context), ERROR);
return new AuthorizationFailure(
statusCode, "ERROR during authorization, Response code: " + statusCode);
}
-
// No failures! Audit if necessary and return
- if (shouldAudit(cores, AuditEvent.EventType.AUTHORIZED)) {
- cores
- .getAuditLoggerPlugin()
- .doAudit(new AuditEvent(AuditEvent.EventType.AUTHORIZED, servletReq, context));
- }
+ cores.audit(
+ () -> new AuditEvent(AuditEvent.EventType.AUTHORIZED, servletReq, context),
+ AuditEvent.EventType.AUTHORIZED);
+
return null;
}
diff --git a/solr/core/src/java/org/apache/solr/servlet/AuthenticationFilter.java b/solr/core/src/java/org/apache/solr/servlet/AuthenticationFilter.java
new file mode 100644
index 00000000000..e842d555d28
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/servlet/AuthenticationFilter.java
@@ -0,0 +1,203 @@
+/*
+ * 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.servlet;
+
+import static org.apache.solr.common.SolrException.ErrorCode.SERVER_ERROR;
+import static org.apache.solr.security.AuditEvent.EventType.ANONYMOUS;
+import static org.apache.solr.security.AuditEvent.EventType.AUTHENTICATED;
+import static org.apache.solr.security.AuditEvent.EventType.REJECTED;
+
+import io.opentelemetry.api.trace.Span;
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.FilterConfig;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.ServletRequest;
+import jakarta.servlet.ServletResponse;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.lang.invoke.MethodHandles;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.core.CoreContainer;
+import org.apache.solr.security.AuditEvent;
+import org.apache.solr.security.AuthenticationPlugin;
+import org.apache.solr.security.PKIAuthenticationPlugin;
+import org.apache.solr.security.PublicKeyHandler;
+import org.apache.solr.util.tracing.TraceUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A servlet filter to handle authentication. Anything this filter wraps that needs to be served
+ * without authentication (such as UI) must be resolved and returned by a filter preceding this one,
+ * typically by forwarding to the default servlet or another servlet. Also, any tracing, auditing
+ * and ratelimiting decisions or setup usually want to be resolved ahead of this filter. Similarly,
+ * any potentially expensive operations should occur after this filter to eliminate denial of
+ * service by unauthenticated users.
+ */
+public class AuthenticationFilter extends CoreContainerAwareHttpFilter {
+
+ private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+ public static final String PLUGIN_DID_NOT_SET_ERROR_STATUS =
+ "{} threw SolrAuthenticationException without setting request status >= 400";
+
+ @Override
+ public void init(FilterConfig config) throws ServletException {
+ super.init(config);
+ }
+
+ @Override
+ protected void doFilter(HttpServletRequest req, HttpServletResponse res, FilterChain chain)
+ throws IOException, ServletException {
+
+ CoreContainer cc = getCores();
+ AuthenticationPlugin authenticationPlugin = cc.getAuthenticationPlugin();
+ String requestPath = ServletUtils.getPathAfterContext(req);
+
+ /////////////////////////////////////////////////////////
+ //////// Check cases where auth is not required. ////////
+ /////////////////////////////////////////////////////////
+
+ // Is authentication configured?
+ if (authenticationPlugin == null) {
+ cc.audit(() -> new AuditEvent(ANONYMOUS, req), ANONYMOUS);
+ chain.doFilter(req, res);
+ return;
+ }
+
+ // /admin/info/key must be always open. see SOLR-9188
+ if (PublicKeyHandler.PATH.equals(requestPath)) {
+ log.debug("Pass through PKI authentication endpoint");
+ chain.doFilter(req, res);
+ return;
+ }
+
+ if (isAdminUI(requestPath)) {
+ log.debug("Pass through Admin UI entry point");
+ chain.doFilter(req, res);
+ return;
+ }
+
+ /////////////////////////////////////////////////////////////////
+ //////// if we make it here, authentication is required. ////////
+ /////////////////////////////////////////////////////////////////
+
+ // internode (internal) requests have their own PKI auth plugin
+ if (isInternodePKI(req, cc)) {
+ authenticationPlugin = cc.getPkiAuthenticationSecurityBuilder();
+ }
+
+ boolean authSuccess = false;
+ try {
+ authSuccess =
+ authenticate(req, res, new AuditSuccessChainWrapper(cc, chain), authenticationPlugin);
+ } finally {
+ if (!authSuccess) {
+ cc.audit(() -> new AuditEvent(REJECTED, req), REJECTED);
+ res.flushBuffer();
+ if (res.getStatus() < 400) {
+ log.error(PLUGIN_DID_NOT_SET_ERROR_STATUS, authenticationPlugin.getClass());
+ res.sendError(401, "Authentication Plugin rejected credentials.");
+ }
+ }
+ }
+ }
+
+ /**
+ * The plugin called by this method will call doFilter(req, res) for us (which is why we pass in
+ * the filter chain object as a parameter).
+ */
+ private boolean authenticate(
+ HttpServletRequest req,
+ HttpServletResponse res,
+ FilterChain chain,
+ AuthenticationPlugin authenticationPlugin) {
+ try {
+
+ // It is imperative that authentication plugins obey the contract in the javadoc for
+ // org.apache.solr.security.AuthenticationPlugin.doAuthenticate, and either return
+ // false or throw an exception if they cannot validate the user's credentials.
+ // Any plugin that doesn't do this is broken and should be fixed.
+
+ logAuthAttempt(req);
+ return authenticationPlugin.authenticate(req, res, chain);
+ } catch (Exception e) {
+ log.info("Error authenticating", e);
+ throw new SolrException(SERVER_ERROR, "Error during request authentication, ", e);
+ }
+ }
+
+ private static boolean isAdminUI(String requestPath) {
+ return "/solr/".equals(requestPath) || "/".equals(requestPath);
+ }
+
+ private boolean isInternodePKI(HttpServletRequest req, CoreContainer cores) {
+ String header = req.getHeader(PKIAuthenticationPlugin.HEADER);
+ String headerV2 = req.getHeader(PKIAuthenticationPlugin.HEADER_V2);
+ return (header != null || headerV2 != null)
+ && cores.getPkiAuthenticationSecurityBuilder() != null;
+ }
+
+ private void logAuthAttempt(HttpServletRequest req) {
+ // moved this to a method because spotless formatting is so horrible, and makes the log
+ // message look like a big deal... but it's just taking up space
+ if (log.isDebugEnabled()) {
+ log.debug(
+ "Request to authenticate: {}, domain: {}, port: {}",
+ req,
+ req.getLocalName(),
+ req.getLocalPort());
+ }
+ }
+
+ @SuppressWarnings("ClassCanBeRecord")
+ private static class AuditSuccessChainWrapper implements FilterChain {
+
+ private final FilterChain chain;
+ private final CoreContainer cc;
+
+ private AuditSuccessChainWrapper(CoreContainer cc, FilterChain chain) {
+ this.cc = cc;
+ this.chain = chain;
+ }
+
+ @Override
+ public void doFilter(ServletRequest rq, ServletResponse rsp)
+ throws IOException, ServletException {
+ // this is a hack. The authentication plugin should accept a callback
+ // to be executed before doFilter is called if authentication succeeds
+ cc.audit(() -> new AuditEvent(AUTHENTICATED, (HttpServletRequest) rq), AUTHENTICATED);
+ Span span = TraceUtils.getSpan((HttpServletRequest) rq);
+ setPrincipalForTracing((HttpServletRequest) rq, span);
+ chain.doFilter(rq, rsp);
+ }
+
+ private void setPrincipalForTracing(HttpServletRequest request, Span span) {
+ if (log.isDebugEnabled()) {
+ log.debug("User principal: {}", request.getUserPrincipal());
+ }
+ final String principalName;
+ if (request.getUserPrincipal() != null) {
+ principalName = request.getUserPrincipal().getName();
+ } else {
+ principalName = null;
+ }
+ TraceUtils.setUser(span, String.valueOf(principalName));
+ }
+ }
+}
diff --git a/solr/core/src/java/org/apache/solr/servlet/HttpSolrCall.java b/solr/core/src/java/org/apache/solr/servlet/HttpSolrCall.java
index 5da3ccf1480..ae042c7343b 100644
--- a/solr/core/src/java/org/apache/solr/servlet/HttpSolrCall.java
+++ b/solr/core/src/java/org/apache/solr/servlet/HttpSolrCall.java
@@ -19,6 +19,8 @@ package org.apache.solr.servlet;
import static org.apache.solr.common.cloud.ZkStateReader.COLLECTION_PROP;
import static org.apache.solr.common.cloud.ZkStateReader.CORE_NAME_PROP;
import static org.apache.solr.common.cloud.ZkStateReader.NODE_NAME_PROP;
+import static org.apache.solr.security.AuditEvent.EventType.COMPLETED;
+import static org.apache.solr.security.AuditEvent.EventType.ERROR;
import static org.apache.solr.servlet.SolrDispatchFilter.Action.ADMIN;
import static org.apache.solr.servlet.SolrDispatchFilter.Action.FORWARD;
import static org.apache.solr.servlet.SolrDispatchFilter.Action.PASSTHROUGH;
@@ -422,9 +424,7 @@ public class HttpSolrCall {
if (solrDispatchFilter.abortErrorMessage != null) {
sendError(500, solrDispatchFilter.abortErrorMessage);
- if (shouldAudit(EventType.ERROR)) {
- cores.getAuditLoggerPlugin().doAudit(new AuditEvent(EventType.ERROR, getReq()));
- }
+ cores.audit(() -> new AuditEvent(ERROR, getReq()), ERROR);
return RETURN;
}
@@ -482,21 +482,11 @@ public class HttpSolrCall {
SolrRequestInfo.setRequestInfo(new SolrRequestInfo(solrReq, solrRsp, action));
mustClearSolrRequestInfo = true;
executeCoreRequest(solrRsp);
- if (shouldAudit(cores)) {
- EventType eventType =
- solrRsp.getException() == null ? EventType.COMPLETED : EventType.ERROR;
- if (shouldAudit(cores, eventType)) {
- cores
- .getAuditLoggerPlugin()
- .doAudit(
- new AuditEvent(
- eventType,
- req,
- getAuthCtx(),
- solrReq.getRequestTimer().getTime(),
- solrRsp.getException()));
- }
- }
+ Exception exception = solrRsp.getException();
+ EventType eventType = exception == null ? COMPLETED : ERROR;
+ double time = solrReq.getRequestTimer().getTime();
+ cores.audit(
+ () -> new AuditEvent(eventType, req, getAuthCtx(), time, exception), eventType);
HttpCacheHeaderUtil.checkHttpCachingVeto(solrRsp, resp, reqMethod);
Iterator<Map.Entry<String, String>> headers = solrRsp.httpHeaders();
while (headers.hasNext()) {
@@ -513,9 +503,7 @@ public class HttpSolrCall {
return action;
}
} catch (Throwable ex) {
- if (shouldAudit(EventType.ERROR)) {
- cores.getAuditLoggerPlugin().doAudit(new AuditEvent(EventType.ERROR, ex, req));
- }
+ cores.audit(() -> new AuditEvent(ERROR, ex, req), ERROR);
sendError(ex);
// walk the entire cause chain to search for an Error
Throwable t = ex;
@@ -583,22 +571,6 @@ public class HttpSolrCall {
return coreOrColName;
}
- public boolean shouldAudit() {
- return shouldAudit(cores);
- }
-
- public boolean shouldAudit(AuditEvent.EventType eventType) {
- return shouldAudit(cores, eventType);
- }
-
- public static boolean shouldAudit(CoreContainer cores) {
- return cores.getAuditLoggerPlugin() != null;
- }
-
- public static boolean shouldAudit(CoreContainer cores, AuditEvent.EventType eventType) {
- return shouldAudit(cores) && cores.getAuditLoggerPlugin().shouldLog(eventType);
- }
-
private boolean shouldAuthorize() {
if (PublicKeyHandler.PATH.equals(path)) return false;
// admin/info/key is the path where public key is exposed . it is always unsecured
@@ -741,20 +713,10 @@ public class HttpSolrCall {
ResponseWritersRegistry.getWriter(solrReq.getParams().get(CommonParams.WT));
if (respWriter == null) respWriter = getResponseWriter();
writeResponse(solrResp, respWriter, Method.getMethod(req.getMethod()));
- if (shouldAudit()) {
- EventType eventType = solrResp.getException() == null ? EventType.COMPLETED : EventType.ERROR;
- if (shouldAudit(eventType)) {
- cores
- .getAuditLoggerPlugin()
- .doAudit(
- new AuditEvent(
- eventType,
- req,
- getAuthCtx(),
- solrReq.getRequestTimer().getTime(),
- solrResp.getException()));
- }
- }
+ Exception ex = solrResp.getException();
+ EventType eventType = ex == null ? COMPLETED : ERROR;
+ double time = solrReq.getRequestTimer().getTime();
+ cores.audit(() -> new AuditEvent(eventType, req, getAuthCtx(), time, ex), eventType);
}
/**
diff --git a/solr/core/src/java/org/apache/solr/servlet/RequiredSolrRequestFilter.java b/solr/core/src/java/org/apache/solr/servlet/RequiredSolrRequestFilter.java
index 50ec9decdc9..7cdddfb2c5b 100644
--- a/solr/core/src/java/org/apache/solr/servlet/RequiredSolrRequestFilter.java
+++ b/solr/core/src/java/org/apache/solr/servlet/RequiredSolrRequestFilter.java
@@ -16,12 +16,15 @@
*/
package org.apache.solr.servlet;
+import static org.apache.solr.servlet.ServletUtils.closeShield;
+
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.invoke.MethodHandles;
+import org.apache.solr.common.SolrException;
import org.apache.solr.common.util.SuppressForbidden;
import org.apache.solr.logging.MDCLoggingContext;
import org.apache.solr.logging.MDCSnapshot;
@@ -72,7 +75,12 @@ public class RequiredSolrRequestFilter extends CoreContainerAwareHttpFilter {
// put the core container in request attribute
// This is required for the LoadAdminUiServlet class. Removing it will cause 404
req.setAttribute(CORE_CONTAINER_REQUEST_ATTRIBUTE, getCores());
- chain.doFilter(req, res);
+
+ // we want to prevent any attempts to close our request or response prematurely
+ chain.doFilter(closeShield(req), closeShield(res));
+ } catch (SolrException e) {
+ // this must never escape without setting the code and reporting the message.
+ res.sendError(e.code(), e.getMessage());
} finally {
// cleanups for above stuff
MDCLoggingContext.reset();
diff --git a/solr/core/src/java/org/apache/solr/servlet/SolrAuthenticationException.java b/solr/core/src/java/org/apache/solr/servlet/SolrAuthenticationException.java
deleted file mode 100644
index 04db031bb06..00000000000
--- a/solr/core/src/java/org/apache/solr/servlet/SolrAuthenticationException.java
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * 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.servlet;
-
-public class SolrAuthenticationException extends Exception {}
diff --git a/solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java b/solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java
index 0d0bef9b6ce..300c54ac562 100644
--- a/solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java
+++ b/solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java
@@ -16,7 +16,6 @@
*/
package org.apache.solr.servlet;
-import static org.apache.solr.servlet.ServletUtils.closeShield;
import static org.apache.solr.util.tracing.TraceUtils.getSpan;
import jakarta.servlet.FilterChain;
@@ -27,8 +26,6 @@ import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.invoke.MethodHandles;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicReference;
import org.apache.solr.api.V2HttpCall;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.SolrException.ErrorCode;
@@ -36,25 +33,15 @@ import org.apache.solr.common.util.ExecutorUtil;
import org.apache.solr.core.CoreContainer;
import org.apache.solr.core.NodeRoles;
import org.apache.solr.handler.api.V2ApiUtils;
-import org.apache.solr.security.AuditEvent;
-import org.apache.solr.security.AuditEvent.EventType;
-import org.apache.solr.security.AuthenticationPlugin;
-import org.apache.solr.security.PKIAuthenticationPlugin;
-import org.apache.solr.security.PublicKeyHandler;
-import org.apache.solr.util.tracing.TraceUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
- * This filter looks at the incoming URL maps them to handlers defined in solrconfig.xml
+ * This filter instantiates an instance of {@link HttpSolrCall}, invokes it and then based on the
+ * action returned handles retry/forward/passthrough dispatch decisions.
*
* @since solr 1.2
*/
-// todo: get rid of this class entirely! Request dispatch is the container's responsibility. Much of
-// what we have here should be several separate but composable servlet Filters, wrapping multiple
-// servlets that are more focused in scope. This should become possible now that we have a
-// ServletContextListener for startup/shutdown of CoreContainer that sets up a service from which
-// things like CoreContainer can be requested. (or better yet injected)
public class SolrDispatchFilter extends CoreContainerAwareHttpFilter {
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
@@ -108,7 +95,6 @@ public class SolrDispatchFilter extends CoreContainerAwareHttpFilter {
if (log.isTraceEnabled()) {
log.trace("SolrDispatchFilter.init(): {}", this.getClass().getClassLoader());
}
-
} catch (Throwable t) {
// catch this so our filter still works
log.error("Could not start Dispatch Filter.", t);
@@ -124,8 +110,7 @@ public class SolrDispatchFilter extends CoreContainerAwareHttpFilter {
public void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws IOException, ServletException {
// internal version of doFilter that tracks if we are in a retry
-
- dispatch(chain, closeShield(request), closeShield(response), false);
+ dispatch(chain, request, response, false);
}
/*
@@ -136,48 +121,19 @@ public class SolrDispatchFilter extends CoreContainerAwareHttpFilter {
In late 2025 SOLR-18040 moved request wrappers to independent ServletFilters
such as PathExclusionFilter see web.xml for a full, up-to-date list
- This class is moving toward only handling dispatch, please think twice
- before adding anything else to it.
+ This class is only handling dispatch, please think twice before adding anything else to it.
*/
private void dispatch(
FilterChain chain, HttpServletRequest request, HttpServletResponse response, boolean retry)
throws IOException, ServletException {
-
- AtomicReference<HttpServletRequest> wrappedRequest = new AtomicReference<>();
- try {
- authenticateRequest(request, response, wrappedRequest);
- } catch (SolrAuthenticationException e) {
- // it seems our auth system expects the plugin to set the status on the request.
- // If this hasn't happened make sure it does happen now rather than throwing an
- // exception, that formerly went on to be ignored in
- // org.apache.solr.servlet.ServletUtils.traceHttpRequestExecution2
- if (response.getStatus() < 400) {
- log.error(
- "Authentication Plugin threw SolrAuthenticationException without setting request status >= 400");
- response.sendError(401, "Authentication Plugin rejected credentials.");
- }
- return; // Nothing more to do, chain.doFilter(req,res) doesn't get called.
- }
- if (wrappedRequest.get() != null) {
- request = wrappedRequest.get();
- }
-
var span = getSpan(request);
- if (getCores().getAuthenticationPlugin() != null) {
- if (log.isDebugEnabled()) {
- log.debug("User principal: {}", request.getUserPrincipal());
- }
- final String principalName;
- if (request.getUserPrincipal() != null) {
- principalName = request.getUserPrincipal().getName();
- } else {
- principalName = null;
- }
- TraceUtils.setUser(span, String.valueOf(principalName));
- }
-
HttpSolrCall call = getHttpSolrCall(request, response, retry);
+
+ // this flag LOOKS like it should be in RequiredSolrRequestFilter, but
+ // the value set here is drives PKIAuthenticationPlugin.isSolrThread
+ // which gets used BEFORE this is set for some reason.
+ // BUG? Important timing?
ExecutorUtil.setServerThreadFlag(Boolean.TRUE);
try {
Action result = call.call();
@@ -215,7 +171,6 @@ public class SolrDispatchFilter extends CoreContainerAwareHttpFilter {
protected HttpSolrCall getHttpSolrCall(
HttpServletRequest request, HttpServletResponse response, boolean retry) {
String path = ServletUtils.getPathAfterContext(request);
-
CoreContainer cores;
try {
cores = getCores();
@@ -225,118 +180,6 @@ public class SolrDispatchFilter extends CoreContainerAwareHttpFilter {
return solrCallFactory.createInstance(this, path, cores, request, response, retry);
}
- // TODO: make this a servlet filter
- private void authenticateRequest(
- HttpServletRequest request,
- HttpServletResponse response,
- final AtomicReference<HttpServletRequest> wrappedRequest)
- throws IOException, SolrAuthenticationException {
- boolean requestContinues;
- final AtomicBoolean isAuthenticated = new AtomicBoolean(false);
- CoreContainer cores;
- try {
- cores = getCores();
- } catch (UnavailableException e) {
- throw new SolrException(ErrorCode.SERVER_ERROR, "Core Container Unavailable");
- }
- AuthenticationPlugin authenticationPlugin = cores.getAuthenticationPlugin();
- if (authenticationPlugin == null) {
- if (shouldAudit(EventType.ANONYMOUS)) {
- cores.getAuditLoggerPlugin().doAudit(new AuditEvent(EventType.ANONYMOUS, request));
- }
- return;
- } else {
- // /admin/info/key must be always open. see SOLR-9188
- String requestPath = ServletUtils.getPathAfterContext(request);
- if (PublicKeyHandler.PATH.equals(requestPath)) {
- log.debug("Pass through PKI authentication endpoint");
- return;
- }
- // /solr/ (Admin UI) must be always open to allow displaying Admin UI with login page
- if ("/solr/".equals(requestPath) || "/".equals(requestPath)) {
- log.debug("Pass through Admin UI entry point");
- return;
- }
- String header = request.getHeader(PKIAuthenticationPlugin.HEADER);
- String headerV2 = request.getHeader(PKIAuthenticationPlugin.HEADER_V2);
- if ((header != null || headerV2 != null)
- && cores.getPkiAuthenticationSecurityBuilder() != null)
- authenticationPlugin = cores.getPkiAuthenticationSecurityBuilder();
- try {
- if (log.isDebugEnabled()) {
- log.debug(
- "Request to authenticate: {}, domain: {}, port: {}",
- request,
- request.getLocalName(),
- request.getLocalPort());
- }
- // For legacy reasons, upon successful authentication this wants to call the chain's next
- // filter, which obfuscates the layout of the code since one usually expects to be able to
- // find the call to doFilter() in the implementation of jakarta.servlet.Filter. Supplying a
- // trivial impl here to keep existing code happy while making the flow clearer. Chain will
- // be called after this method completes. Eventually auth all moves to its own filter
- // (hopefully). Most auth plugins simply return true after calling this anyway, so they
- // obviously don't care.
- //
- // The Hadoop Auth Plugin was removed in SOLR-17540, however leaving the below reference
- // for future readers, as there may be an option to simplify this logic.
- //
- // Kerberos plugins seem to mostly use it to satisfy the api of a
- // wrapped instance of javax.servlet.Filter and neither of those seem to be doing anything
- // fancy with the filter chain, so this would seem to be a hack brought on by the fact that
- // our auth code has been forced to be code within dispatch filter, rather than being a
- // filter itself. The HadoopAuthPlugin has a suspicious amount of code after the call to
- // doFilter() which seems to imply that anything in this chain can get executed before
- // authentication completes, and I can't figure out how that's a good idea in the first
- // place.
- requestContinues =
- authenticationPlugin.authenticate(
- request,
- response,
- (req, rsp) -> {
- isAuthenticated.set(true);
- wrappedRequest.set((HttpServletRequest) req);
- });
- } catch (Exception e) {
- log.info("Error authenticating", e);
- throw new SolrException(ErrorCode.SERVER_ERROR, "Error during request authentication, ", e);
- }
- }
- // requestContinues is an optional short circuit, thus we still need to check isAuthenticated.
- // This is because the AuthenticationPlugin doesn't always have enough information to determine
- // if it should short circuit, e.g. the Kerberos Authentication Filter will send an error and
- // not call later filters in chain, but doesn't throw an exception. We could force each Plugin
- // to implement isAuthenticated to simplify the check here, but that just moves the complexity
- // to multiple code paths.
- if (!requestContinues || !isAuthenticated.get()) {
- response.flushBuffer();
- if (shouldAudit(EventType.REJECTED)) {
- cores.getAuditLoggerPlugin().doAudit(new AuditEvent(EventType.REJECTED, request));
- }
- throw new SolrAuthenticationException();
- }
- if (shouldAudit(EventType.AUTHENTICATED)) {
- cores.getAuditLoggerPlugin().doAudit(new AuditEvent(EventType.AUTHENTICATED, request));
- }
- // Auth Success
- }
-
- /**
- * Check if audit logging is enabled and should happen for given event type
- *
- * @param eventType the audit event
- */
- private boolean shouldAudit(AuditEvent.EventType eventType) {
- CoreContainer cores;
- try {
- cores = getCores();
- } catch (UnavailableException e) {
- throw new SolrException(ErrorCode.SERVER_ERROR, "Core Container Unavailable");
- }
- return cores.getAuditLoggerPlugin() != null
- && cores.getAuditLoggerPlugin().shouldLog(eventType);
- }
-
/** internal API */
public interface HttpSolrCallFactory {
default HttpSolrCall createInstance(
diff --git a/solr/test-framework/src/java/org/apache/solr/embedded/JettySolrRunner.java b/solr/test-framework/src/java/org/apache/solr/embedded/JettySolrRunner.java
index 04c948b99c2..05405b37d3c 100644
--- a/solr/test-framework/src/java/org/apache/solr/embedded/JettySolrRunner.java
+++ b/solr/test-framework/src/java/org/apache/solr/embedded/JettySolrRunner.java
@@ -61,6 +61,7 @@ import org.apache.solr.common.util.TimeSource;
import org.apache.solr.common.util.Utils;
import org.apache.solr.core.CoreContainer;
import org.apache.solr.metrics.SolrMetricManager;
+import org.apache.solr.servlet.AuthenticationFilter;
import org.apache.solr.servlet.CoreContainerProvider;
import org.apache.solr.servlet.PathExclusionFilter;
import org.apache.solr.servlet.RateLimitFilter;
@@ -118,6 +119,7 @@ public class JettySolrRunner {
volatile FilterHolder pathExcludeFilter;
volatile FilterHolder requiredFilter;
volatile FilterHolder rateLimitFilter;
+ volatile FilterHolder authFilter;
volatile FilterHolder dispatchFilter;
private FilterHolder tracingFilter;
@@ -424,10 +426,14 @@ public class JettySolrRunner {
rateLimitFilter = root.getServletHandler().newFilterHolder(Source.EMBEDDED);
rateLimitFilter.setHeldClass(RateLimitFilter.class);
- // Ratelimit Requests
+ // Trace Requests
tracingFilter = root.getServletHandler().newFilterHolder(Source.EMBEDDED);
tracingFilter.setHeldClass(TracingFilter.class);
+ // Authenticate Requests
+ authFilter = root.getServletHandler().newFilterHolder(Source.EMBEDDED);
+ authFilter.setHeldClass(AuthenticationFilter.class);
+
// This is our main workhorse
dispatchFilter = root.getServletHandler().newFilterHolder(Source.EMBEDDED);
dispatchFilter.setHeldClass(SolrDispatchFilter.class);
@@ -437,6 +443,7 @@ public class JettySolrRunner {
root.addFilter(requiredFilter, "/*", EnumSet.of(DispatcherType.REQUEST));
root.addFilter(rateLimitFilter, "/*", EnumSet.of(DispatcherType.REQUEST));
root.addFilter(tracingFilter, "/*", EnumSet.of(DispatcherType.REQUEST));
+ root.addFilter(authFilter, "/*", EnumSet.of(DispatcherType.REQUEST));
root.addFilter(dispatchFilter, "/*", EnumSet.of(DispatcherType.REQUEST));
// Default servlet as a fall-through
diff --git a/solr/webapp/web/WEB-INF/web.xml b/solr/webapp/web/WEB-INF/web.xml
index 6732a6cb58a..501cb160f24 100644
--- a/solr/webapp/web/WEB-INF/web.xml
+++ b/solr/webapp/web/WEB-INF/web.xml
@@ -45,12 +45,12 @@
</filter-mapping>
<filter>
- <filter-name>MDCLoggingFilter</filter-name>
+ <filter-name>SolrFilter</filter-name>
<filter-class>org.apache.solr.servlet.RequiredSolrRequestFilter</filter-class>
</filter>
<filter-mapping>
- <filter-name>MDCLoggingFilter</filter-name>
+ <filter-name>SolrFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
@@ -74,6 +74,15 @@
<url-pattern>/*</url-pattern>
</filter-mapping>
+ <filter>
+ <filter-name>AuthenticationFilter</filter-name>
+ <filter-class>org.apache.solr.servlet.AuthenticationFilter</filter-class>
+ </filter>
+
+ <filter-mapping>
+ <filter-name>AuthenticationFilter</filter-name>
+ <url-pattern>/*</url-pattern>
+ </filter-mapping>
<filter>
<filter-name>SolrRequestFilter</filter-name>
<filter-class>org.apache.solr.servlet.SolrDispatchFilter</filter-class>
(solr) 02/06: Benchmark fixes (#4194)s
Posted by ds...@apache.org.
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 8e1087354d4b2934211e10add4aa6df1b3d44bd9
Author: David Smiley <ds...@apache.org>
AuthorDate: Thu Mar 19 22:29:27 2026 -0400
Benchmark fixes (#4194)s
---
.../java/org/apache/solr/bench/SolrBenchState.java | 6 ++++
.../apache/solr/bench/generators/IntegersDSL.java | 17 +++++++----
.../org/apache/solr/bench/search/FilterCache.java | 34 ++++++++++++----------
.../solr/bench/search/QueryResponseWriters.java | 13 +++++++--
.../apache/solr/bench/search/StreamingSearch.java | 16 +++++-----
.../configs/cloud-minimal/conf/schema.xml | 2 +-
6 files changed, 55 insertions(+), 33 deletions(-)
diff --git a/solr/benchmark/src/java/org/apache/solr/bench/SolrBenchState.java b/solr/benchmark/src/java/org/apache/solr/bench/SolrBenchState.java
index 1aade6389dd..0a7e9b1f2be 100755
--- a/solr/benchmark/src/java/org/apache/solr/bench/SolrBenchState.java
+++ b/solr/benchmark/src/java/org/apache/solr/bench/SolrBenchState.java
@@ -47,6 +47,7 @@ import org.apache.solr.common.SolrInputDocument;
import org.apache.solr.common.params.ModifiableSolrParams;
import org.apache.solr.common.util.IOUtils;
import org.apache.solr.common.util.NamedList;
+import org.apache.solr.common.util.ObjectReleaseTracker;
import org.apache.solr.common.util.SolrNamedThreadFactory;
import org.apache.solr.common.util.SuppressForbidden;
import org.apache.solr.embedded.JettySolrRunner;
@@ -154,6 +155,11 @@ public class SolrBenchState {
IOUtils.closeQuietly(client);
cluster.shutdown();
logClusterDirectorySize();
+
+ String orr = ObjectReleaseTracker.clearObjectTrackerAndCheckEmpty();
+ if (orr != null) {
+ throw new AssertionError("ObjectReleaseTracker found unreleased objects:\n" + orr);
+ }
}
private void logClusterDirectorySize() throws IOException {
diff --git a/solr/benchmark/src/java/org/apache/solr/bench/generators/IntegersDSL.java b/solr/benchmark/src/java/org/apache/solr/bench/generators/IntegersDSL.java
index 6a41c10c317..89e6d3ad24f 100644
--- a/solr/benchmark/src/java/org/apache/solr/bench/generators/IntegersDSL.java
+++ b/solr/benchmark/src/java/org/apache/solr/bench/generators/IntegersDSL.java
@@ -116,7 +116,7 @@ public class IntegersDSL {
private final Gen<Integer> integers;
/** The Cardinality start. */
- Integer cardinalityStart;
+ volatile Integer cardinalityStart;
/**
* Instantiates a new Integer max cardinality solr gen.
@@ -132,13 +132,18 @@ public class IntegersDSL {
@Override
public Integer generate(SolrRandomnessSource in) {
- if (cardinalityStart == null) {
- cardinalityStart =
- SolrGenerate.range(0, Integer.MAX_VALUE - maxCardinality - 1).generate(in);
+ Integer localStart = cardinalityStart;
+ if (localStart == null) {
+ synchronized (this) {
+ localStart = cardinalityStart;
+ if (localStart == null) {
+ localStart = SolrGenerate.range(0, Integer.MAX_VALUE - maxCardinality - 1).generate(in);
+ cardinalityStart = localStart;
+ }
+ }
}
- long seed =
- SolrGenerate.range(cardinalityStart, cardinalityStart + maxCardinality - 1).generate(in);
+ long seed = SolrGenerate.range(localStart, localStart + maxCardinality - 1).generate(in);
return integers.generate(new SplittableRandomSource(new SplittableRandom(seed)));
}
}
diff --git a/solr/benchmark/src/java/org/apache/solr/bench/search/FilterCache.java b/solr/benchmark/src/java/org/apache/solr/bench/search/FilterCache.java
index f736aa4bf80..cf90548f296 100644
--- a/solr/benchmark/src/java/org/apache/solr/bench/search/FilterCache.java
+++ b/solr/benchmark/src/java/org/apache/solr/bench/search/FilterCache.java
@@ -19,9 +19,11 @@ package org.apache.solr.bench.search;
import static org.apache.solr.bench.generators.SourceDSL.integers;
import java.io.IOException;
-import java.net.HttpURLConnection;
import java.net.URI;
-import java.nio.charset.StandardCharsets;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+import java.util.stream.Collectors;
import org.apache.solr.bench.BaseBenchState;
import org.apache.solr.bench.Docs;
import org.apache.solr.bench.SolrBenchState;
@@ -115,19 +117,21 @@ public class FilterCache {
public void dumpMetrics(SolrBenchState solrBenchState) {
// TODO add a verbose flag
- String url =
- solrBenchState.nodes.get(0)
- + "/admin/metrics?prefix=CACHE.searcher.filterCache&omitHeader=true";
- HttpURLConnection conn = null;
- try {
- conn = (HttpURLConnection) URI.create(url).toURL().openConnection();
- conn.connect();
- BaseBenchState.log(
- new String(conn.getInputStream().readAllBytes(), StandardCharsets.UTF_8));
- } catch (IOException e) {
- // ignored
- } finally {
- if (conn != null) conn.disconnect();
+ String url = solrBenchState.nodes.getFirst() + "/admin/metrics?category=CACHE?wt=prometheus";
+ try (HttpClient client = HttpClient.newHttpClient()) {
+ HttpRequest request = HttpRequest.newBuilder().uri(URI.create(url)).GET().build();
+ HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
+
+ // Filter to only lines containing filterCache metrics
+ String filteredMetrics =
+ response
+ .body()
+ .lines()
+ .filter(line -> line.contains("filterCache"))
+ .collect(Collectors.joining("\n"));
+ BaseBenchState.log(filteredMetrics);
+ } catch (IOException | InterruptedException e) {
+ throw new RuntimeException(e);
}
}
}
diff --git a/solr/benchmark/src/java/org/apache/solr/bench/search/QueryResponseWriters.java b/solr/benchmark/src/java/org/apache/solr/bench/search/QueryResponseWriters.java
index 6929d2810a5..6c804c60afc 100644
--- a/solr/benchmark/src/java/org/apache/solr/bench/search/QueryResponseWriters.java
+++ b/solr/benchmark/src/java/org/apache/solr/bench/search/QueryResponseWriters.java
@@ -22,6 +22,8 @@ import static org.apache.solr.bench.generators.SourceDSL.integers;
import static org.apache.solr.bench.generators.SourceDSL.strings;
import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
import org.apache.solr.bench.Docs;
import org.apache.solr.bench.SolrBenchState;
import org.apache.solr.client.solrj.SolrServerException;
@@ -29,6 +31,7 @@ import org.apache.solr.client.solrj.request.QueryRequest;
import org.apache.solr.client.solrj.response.InputStreamResponseParser;
import org.apache.solr.common.params.CommonParams;
import org.apache.solr.common.params.ModifiableSolrParams;
+import org.apache.solr.common.util.NamedList;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
@@ -56,7 +59,7 @@ public class QueryResponseWriters {
@State(Scope.Benchmark)
public static class BenchState {
- @Param({CommonParams.JAVABIN, CommonParams.JSON, "cbor", "smile", "xml", "raw"})
+ @Param({CommonParams.JAVABIN, CommonParams.JSON, "cbor", "smile", "xml"})
String wt;
private int docs = 100;
@@ -91,6 +94,12 @@ public class QueryResponseWriters {
@Benchmark
public Object query(BenchState benchState, SolrBenchState solrBenchState)
throws SolrServerException, IOException {
- return solrBenchState.client.request(benchState.q, collection);
+ NamedList<Object> response = solrBenchState.client.request(benchState.q, collection);
+ // consume the stream completely
+ try (InputStream responseStream =
+ (InputStream) response.get(InputStreamResponseParser.STREAM_KEY)) {
+ responseStream.transferTo(OutputStream.nullOutputStream());
+ }
+ return response;
}
}
diff --git a/solr/benchmark/src/java/org/apache/solr/bench/search/StreamingSearch.java b/solr/benchmark/src/java/org/apache/solr/bench/search/StreamingSearch.java
index c354377eaf2..5797682c63e 100644
--- a/solr/benchmark/src/java/org/apache/solr/bench/search/StreamingSearch.java
+++ b/solr/benchmark/src/java/org/apache/solr/bench/search/StreamingSearch.java
@@ -25,6 +25,7 @@ import java.util.ArrayList;
import java.util.List;
import org.apache.solr.bench.Docs;
import org.apache.solr.bench.SolrBenchState;
+import org.apache.solr.client.solrj.SolrClient;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.io.SolrClientCache;
import org.apache.solr.client.solrj.io.Tuple;
@@ -34,6 +35,7 @@ import org.apache.solr.client.solrj.io.stream.TupleStream;
import org.apache.solr.client.solrj.jetty.HttpJettySolrClient;
import org.apache.solr.common.params.CommonParams;
import org.apache.solr.common.params.ModifiableSolrParams;
+import org.apache.solr.common.util.IOUtils;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
@@ -67,7 +69,7 @@ public class StreamingSearch {
private String zkHost;
private ModifiableSolrParams params;
private StreamContext streamContext;
- private HttpJettySolrClient httpJettySolrClient;
+ private SolrClient httpSolrClient;
@Setup(Level.Trial)
public void setup(SolrBenchState solrBenchState) throws Exception {
@@ -95,11 +97,9 @@ public class StreamingSearch {
@Setup(Level.Iteration)
public void setupIteration(SolrBenchState solrBenchState)
throws SolrServerException, IOException {
- SolrClientCache solrClientCache;
- // TODO tune params?
- var client = new HttpJettySolrClient.Builder().useHttp1_1(useHttp1).build();
- solrClientCache = new SolrClientCache(client);
-
+ var httpSolrClient = new HttpJettySolrClient.Builder().useHttp1_1(useHttp1).build();
+ this.httpSolrClient = httpSolrClient;
+ SolrClientCache solrClientCache = new SolrClientCache(httpSolrClient);
streamContext = new StreamContext();
streamContext.setSolrClientCache(solrClientCache);
}
@@ -107,9 +107,7 @@ public class StreamingSearch {
@TearDown(Level.Iteration)
public void teardownIt() {
streamContext.getSolrClientCache().close();
- if (httpJettySolrClient != null) {
- httpJettySolrClient.close();
- }
+ IOUtils.closeQuietly(httpSolrClient);
}
}
diff --git a/solr/benchmark/src/resources/configs/cloud-minimal/conf/schema.xml b/solr/benchmark/src/resources/configs/cloud-minimal/conf/schema.xml
index e517aea5930..b1851c3c290 100644
--- a/solr/benchmark/src/resources/configs/cloud-minimal/conf/schema.xml
+++ b/solr/benchmark/src/resources/configs/cloud-minimal/conf/schema.xml
@@ -36,7 +36,7 @@
</fieldType>
<!-- for versioning -->
- <field name="_version_" type="long" indexed="true" stored="true"/>
+ <field name="_version_" type="long" indexed="true" stored="true" docValues="true"/>
<field name="_root_" type="string" indexed="true" stored="true" multiValued="false" required="false"/>
<field name="id" type="string" indexed="true" stored="true"/>
<field name="text" type="text" indexed="true" stored="false"/>
(solr) 05/06: SOLR-18075: SolrJ-Zookeeper no longer depends on Jetty (#4062)
Posted by ds...@apache.org.
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 06bb1feed52db2c55e7fbdd49e1313d430ab68b1
Author: David Smiley <ds...@apache.org>
AuthorDate: Mon Mar 23 10:21:28 2026 -0400
SOLR-18075: SolrJ-Zookeeper no longer depends on Jetty (#4062)
---
changelog/unreleased/SOLR-18075-zkNoJetty.yml | 8 ++++++++
solr/solrj-zookeeper/build.gradle | 1 -
solr/solrj-zookeeper/gradle.lockfile | 20 ++++++++++----------
.../solr/common/cloud/NodesSysPropsCacher.java | 8 ++++----
4 files changed, 22 insertions(+), 15 deletions(-)
diff --git a/changelog/unreleased/SOLR-18075-zkNoJetty.yml b/changelog/unreleased/SOLR-18075-zkNoJetty.yml
new file mode 100644
index 00000000000..f501e1cb602
--- /dev/null
+++ b/changelog/unreleased/SOLR-18075-zkNoJetty.yml
@@ -0,0 +1,8 @@
+# See https://github.com/apache/solr/blob/main/dev-docs/changelog.adoc
+title: 'solrj-zookeeper depended on jetty, although not in practice. Not any longer.'
+type: other # added, changed, fixed, deprecated, removed, dependency_update, security, other
+authors:
+ - name: David Smiley
+links:
+ - name: SOLR-18075
+ url: https://issues.apache.org/jira/browse/SOLR-18075
diff --git a/solr/solrj-zookeeper/build.gradle b/solr/solrj-zookeeper/build.gradle
index 1f2f212f15e..57abfbf5b0c 100644
--- a/solr/solrj-zookeeper/build.gradle
+++ b/solr/solrj-zookeeper/build.gradle
@@ -30,7 +30,6 @@ dependencies {
}
implementation project(':solr:solrj')
- implementation project(':solr:solrj-jetty')
implementation project(':solr:api')
implementation libs.slf4j.api
diff --git a/solr/solrj-zookeeper/gradle.lockfile b/solr/solrj-zookeeper/gradle.lockfile
index 98251e1475e..16a1d223fb7 100644
--- a/solr/solrj-zookeeper/gradle.lockfile
+++ b/solr/solrj-zookeeper/gradle.lockfile
@@ -130,23 +130,23 @@ org.apiguardian:apiguardian-api:1.1.2=jarValidation,testRuntimeClasspath
org.checkerframework:checker-qual:3.33.0=apiHelper
org.codehaus.woodstox:stax2-api:4.2.2=jarValidation,testRuntimeClasspath
org.eclipse.jetty.ee10:jetty-ee10-servlet:12.0.27=jarValidation,testCompileClasspath,testRuntimeClasspath
-org.eclipse.jetty.http2:jetty-http2-client-transport:12.0.27=jarValidation,runtimeClasspath,testRuntimeClasspath
-org.eclipse.jetty.http2:jetty-http2-client:12.0.27=compileClasspath,jarValidation,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.eclipse.jetty.http2:jetty-http2-common:12.0.27=compileClasspath,jarValidation,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.eclipse.jetty.http2:jetty-http2-hpack:12.0.27=compileClasspath,jarValidation,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.eclipse.jetty.http2:jetty-http2-client-transport:12.0.27=jarValidation,testRuntimeClasspath
+org.eclipse.jetty.http2:jetty-http2-client:12.0.27=jarValidation,testCompileClasspath,testRuntimeClasspath
+org.eclipse.jetty.http2:jetty-http2-common:12.0.27=jarValidation,testCompileClasspath,testRuntimeClasspath
+org.eclipse.jetty.http2:jetty-http2-hpack:12.0.27=jarValidation,testCompileClasspath,testRuntimeClasspath
org.eclipse.jetty.http2:jetty-http2-server:12.0.27=jarValidation,testRuntimeClasspath
-org.eclipse.jetty:jetty-alpn-client:12.0.27=compileClasspath,jarValidation,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.eclipse.jetty:jetty-alpn-java-client:12.0.27=jarValidation,runtimeClasspath,testRuntimeClasspath
+org.eclipse.jetty:jetty-alpn-client:12.0.27=jarValidation,testCompileClasspath,testRuntimeClasspath
+org.eclipse.jetty:jetty-alpn-java-client:12.0.27=jarValidation,testRuntimeClasspath
org.eclipse.jetty:jetty-alpn-java-server:12.0.27=jarValidation,testRuntimeClasspath
org.eclipse.jetty:jetty-alpn-server:12.0.27=jarValidation,testRuntimeClasspath
-org.eclipse.jetty:jetty-client:12.0.27=jarValidation,runtimeClasspath,testRuntimeClasspath
-org.eclipse.jetty:jetty-http:12.0.27=compileClasspath,jarValidation,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.eclipse.jetty:jetty-io:12.0.27=compileClasspath,jarValidation,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.eclipse.jetty:jetty-client:12.0.27=jarValidation,testRuntimeClasspath
+org.eclipse.jetty:jetty-http:12.0.27=jarValidation,testCompileClasspath,testRuntimeClasspath
+org.eclipse.jetty:jetty-io:12.0.27=jarValidation,testCompileClasspath,testRuntimeClasspath
org.eclipse.jetty:jetty-rewrite:12.0.27=jarValidation,testRuntimeClasspath
org.eclipse.jetty:jetty-security:12.0.27=jarValidation,testCompileClasspath,testRuntimeClasspath
org.eclipse.jetty:jetty-server:12.0.27=jarValidation,testCompileClasspath,testRuntimeClasspath
org.eclipse.jetty:jetty-session:12.0.27=jarValidation,testCompileClasspath,testRuntimeClasspath
-org.eclipse.jetty:jetty-util:12.0.27=compileClasspath,jarValidation,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.eclipse.jetty:jetty-util:12.0.27=jarValidation,testCompileClasspath,testRuntimeClasspath
org.glassfish.hk2.external:aopalliance-repackaged:3.1.1=jarValidation,testRuntimeClasspath
org.glassfish.hk2:hk2-api:3.1.1=jarValidation,testRuntimeClasspath
org.glassfish.hk2:hk2-locator:3.1.1=jarValidation,testRuntimeClasspath
diff --git a/solr/solrj-zookeeper/src/java/org/apache/solr/common/cloud/NodesSysPropsCacher.java b/solr/solrj-zookeeper/src/java/org/apache/solr/common/cloud/NodesSysPropsCacher.java
index 46974dce6d4..856c6d8a9f9 100644
--- a/solr/solrj-zookeeper/src/java/org/apache/solr/common/cloud/NodesSysPropsCacher.java
+++ b/solr/solrj-zookeeper/src/java/org/apache/solr/common/cloud/NodesSysPropsCacher.java
@@ -22,7 +22,7 @@ import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.solr.client.solrj.SolrRequest;
-import org.apache.solr.client.solrj.jetty.HttpJettySolrClient;
+import org.apache.solr.client.solrj.impl.HttpSolrClientBase;
import org.apache.solr.client.solrj.request.GenericSolrRequest;
import org.apache.solr.common.NavigableObject;
import org.apache.solr.common.SolrException;
@@ -33,10 +33,10 @@ import org.apache.solr.common.params.ModifiableSolrParams;
public class NodesSysPropsCacher implements NodesSysProps, AutoCloseable {
private volatile boolean isClosed = false;
private final Map<String, Map<String, Object>> nodeVsTagsCache = new ConcurrentHashMap<>();
- private ZkStateReader zkStateReader;
- private final HttpJettySolrClient solrClient;
+ private final ZkStateReader zkStateReader;
+ private final HttpSolrClientBase solrClient;
- public NodesSysPropsCacher(HttpJettySolrClient solrClient, ZkStateReader zkStateReader) {
+ public NodesSysPropsCacher(HttpSolrClientBase solrClient, ZkStateReader zkStateReader) {
this.zkStateReader = zkStateReader;
this.solrClient = solrClient;
zkStateReader.registerLiveNodesListener(
(solr) 03/06: Refactor: move isFileForbiddenInConfigSets (#4193)
Posted by ds...@apache.org.
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 bc8226f8a5c7ac27e116b5b8aa61446c97ae04c0
Author: David Smiley <ds...@apache.org>
AuthorDate: Thu Mar 19 22:45:59 2026 -0400
Refactor: move isFileForbiddenInConfigSets (#4193)
CLI configSet upload no longer blocks certain file types. The user of the CLI tool must already have permissions to touch ZooKeeper. Enforcement of such remains at Solr's ConfigSet HTTP endpoint.
Co-authored-by: copilot-swe-agent[bot] <19...@users.noreply.github.com>
Co-authored-by: dsmiley <37...@users.noreply.github.com>
---
changelog/unreleased/move-configset-methods.yml | 7 ++++
.../org/apache/solr/cloud/ZkConfigSetService.java | 26 +++++++++++--
.../org/apache/solr/core/ConfigSetService.java | 28 ++++++++++++++
.../solr/core/FileSystemConfigSetService.java | 5 +--
.../org/apache/solr/core/backup/BackupManager.java | 5 +--
.../solr/handler/configsets/UploadConfigSet.java | 3 +-
.../org/apache/solr/cloud/TestConfigSetsAPI.java | 7 ++--
.../solr/common/cloud/ZkMaintenanceUtils.java | 43 ++--------------------
.../solr/common/cloud/TestZkConfigSetService.java | 4 +-
9 files changed, 71 insertions(+), 57 deletions(-)
diff --git a/changelog/unreleased/move-configset-methods.yml b/changelog/unreleased/move-configset-methods.yml
new file mode 100644
index 00000000000..428ed169cf3
--- /dev/null
+++ b/changelog/unreleased/move-configset-methods.yml
@@ -0,0 +1,7 @@
+title: CLI configSet upload no longer blocks certain file types. The user of the CLI tool must already have permissions to touch ZooKeeper. Enforcement of such remains at Solr's ConfigSet HTTP endpoint.
+type: other
+authors:
+ - name: David Smiley
+links:
+ - name: PR#4193
+ url: https://github.com/apache/solr/pull/4193
diff --git a/solr/core/src/java/org/apache/solr/cloud/ZkConfigSetService.java b/solr/core/src/java/org/apache/solr/cloud/ZkConfigSetService.java
index 99b4fb2d675..a0b9880c4ca 100644
--- a/solr/core/src/java/org/apache/solr/cloud/ZkConfigSetService.java
+++ b/solr/core/src/java/org/apache/solr/cloud/ZkConfigSetService.java
@@ -18,7 +18,11 @@ package org.apache.solr.cloud;
import java.io.IOException;
import java.lang.invoke.MethodHandles;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
import java.nio.file.Path;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -168,6 +172,22 @@ public class ZkConfigSetService extends ConfigSetService {
@Override
public void uploadConfig(String configName, Path dir) throws IOException {
+ Files.walkFileTree(
+ dir,
+ new SimpleFileVisitor<>() {
+ @Override
+ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
+ String filename = file.getFileName().toString();
+ if (ConfigSetService.isFileForbiddenInConfigSets(filename)) {
+ throw new SolrException(
+ SolrException.ErrorCode.BAD_REQUEST,
+ "The file type provided for upload, '"
+ + filename
+ + "', is forbidden for use in uploading configsets.");
+ }
+ return FileVisitResult.CONTINUE;
+ }
+ });
zkClient.uploadToZK(
dir, CONFIGS_ZKNODE + "/" + configName, ConfigSetService.UPLOAD_FILENAME_EXCLUDE_PATTERN);
}
@@ -178,7 +198,7 @@ public class ZkConfigSetService extends ConfigSetService {
throws IOException {
String filePath = CONFIGS_ZKNODE + "/" + configName + "/" + fileName;
try {
- if (ZkMaintenanceUtils.isFileForbiddenInConfigSets(fileName)) {
+ if (ConfigSetService.isFileForbiddenInConfigSets(fileName)) {
throw new SolrException(
SolrException.ErrorCode.BAD_REQUEST,
"The file type provided for upload, '"
@@ -324,11 +344,11 @@ public class ZkConfigSetService extends ConfigSetService {
private void copyData(String fromZkFilePath, String toZkFilePath)
throws KeeperException, InterruptedException {
- if (ZkMaintenanceUtils.isFileForbiddenInConfigSets(fromZkFilePath)) {
+ if (ConfigSetService.isFileForbiddenInConfigSets(fromZkFilePath)) {
log.warn(
"Skipping copy of file in ZK, as the source file is a forbidden type: {}",
fromZkFilePath);
- } else if (ZkMaintenanceUtils.isFileForbiddenInConfigSets(toZkFilePath)) {
+ } else if (ConfigSetService.isFileForbiddenInConfigSets(toZkFilePath)) {
log.warn(
"Skipping download of file from ZK, as the target file is a forbidden type: {}",
toZkFilePath);
diff --git a/solr/core/src/java/org/apache/solr/core/ConfigSetService.java b/solr/core/src/java/org/apache/solr/core/ConfigSetService.java
index 40145779a0d..409809a7d98 100644
--- a/solr/core/src/java/org/apache/solr/core/ConfigSetService.java
+++ b/solr/core/src/java/org/apache/solr/core/ConfigSetService.java
@@ -26,6 +26,7 @@ import java.nio.file.Path;
import java.util.List;
import java.util.Locale;
import java.util.Map;
+import java.util.Set;
import java.util.regex.Pattern;
import org.apache.solr.cloud.ZkConfigSetService;
import org.apache.solr.cloud.ZkController;
@@ -33,6 +34,7 @@ import org.apache.solr.common.ConfigNode;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.util.EnvUtils;
import org.apache.solr.common.util.NamedList;
+import org.apache.solr.common.util.StrUtils;
import org.apache.solr.handler.admin.ConfigSetsHandler;
import org.apache.solr.schema.IndexSchema;
import org.apache.solr.schema.IndexSchemaFactory;
@@ -46,6 +48,32 @@ public abstract class ConfigSetService {
public static final String UPLOAD_FILENAME_EXCLUDE_REGEX = "^\\..*$";
public static final Pattern UPLOAD_FILENAME_EXCLUDE_PATTERN =
Pattern.compile(UPLOAD_FILENAME_EXCLUDE_REGEX);
+
+ public static final String FORBIDDEN_FILE_TYPES_PROP = "solr.configset.forbidden.file.types";
+ public static final Set<String> DEFAULT_FORBIDDEN_FILE_TYPES =
+ Set.of("class", "java", "jar", "tgz", "zip", "tar", "gz");
+
+ private static final Set<String> USE_FORBIDDEN_FILE_TYPES;
+
+ static {
+ String userForbiddenFileTypes = EnvUtils.getProperty(FORBIDDEN_FILE_TYPES_PROP);
+ USE_FORBIDDEN_FILE_TYPES =
+ StrUtils.isNullOrEmpty(userForbiddenFileTypes)
+ ? DEFAULT_FORBIDDEN_FILE_TYPES
+ : Set.of(userForbiddenFileTypes.split(","));
+ }
+
+ /**
+ * Determine if a file path is forbidden for use in configsets based on its file extension.
+ *
+ * @param filePath the file path or name to check
+ * @return true if the file extension is among the forbidden types
+ */
+ public static boolean isFileForbiddenInConfigSets(String filePath) {
+ int lastDot = filePath.lastIndexOf('.');
+ return lastDot >= 0 && USE_FORBIDDEN_FILE_TYPES.contains(filePath.substring(lastDot + 1));
+ }
+
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
public static ConfigSetService createConfigSetService(CoreContainer coreContainer) {
diff --git a/solr/core/src/java/org/apache/solr/core/FileSystemConfigSetService.java b/solr/core/src/java/org/apache/solr/core/FileSystemConfigSetService.java
index 811e6d91fee..496e9fa86a4 100644
--- a/solr/core/src/java/org/apache/solr/core/FileSystemConfigSetService.java
+++ b/solr/core/src/java/org/apache/solr/core/FileSystemConfigSetService.java
@@ -35,7 +35,6 @@ import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.solr.common.SolrException;
-import org.apache.solr.common.cloud.ZkMaintenanceUtils;
import org.apache.solr.common.util.Utils;
import org.apache.solr.util.FileTypeMagicUtil;
import org.apache.solr.util.FileUtils;
@@ -155,7 +154,7 @@ public class FileSystemConfigSetService extends ConfigSetService {
public void uploadFileToConfig(
String configName, String fileName, byte[] data, boolean overwriteOnExists)
throws IOException {
- if (ZkMaintenanceUtils.isFileForbiddenInConfigSets(fileName)) {
+ if (ConfigSetService.isFileForbiddenInConfigSets(fileName)) {
throw new SolrException(
SolrException.ErrorCode.BAD_REQUEST,
"The file type provided for upload, '"
@@ -234,7 +233,7 @@ public class FileSystemConfigSetService extends ConfigSetService {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
throws IOException {
- if (ZkMaintenanceUtils.isFileForbiddenInConfigSets(file.getFileName().toString())) {
+ if (ConfigSetService.isFileForbiddenInConfigSets(file.getFileName().toString())) {
log.warn(
"Not including uploading file to config, as it is a forbidden type: {}",
file.getFileName());
diff --git a/solr/core/src/java/org/apache/solr/core/backup/BackupManager.java b/solr/core/src/java/org/apache/solr/core/backup/BackupManager.java
index 2525bc36ad8..f47df829f74 100644
--- a/solr/core/src/java/org/apache/solr/core/backup/BackupManager.java
+++ b/solr/core/src/java/org/apache/solr/core/backup/BackupManager.java
@@ -37,7 +37,6 @@ import org.apache.solr.common.SolrException.ErrorCode;
import org.apache.solr.common.cloud.ClusterState;
import org.apache.solr.common.cloud.DocCollection;
import org.apache.solr.common.cloud.SolrZkClient;
-import org.apache.solr.common.cloud.ZkMaintenanceUtils;
import org.apache.solr.common.cloud.ZkStateReader;
import org.apache.solr.common.util.Utils;
import org.apache.solr.core.ConfigSetService;
@@ -344,7 +343,7 @@ public class BackupManager {
// checking for '/' is correct for a directory since ConfigSetService#getAllConfigFiles
// always separates file paths with '/'
if (!filePath.endsWith("/")) {
- if (ZkMaintenanceUtils.isFileForbiddenInConfigSets(filePath)) {
+ if (ConfigSetService.isFileForbiddenInConfigSets(filePath)) {
log.warn(
"Not including zookeeper file in backup, as it is a forbidden type: {}", filePath);
} else {
@@ -385,7 +384,7 @@ public class BackupManager {
switch (t) {
case FILE:
{
- if (ZkMaintenanceUtils.isFileForbiddenInConfigSets(filePath)) {
+ if (ConfigSetService.isFileForbiddenInConfigSets(filePath)) {
log.warn(
"Not including zookeeper file in restore, as it is a forbidden type: {}", file);
} else {
diff --git a/solr/core/src/java/org/apache/solr/handler/configsets/UploadConfigSet.java b/solr/core/src/java/org/apache/solr/handler/configsets/UploadConfigSet.java
index d87c38154f5..617d5c8af22 100644
--- a/solr/core/src/java/org/apache/solr/handler/configsets/UploadConfigSet.java
+++ b/solr/core/src/java/org/apache/solr/handler/configsets/UploadConfigSet.java
@@ -30,7 +30,6 @@ import java.util.zip.ZipInputStream;
import org.apache.solr.client.api.endpoint.ConfigsetsApi;
import org.apache.solr.client.api.model.SolrJerseyResponse;
import org.apache.solr.common.SolrException;
-import org.apache.solr.common.cloud.ZkMaintenanceUtils;
import org.apache.solr.core.ConfigSetService;
import org.apache.solr.core.CoreContainer;
import org.apache.solr.jersey.PermissionName;
@@ -129,7 +128,7 @@ public class UploadConfigSet extends ConfigSetAPIBase implements ConfigsetsApi.U
throw new SolrException(
SolrException.ErrorCode.BAD_REQUEST,
"The file path provided for upload, '" + singleFilePath + "', is not valid.");
- } else if (ZkMaintenanceUtils.isFileForbiddenInConfigSets(fixedSingleFilePath)
+ } else if (ConfigSetService.isFileForbiddenInConfigSets(fixedSingleFilePath)
|| FileTypeMagicUtil.isFileForbiddenInConfigset(data)) {
throw new SolrException(
SolrException.ErrorCode.BAD_REQUEST,
diff --git a/solr/core/src/test/org/apache/solr/cloud/TestConfigSetsAPI.java b/solr/core/src/test/org/apache/solr/cloud/TestConfigSetsAPI.java
index a09737174ae..5dc13d0ca3a 100644
--- a/solr/core/src/test/org/apache/solr/cloud/TestConfigSetsAPI.java
+++ b/solr/core/src/test/org/apache/solr/cloud/TestConfigSetsAPI.java
@@ -84,7 +84,6 @@ import org.apache.solr.client.solrj.response.schema.SchemaResponse;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.SolrInputDocument;
import org.apache.solr.common.cloud.SolrZkClient;
-import org.apache.solr.common.cloud.ZkMaintenanceUtils;
import org.apache.solr.common.params.CollectionParams.CollectionAction;
import org.apache.solr.common.params.ConfigSetParams;
import org.apache.solr.common.params.ConfigSetParams.ConfigSetAction;
@@ -583,7 +582,7 @@ public class TestConfigSetsAPI extends SolrCloudTestCase {
assertEquals(
400, uploadConfigSet(configsetName, configsetSuffix, null, true, false, v2, true, false));
- for (String fileEnding : ZkMaintenanceUtils.DEFAULT_FORBIDDEN_FILE_TYPES) {
+ for (String fileEnding : ConfigSetService.DEFAULT_FORBIDDEN_FILE_TYPES) {
String f = configPath + "/test." + fileEnding;
assertFalse(
"Expecting file " + f + " to not exist, because it has a forbidden file type",
@@ -804,7 +803,7 @@ public class TestConfigSetsAPI extends SolrCloudTestCase {
.withTimeout(AbstractZkTestCase.TIMEOUT, TimeUnit.MILLISECONDS)
.withConnTimeOut(45000, TimeUnit.MILLISECONDS)
.build()) {
- for (String fileType : ZkMaintenanceUtils.DEFAULT_FORBIDDEN_FILE_TYPES) {
+ for (String fileType : ConfigSetService.DEFAULT_FORBIDDEN_FILE_TYPES) {
ignoreException("is forbidden for use in configSets");
assertEquals(
"Can't upload a configset file with a forbidden type: " + fileType,
@@ -1385,7 +1384,7 @@ public class TestConfigSetsAPI extends SolrCloudTestCase {
try (ZipOutputStream zout = new ZipOutputStream(out)) {
if (Files.isRegularFile(fileOrDirectory)) {
// Create entries with given file, one for each forbidden endding
- for (String fileType : ZkMaintenanceUtils.DEFAULT_FORBIDDEN_FILE_TYPES) {
+ for (String fileType : ConfigSetService.DEFAULT_FORBIDDEN_FILE_TYPES) {
zout.putNextEntry(new ZipEntry("test." + fileType));
try (InputStream in = Files.newInputStream(fileOrDirectory)) {
diff --git a/solr/solrj-zookeeper/src/java/org/apache/solr/common/cloud/ZkMaintenanceUtils.java b/solr/solrj-zookeeper/src/java/org/apache/solr/common/cloud/ZkMaintenanceUtils.java
index 0d98a65da7c..6438e84bbfc 100644
--- a/solr/solrj-zookeeper/src/java/org/apache/solr/common/cloud/ZkMaintenanceUtils.java
+++ b/solr/solrj-zookeeper/src/java/org/apache/solr/common/cloud/ZkMaintenanceUtils.java
@@ -29,12 +29,9 @@ import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Locale;
-import java.util.Set;
import java.util.function.Predicate;
import java.util.regex.Pattern;
import org.apache.solr.client.solrj.SolrServerException;
-import org.apache.solr.common.SolrException;
-import org.apache.solr.common.util.EnvUtils;
import org.apache.solr.common.util.StrUtils;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
@@ -345,13 +342,6 @@ public class ZkMaintenanceUtils {
filenameExclusions);
return FileVisitResult.CONTINUE;
}
- if (isFileForbiddenInConfigSets(filename)) {
- throw new SolrException(
- SolrException.ErrorCode.BAD_REQUEST,
- "The file type provided for upload, '"
- + filename
- + "', is forbidden for use in uploading configsets.");
- }
// TODO: Cannot check MAGIC header for file since FileTypeGuesser is in core
String zkNode = createZkNodeName(zkPath, rootPath, file);
try {
@@ -443,13 +433,9 @@ public class ZkMaintenanceUtils {
if (children.size() == 0) {
// If we didn't copy data down, then we also didn't create the file. But we still need a
// marker on the local disk so create an empty file.
- if (isFileForbiddenInConfigSets(zkPath)) {
- log.warn("Skipping download of file from ZK, as it is a forbidden type: {}", zkPath);
- } else {
- // TODO: Cannot check MAGIC header for file since FileTypeGuesser is in core
- if (copyDataDown(zkClient, zkPath, file) == 0) {
- Files.createFile(file);
- }
+ // TODO: Cannot check MAGIC header for file since FileTypeGuesser is in core
+ if (copyDataDown(zkClient, zkPath, file) == 0) {
+ Files.createFile(file);
}
} else {
Files.createDirectories(file); // Make parent dir.
@@ -581,29 +567,6 @@ public class ZkMaintenanceUtils {
return ret;
}
- public static final String FORBIDDEN_FILE_TYPES_PROP = "solr.configset.forbidden.file.types";
- public static final Set<String> DEFAULT_FORBIDDEN_FILE_TYPES =
- Set.of("class", "java", "jar", "tgz", "zip", "tar", "gz");
- private static volatile Set<String> USE_FORBIDDEN_FILE_TYPES = null;
-
- public static boolean isFileForbiddenInConfigSets(String filePath) {
- // Try to set the forbidden file types just once, since it is set by SysProp/EnvVar
- if (USE_FORBIDDEN_FILE_TYPES == null) {
- synchronized (DEFAULT_FORBIDDEN_FILE_TYPES) {
- if (USE_FORBIDDEN_FILE_TYPES == null) {
- String userForbiddenFileTypes = EnvUtils.getProperty(FORBIDDEN_FILE_TYPES_PROP);
- if (StrUtils.isNullOrEmpty(userForbiddenFileTypes)) {
- USE_FORBIDDEN_FILE_TYPES = DEFAULT_FORBIDDEN_FILE_TYPES;
- } else {
- USE_FORBIDDEN_FILE_TYPES = Set.of(userForbiddenFileTypes.split(","));
- }
- }
- }
- }
- int lastDot = filePath.lastIndexOf('.');
- return lastDot >= 0 && USE_FORBIDDEN_FILE_TYPES.contains(filePath.substring(lastDot + 1));
- }
-
/**
* Create a persistent znode with no data if it does not already exist
*
diff --git a/solr/solrj-zookeeper/src/test/org/apache/solr/common/cloud/TestZkConfigSetService.java b/solr/solrj-zookeeper/src/test/org/apache/solr/common/cloud/TestZkConfigSetService.java
index 9928d543210..d7ed83b4d61 100644
--- a/solr/solrj-zookeeper/src/test/org/apache/solr/common/cloud/TestZkConfigSetService.java
+++ b/solr/solrj-zookeeper/src/test/org/apache/solr/common/cloud/TestZkConfigSetService.java
@@ -131,7 +131,7 @@ public class TestZkConfigSetService extends SolrTestCaseJ4 {
Files.write(tempConfig.resolve("file1"), overwritten);
configSetService.uploadConfig("testconfig", tempConfig);
- assertEquals(2, configSetService.listConfigs().size());
+ assertEquals(1, configSetService.listConfigs().size());
Path download2 = createTempDir("download2");
configSetService.downloadConfig("testconfig", download2);
byte[] checkdata2 = Files.readAllBytes(download2.resolve("file1"));
@@ -139,7 +139,7 @@ public class TestZkConfigSetService extends SolrTestCaseJ4 {
// uploading same files to a new name creates a new config
configSetService.uploadConfig("config2", tempConfig);
- assertEquals(3, configSetService.listConfigs().size());
+ assertEquals(2, configSetService.listConfigs().size());
// Test copying a config works in both flavors
configSetService.copyConfig("config2", "config2copy");
(solr) 06/06: SOLR-18112: SolrDispatchFilter is now SolrServlet (#4119)
Posted by ds...@apache.org.
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 518455f3f367fd371ad39011591a321d055a110c
Author: David Smiley <ds...@apache.org>
AuthorDate: Thu Mar 26 21:11:52 2026 -0400
SOLR-18112: SolrDispatchFilter is now SolrServlet (#4119)
And reduced the contents of SolrServlet:
* Moved constants to better places
* Moved "Action" enum into HttpSolrCall
* Removed abortErrorMessage; not used
Updated some tests/references to not refer to SolrServlet needlessly.
Co-authored-by: copilot-swe-agent[bot] <19...@users.noreply.github.com>
Co-authored-by: dsmiley <37...@users.noreply.github.com>
---
.../SOLR-18112-SolrDispatchFilterServlet.yml | 7 ++
.../apache/solr/api/CoordinatorV2HttpSolrCall.java | 7 +-
.../src/java/org/apache/solr/api/V2HttpCall.java | 17 +--
.../org/apache/solr/cli/SolrProcessManager.java | 4 +-
.../java/org/apache/solr/cloud/SolrZkServer.java | 6 +-
.../org/apache/solr/core/ConfigSetService.java | 12 +-
.../src/java/org/apache/solr/core/NodeConfig.java | 8 +-
.../java/org/apache/solr/core/SolrXmlConfig.java | 15 ++-
.../org/apache/solr/request/SolrRequestInfo.java | 13 +-
.../org/apache/solr/rest/BaseSolrResource.java | 4 +-
.../solr/servlet/CoordinatorHttpSolrCall.java | 16 ++-
.../apache/solr/servlet/CoreContainerProvider.java | 26 ++--
.../java/org/apache/solr/servlet/HttpSolrCall.java | 46 ++++---
.../apache/solr/servlet/LoadAdminUiServlet.java | 12 +-
.../solr/servlet/RequiredSolrRequestFilter.java | 1 -
.../apache/solr/servlet/SolrRequestParsers.java | 16 +--
.../{SolrDispatchFilter.java => SolrServlet.java} | 133 ++++++++-------------
.../org/apache/solr/cloud/DeleteReplicaTest.java | 2 +-
.../solr/cloud/TestCloudSearcherWarming.java | 4 +-
.../org/apache/solr/cloud/TestConfigSetsAPI.java | 9 +-
.../org/apache/solr/core/TestCoreContainer.java | 9 +-
.../apache/solr/servlet/HttpSolrCallCloudTest.java | 28 ++++-
.../solr/servlet/LoadAdminUiServletTest.java | 4 +
.../solr/configsets/_default/conf/solrconfig.xml | 8 +-
.../conf/solrconfig.xml | 6 -
.../src/java/org/apache/solr/SolrTestCase.java | 14 +--
.../solr/client/solrj/apache/HttpSolrClient.java | 2 +-
.../org/apache/solr/embedded/JettySolrRunner.java | 64 +++++-----
.../src/java/org/apache/solr/util/TestHarness.java | 11 +-
solr/webapp/web/WEB-INF/web.xml | 15 +--
30 files changed, 245 insertions(+), 274 deletions(-)
diff --git a/changelog/unreleased/SOLR-18112-SolrDispatchFilterServlet.yml b/changelog/unreleased/SOLR-18112-SolrDispatchFilterServlet.yml
new file mode 100644
index 00000000000..3acba548f49
--- /dev/null
+++ b/changelog/unreleased/SOLR-18112-SolrDispatchFilterServlet.yml
@@ -0,0 +1,7 @@
+title: SolrDispatchFilter is now SolrServlet; isn't a Filter
+type: other
+authors:
+ - name: David Smiley
+links:
+ - name: SOLR-18112
+ url: https://issues.apache.org/jira/browse/SOLR-18112
diff --git a/solr/core/src/java/org/apache/solr/api/CoordinatorV2HttpSolrCall.java b/solr/core/src/java/org/apache/solr/api/CoordinatorV2HttpSolrCall.java
index 4fb2e888d50..c2b17be5ef7 100644
--- a/solr/core/src/java/org/apache/solr/api/CoordinatorV2HttpSolrCall.java
+++ b/solr/core/src/java/org/apache/solr/api/CoordinatorV2HttpSolrCall.java
@@ -22,7 +22,7 @@ import jakarta.servlet.http.HttpServletResponse;
import org.apache.solr.core.CoreContainer;
import org.apache.solr.core.SolrCore;
import org.apache.solr.servlet.CoordinatorHttpSolrCall;
-import org.apache.solr.servlet.SolrDispatchFilter;
+import org.apache.solr.servlet.HttpSolrCall;
public class CoordinatorV2HttpSolrCall extends V2HttpCall {
private String collectionName;
@@ -30,12 +30,11 @@ public class CoordinatorV2HttpSolrCall extends V2HttpCall {
public CoordinatorV2HttpSolrCall(
CoordinatorHttpSolrCall.Factory factory,
- SolrDispatchFilter solrDispatchFilter,
CoreContainer cc,
HttpServletRequest request,
HttpServletResponse response,
boolean retry) {
- super(solrDispatchFilter, cc, request, response, retry);
+ super(cc, request, response, retry);
this.factory = factory;
}
@@ -51,7 +50,7 @@ public class CoordinatorV2HttpSolrCall extends V2HttpCall {
@Override
protected void init() throws Exception {
super.init();
- if (action == SolrDispatchFilter.Action.PROCESS && core != null) {
+ if (action == HttpSolrCall.Action.PROCESS && core != null) {
solrReq = CoordinatorHttpSolrCall.wrappedReq(solrReq, collectionName, this);
}
}
diff --git a/solr/core/src/java/org/apache/solr/api/V2HttpCall.java b/solr/core/src/java/org/apache/solr/api/V2HttpCall.java
index 04ea7c6ee9e..ba2b6fa7af8 100644
--- a/solr/core/src/java/org/apache/solr/api/V2HttpCall.java
+++ b/solr/core/src/java/org/apache/solr/api/V2HttpCall.java
@@ -18,10 +18,10 @@
package org.apache.solr.api;
import static org.apache.solr.common.cloud.ZkStateReader.COLLECTION_PROP;
-import static org.apache.solr.servlet.SolrDispatchFilter.Action.ADMIN;
-import static org.apache.solr.servlet.SolrDispatchFilter.Action.ADMIN_OR_REMOTEPROXY;
-import static org.apache.solr.servlet.SolrDispatchFilter.Action.PROCESS;
-import static org.apache.solr.servlet.SolrDispatchFilter.Action.REMOTEPROXY;
+import static org.apache.solr.servlet.HttpSolrCall.Action.ADMIN;
+import static org.apache.solr.servlet.HttpSolrCall.Action.ADMIN_OR_REMOTEPROXY;
+import static org.apache.solr.servlet.HttpSolrCall.Action.PROCESS;
+import static org.apache.solr.servlet.HttpSolrCall.Action.REMOTEPROXY;
import io.opentelemetry.api.trace.Span;
import jakarta.servlet.http.HttpServletRequest;
@@ -56,7 +56,6 @@ import org.apache.solr.response.QueryResponseWriter;
import org.apache.solr.response.SolrQueryResponse;
import org.apache.solr.security.AuthorizationContext;
import org.apache.solr.servlet.HttpSolrCall;
-import org.apache.solr.servlet.SolrDispatchFilter;
import org.apache.solr.servlet.SolrRequestParsers;
import org.apache.solr.servlet.cache.Method;
import org.apache.solr.util.tracing.TraceUtils;
@@ -78,12 +77,8 @@ public class V2HttpCall extends HttpSolrCall {
static final Set<String> knownPrefixes = Set.of("cluster", "node", "collections", "cores", "c");
public V2HttpCall(
- SolrDispatchFilter solrDispatchFilter,
- CoreContainer cc,
- HttpServletRequest request,
- HttpServletResponse response,
- boolean retry) {
- super(solrDispatchFilter, cc, request, response, retry);
+ CoreContainer cc, HttpServletRequest request, HttpServletResponse response, boolean retry) {
+ super(cc, request, response, retry);
}
@Override
diff --git a/solr/core/src/java/org/apache/solr/cli/SolrProcessManager.java b/solr/core/src/java/org/apache/solr/cli/SolrProcessManager.java
index 67146509da5..3001fd2afd5 100644
--- a/solr/core/src/java/org/apache/solr/cli/SolrProcessManager.java
+++ b/solr/core/src/java/org/apache/solr/cli/SolrProcessManager.java
@@ -16,7 +16,7 @@
*/
package org.apache.solr.cli;
-import static org.apache.solr.servlet.SolrDispatchFilter.SOLR_INSTALL_DIR_ATTRIBUTE;
+import static org.apache.solr.servlet.CoreContainerProvider.SOLR_INSTALL_DIR;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
@@ -76,7 +76,7 @@ public class SolrProcessManager {
ph.pid(), parsePortFromProcess(ph).orElseThrow(), isProcessSsl(ph))));
portProcessMap =
pidProcessMap.values().stream().collect(Collectors.toUnmodifiableMap(p -> p.port, p -> p));
- String solrInstallDir = EnvUtils.getProperty(SOLR_INSTALL_DIR_ATTRIBUTE);
+ String solrInstallDir = EnvUtils.getProperty(SOLR_INSTALL_DIR);
pidDir =
Path.of(
EnvUtils.getProperty(
diff --git a/solr/core/src/java/org/apache/solr/cloud/SolrZkServer.java b/solr/core/src/java/org/apache/solr/cloud/SolrZkServer.java
index 84617c70027..cdd3ae90f86 100644
--- a/solr/core/src/java/org/apache/solr/cloud/SolrZkServer.java
+++ b/solr/core/src/java/org/apache/solr/cloud/SolrZkServer.java
@@ -27,7 +27,7 @@ import java.util.Properties;
import java.util.concurrent.atomic.AtomicReference;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.util.EnvUtils;
-import org.apache.solr.servlet.SolrDispatchFilter;
+import org.apache.solr.servlet.CoreContainerProvider;
import org.apache.solr.util.AddressUtils;
import org.apache.zookeeper.server.ServerConfig;
import org.apache.zookeeper.server.ZooKeeperServerMain;
@@ -104,12 +104,12 @@ public class SolrZkServer {
var zooCfgPath = Path.of(confHome).resolve("zoo.cfg");
if (!Files.exists(zooCfgPath)) {
log.info("Zookeeper configuration not found in {}, using built-in default", confHome);
- String solrInstallDir = System.getProperty(SolrDispatchFilter.SOLR_INSTALL_DIR_ATTRIBUTE);
+ String solrInstallDir = System.getProperty(CoreContainerProvider.SOLR_INSTALL_DIR);
if (solrInstallDir == null) {
throw new SolrException(
SolrException.ErrorCode.SERVER_ERROR,
"Could not find default zoo.cfg file due to missing "
- + SolrDispatchFilter.SOLR_INSTALL_DIR_ATTRIBUTE);
+ + CoreContainerProvider.SOLR_INSTALL_DIR);
}
zooCfgPath = Path.of(solrInstallDir).resolve("server").resolve("solr").resolve("zoo.cfg");
}
diff --git a/solr/core/src/java/org/apache/solr/core/ConfigSetService.java b/solr/core/src/java/org/apache/solr/core/ConfigSetService.java
index 409809a7d98..99d237f9e38 100644
--- a/solr/core/src/java/org/apache/solr/core/ConfigSetService.java
+++ b/solr/core/src/java/org/apache/solr/core/ConfigSetService.java
@@ -38,7 +38,7 @@ import org.apache.solr.common.util.StrUtils;
import org.apache.solr.handler.admin.ConfigSetsHandler;
import org.apache.solr.schema.IndexSchema;
import org.apache.solr.schema.IndexSchemaFactory;
-import org.apache.solr.servlet.SolrDispatchFilter;
+import org.apache.solr.servlet.CoreContainerProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -48,6 +48,7 @@ public abstract class ConfigSetService {
public static final String UPLOAD_FILENAME_EXCLUDE_REGEX = "^\\..*$";
public static final Pattern UPLOAD_FILENAME_EXCLUDE_PATTERN =
Pattern.compile(UPLOAD_FILENAME_EXCLUDE_REGEX);
+ public static final String SOLR_CONFIGSET_DEFAULT_CONFDIR = "solr.configset.default.confdir";
public static final String FORBIDDEN_FILE_TYPES_PROP = "solr.configset.forbidden.file.types";
public static final Set<String> DEFAULT_FORBIDDEN_FILE_TYPES =
@@ -136,7 +137,7 @@ public abstract class ConfigSetService {
log.warn(
"The _default configset could not be uploaded. Please provide 'solr.configset.default.confdir' parameter that points to a configset {} {}",
"intended to be the default. Current 'solr.configset.default.confdir' value:",
- System.getProperty(SolrDispatchFilter.SOLR_CONFIGSET_DEFAULT_CONFDIR_ATTRIBUTE));
+ System.getProperty(SOLR_CONFIGSET_DEFAULT_CONFDIR));
} else {
this.uploadConfig(ConfigSetsHandler.DEFAULT_CONFIGSET_NAME, configDirPath);
}
@@ -161,11 +162,10 @@ public abstract class ConfigSetService {
* to the sysprop "solr.install.dir". Returns null if not found anywhere.
*
* @lucene.internal
- * @see SolrDispatchFilter#SOLR_CONFIGSET_DEFAULT_CONFDIR_ATTRIBUTE
+ * @see ConfigSetService#SOLR_CONFIGSET_DEFAULT_CONFDIR
*/
public static Path getDefaultConfigDirPath() {
- String confDir =
- System.getProperty(SolrDispatchFilter.SOLR_CONFIGSET_DEFAULT_CONFDIR_ATTRIBUTE);
+ String confDir = System.getProperty(SOLR_CONFIGSET_DEFAULT_CONFDIR);
if (confDir != null) {
Path path = Path.of(confDir);
if (Files.exists(path)) {
@@ -173,7 +173,7 @@ public abstract class ConfigSetService {
}
}
- String installDir = System.getProperty(SolrDispatchFilter.SOLR_INSTALL_DIR_ATTRIBUTE);
+ String installDir = System.getProperty(CoreContainerProvider.SOLR_INSTALL_DIR);
if (installDir != null) {
Path subPath = Path.of("server", "solr", "configsets", "_default", "conf");
Path path = Path.of(installDir).resolve(subPath);
diff --git a/solr/core/src/java/org/apache/solr/core/NodeConfig.java b/solr/core/src/java/org/apache/solr/core/NodeConfig.java
index c52cb54e60e..1634c2c29e8 100644
--- a/solr/core/src/java/org/apache/solr/core/NodeConfig.java
+++ b/solr/core/src/java/org/apache/solr/core/NodeConfig.java
@@ -43,7 +43,7 @@ import org.apache.solr.common.util.EnvUtils;
import org.apache.solr.common.util.StrUtils;
import org.apache.solr.logging.LogWatcherConfig;
import org.apache.solr.search.CacheConfig;
-import org.apache.solr.servlet.SolrDispatchFilter;
+import org.apache.solr.servlet.CoreContainerProvider;
import org.apache.solr.update.UpdateShardHandlerConfig;
import org.apache.solr.util.ModuleUtils;
import org.slf4j.Logger;
@@ -294,7 +294,7 @@ public class NodeConfig {
* @return path to install dir or null if solr.install.dir not set.
*/
public static Path getSolrInstallDir() {
- String prop = System.getProperty(SolrDispatchFilter.SOLR_INSTALL_DIR_ATTRIBUTE);
+ String prop = System.getProperty(CoreContainerProvider.SOLR_INSTALL_DIR);
if (prop == null || prop.isBlank()) {
log.debug("solr.install.dir property not initialized.");
return null;
@@ -451,7 +451,7 @@ public class NodeConfig {
if (solrInstallDir == null) {
log.warn(
"Unable to add $SOLR_HOME/lib for shared lib since {} was not set.",
- SolrDispatchFilter.SOLR_INSTALL_DIR_ATTRIBUTE);
+ CoreContainerProvider.SOLR_INSTALL_DIR);
} else {
// Always add $SOLR_TIP/lib to the shared resource loader
libDirs.add(solrInstallDir.resolve("lib").toAbsolutePath().normalize().toString());
@@ -524,7 +524,7 @@ public class NodeConfig {
"Unable to setup modules "
+ moduleNames
+ " because "
- + SolrDispatchFilter.SOLR_INSTALL_DIR_ATTRIBUTE
+ + CoreContainerProvider.SOLR_INSTALL_DIR
+ " was not set.");
}
return;
diff --git a/solr/core/src/java/org/apache/solr/core/SolrXmlConfig.java b/solr/core/src/java/org/apache/solr/core/SolrXmlConfig.java
index 14e87a52701..c4b801a28bc 100644
--- a/solr/core/src/java/org/apache/solr/core/SolrXmlConfig.java
+++ b/solr/core/src/java/org/apache/solr/core/SolrXmlConfig.java
@@ -49,7 +49,7 @@ import org.apache.solr.common.util.StrUtils;
import org.apache.solr.common.util.Utils;
import org.apache.solr.logging.LogWatcherConfig;
import org.apache.solr.search.CacheConfig;
-import org.apache.solr.servlet.SolrDispatchFilter;
+import org.apache.solr.servlet.CoreContainerProvider;
import org.apache.solr.update.UpdateShardHandlerConfig;
import org.apache.solr.util.DOMConfigNode;
import org.apache.solr.util.DataConfigNode;
@@ -77,9 +77,8 @@ public class SolrXmlConfig {
* wrapping the original Properties, with the 'zkHost' value set based on the value of the
* corresponding System property (if set)
*
- * <p>In theory we only need this logic once, ideally in SolrDispatchFilter, but we put it here to
- * re-use redundantly because of how much surface area our API has for various tests to poke at
- * us.
+ * <p>In theory we only need this logic once, ideally in SolrServlet, but we put it here to re-use
+ * redundantly because of how much surface area our API has for various tests to poke at us.
*/
public static Properties wrapAndSetZkHostFromSysPropIfNeeded(final Properties props) {
if (null != props && StrUtils.isNotNullOrEmpty(props.getProperty(ZK_HOST))) {
@@ -104,8 +103,8 @@ public class SolrXmlConfig {
// *directly* check the system
// property if it's not specified.
//
- // (checking the sys prop here is really just for tests that by-pass SolrDispatchFilter. In
- // non-test situations, SolrDispatchFilter will check the system property if needed in order to
+ // (checking the sys prop here is really just for tests that by-pass SolrServlet. In
+ // non-test situations, SolrServlet will check the system property if needed in order to
// try and load solr.xml from ZK, and should have put the sys prop value in the node properties
// for us)
final String defaultZkHost =
@@ -180,12 +179,12 @@ public class SolrXmlConfig {
"solr.xml does not exist in " + configFile.getParent() + " cannot start Solr");
}
log.info("solr.xml not found in SOLR_HOME, using built-in default");
- String solrInstallDir = System.getProperty(SolrDispatchFilter.SOLR_INSTALL_DIR_ATTRIBUTE);
+ String solrInstallDir = System.getProperty(CoreContainerProvider.SOLR_INSTALL_DIR);
if (solrInstallDir == null) {
throw new SolrException(
SolrException.ErrorCode.SERVER_ERROR,
"Could not find default solr.xml file due to missing "
- + SolrDispatchFilter.SOLR_INSTALL_DIR_ATTRIBUTE);
+ + CoreContainerProvider.SOLR_INSTALL_DIR);
}
configFile = Path.of(solrInstallDir).resolve("server").resolve("solr").resolve("solr.xml");
}
diff --git a/solr/core/src/java/org/apache/solr/request/SolrRequestInfo.java b/solr/core/src/java/org/apache/solr/request/SolrRequestInfo.java
index a7d92859d26..fe3b6a69742 100644
--- a/solr/core/src/java/org/apache/solr/request/SolrRequestInfo.java
+++ b/solr/core/src/java/org/apache/solr/request/SolrRequestInfo.java
@@ -32,7 +32,7 @@ import org.apache.solr.common.util.ExecutorUtil;
import org.apache.solr.handler.component.ResponseBuilder;
import org.apache.solr.response.SolrQueryResponse;
import org.apache.solr.search.QueryLimits;
-import org.apache.solr.servlet.SolrDispatchFilter;
+import org.apache.solr.servlet.HttpSolrCall;
import org.apache.solr.util.TimeZoneUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -54,7 +54,7 @@ public class SolrRequestInfo {
private TimeZone tz;
private ResponseBuilder rb;
private List<AutoCloseable> closeHooks;
- private SolrDispatchFilter.Action action;
+ private HttpSolrCall.Action action;
private boolean useServerToken = false;
private Principal principal;
@@ -154,8 +154,7 @@ public class SolrRequestInfo {
this.principal = req != null ? req.getUserPrincipal() : null;
}
- public SolrRequestInfo(
- SolrQueryRequest req, SolrQueryResponse rsp, SolrDispatchFilter.Action action) {
+ public SolrRequestInfo(SolrQueryRequest req, SolrQueryResponse rsp, HttpSolrCall.Action action) {
this(req, rsp);
this.setAction(action);
}
@@ -166,7 +165,7 @@ public class SolrRequestInfo {
}
public SolrRequestInfo(
- HttpServletRequest httpReq, SolrQueryResponse rsp, SolrDispatchFilter.Action action) {
+ HttpServletRequest httpReq, SolrQueryResponse rsp, HttpSolrCall.Action action) {
this(httpReq, rsp);
this.action = action;
}
@@ -259,11 +258,11 @@ public class SolrRequestInfo {
request.getContext().computeIfAbsent(LIMITS_KEY, (k) -> new QueryLimits(request, response));
}
- public SolrDispatchFilter.Action getAction() {
+ public HttpSolrCall.Action getAction() {
return action;
}
- public void setAction(SolrDispatchFilter.Action action) {
+ public void setAction(HttpSolrCall.Action action) {
this.action = action;
}
diff --git a/solr/core/src/java/org/apache/solr/rest/BaseSolrResource.java b/solr/core/src/java/org/apache/solr/rest/BaseSolrResource.java
index 2591a9f1fe3..873a2aadc37 100644
--- a/solr/core/src/java/org/apache/solr/rest/BaseSolrResource.java
+++ b/solr/core/src/java/org/apache/solr/rest/BaseSolrResource.java
@@ -80,8 +80,8 @@ public abstract class BaseSolrResource {
}
/**
- * Pulls the SolrQueryRequest constructed in SolrDispatchFilter from the SolrRequestInfo thread
- * local, then gets the SolrCore and IndexSchema and sets up the response. writer.
+ * Pulls the SolrQueryRequest constructed in SolrServlet from the SolrRequestInfo thread local,
+ * then gets the SolrCore and IndexSchema and sets up the response. writer.
*/
public void doInit(SolrQueryRequest solrRequest, SolrQueryResponse solrResponse) {
try {
diff --git a/solr/core/src/java/org/apache/solr/servlet/CoordinatorHttpSolrCall.java b/solr/core/src/java/org/apache/solr/servlet/CoordinatorHttpSolrCall.java
index f28ffdafaaf..450d6c2c652 100644
--- a/solr/core/src/java/org/apache/solr/servlet/CoordinatorHttpSolrCall.java
+++ b/solr/core/src/java/org/apache/solr/servlet/CoordinatorHttpSolrCall.java
@@ -58,12 +58,11 @@ public class CoordinatorHttpSolrCall extends HttpSolrCall {
public CoordinatorHttpSolrCall(
Factory factory,
- SolrDispatchFilter solrDispatchFilter,
CoreContainer cores,
HttpServletRequest request,
HttpServletResponse response,
boolean retry) {
- super(solrDispatchFilter, cores, request, response, retry);
+ super(cores, request, response, retry);
this.factory = factory;
}
@@ -150,7 +149,7 @@ public class CoordinatorHttpSolrCall extends HttpSolrCall {
@Override
protected void init() throws Exception {
super.init();
- if (action == SolrDispatchFilter.Action.PROCESS && core != null) {
+ if (action == HttpSolrCall.Action.PROCESS && core != null) {
solrReq = wrappedReq(solrReq, collectionName, this);
}
}
@@ -199,24 +198,23 @@ public class CoordinatorHttpSolrCall extends HttpSolrCall {
}
// The factory that creates an instance of HttpSolrCall
- public static class Factory implements SolrDispatchFilter.HttpSolrCallFactory {
+ public static class Factory implements SolrServlet.HttpSolrCallFactory {
private final Map<String, String> collectionVsCoreNameMapping = new ConcurrentHashMap<>();
@Override
public HttpSolrCall createInstance(
- SolrDispatchFilter filter,
String path,
CoreContainer cores,
HttpServletRequest request,
HttpServletResponse response,
boolean retry) {
if ((path.startsWith("/____v2/") || path.equals("/____v2"))) {
- return new CoordinatorV2HttpSolrCall(this, filter, cores, request, response, retry);
+ return new CoordinatorV2HttpSolrCall(this, cores, request, response, retry);
} else if (path.startsWith("/" + SYNTHETIC_COLL_PREFIX)) {
- return SolrDispatchFilter.HttpSolrCallFactory.super.createInstance(
- filter, path, cores, request, response, retry);
+ return SolrServlet.HttpSolrCallFactory.super.createInstance(
+ path, cores, request, response, retry);
} else {
- return new CoordinatorHttpSolrCall(this, filter, cores, request, response, retry);
+ return new CoordinatorHttpSolrCall(this, cores, request, response, retry);
}
}
}
diff --git a/solr/core/src/java/org/apache/solr/servlet/CoreContainerProvider.java b/solr/core/src/java/org/apache/solr/servlet/CoreContainerProvider.java
index c718ef0e569..19d06f392b7 100644
--- a/solr/core/src/java/org/apache/solr/servlet/CoreContainerProvider.java
+++ b/solr/core/src/java/org/apache/solr/servlet/CoreContainerProvider.java
@@ -17,11 +17,6 @@
package org.apache.solr.servlet;
import static org.apache.solr.core.NodeConfig.loadNodeConfig;
-import static org.apache.solr.servlet.SolrDispatchFilter.PROPERTIES_ATTRIBUTE;
-import static org.apache.solr.servlet.SolrDispatchFilter.SOLRHOME_ATTRIBUTE;
-import static org.apache.solr.servlet.SolrDispatchFilter.SOLR_INSTALL_DIR_ATTRIBUTE;
-import static org.apache.solr.servlet.SolrDispatchFilter.SOLR_LOG_LEVEL;
-import static org.apache.solr.servlet.SolrDispatchFilter.SOLR_LOG_MUTECONSOLE;
import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletContextEvent;
@@ -61,6 +56,12 @@ import org.slf4j.LoggerFactory;
* instance of solr.
*/
public class CoreContainerProvider implements ServletContextListener {
+ public static final String SOLR_PROPERTIES = "solr.properties";
+ public static final String SOLR_SOLR_HOME = "solr.solr.home";
+ public static final String SOLR_INSTALL_DIR = "solr.install.dir";
+ public static final String SOLR_LOG_MUTECONSOLE = "solr.log.muteconsole";
+ public static final String SOLR_LOG_LEVEL = "solr.log.level";
+
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
private CoreContainer cores;
@@ -90,10 +91,8 @@ public class CoreContainerProvider implements ServletContextListener {
// could remove ourselves from ctx but why bother
}
- /**
- * @see SolrDispatchFilter#getCores()
- */
- CoreContainer getCoreContainer() throws UnavailableException {
+ /** The CoreContainer. Never null. */
+ public CoreContainer getCoreContainer() throws UnavailableException {
checkReady();
return cores;
}
@@ -145,7 +144,7 @@ public class CoreContainerProvider implements ServletContextListener {
// caller
Properties extraProperties =
SolrXmlConfig.wrapAndSetZkHostFromSysPropIfNeeded(
- (Properties) servletContext.getAttribute(PROPERTIES_ATTRIBUTE));
+ (Properties) servletContext.getAttribute(SOLR_PROPERTIES));
StartupLoggingUtils.checkLogDir();
if (log.isInfoEnabled()) {
@@ -209,8 +208,7 @@ public class CoreContainerProvider implements ServletContextListener {
getSolrPort());
}
if (log.isInfoEnabled()) {
- log.info(
- "\\__ \\/ _ \\ | '_| Install dir: {}", System.getProperty(SOLR_INSTALL_DIR_ATTRIBUTE));
+ log.info("\\__ \\/ _ \\ | '_| Install dir: {}", System.getProperty(SOLR_INSTALL_DIR));
}
if (log.isInfoEnabled()) {
log.info("|___/\\___/_|_| Start time: {}", Instant.now());
@@ -295,8 +293,8 @@ public class CoreContainerProvider implements ServletContextListener {
private static Path computeSolrHome(ServletContext servletContext) {
// start with explicit check of servlet config...
- String source = "servlet config: " + SOLRHOME_ATTRIBUTE;
- String home = (String) servletContext.getAttribute(SOLRHOME_ATTRIBUTE);
+ String source = "servlet config: " + SOLR_SOLR_HOME;
+ String home = (String) servletContext.getAttribute(SOLR_SOLR_HOME);
if (null == home) {
final String lookup = "java:comp/env/solr/home";
diff --git a/solr/core/src/java/org/apache/solr/servlet/HttpSolrCall.java b/solr/core/src/java/org/apache/solr/servlet/HttpSolrCall.java
index ae042c7343b..cfcc58907e8 100644
--- a/solr/core/src/java/org/apache/solr/servlet/HttpSolrCall.java
+++ b/solr/core/src/java/org/apache/solr/servlet/HttpSolrCall.java
@@ -21,13 +21,12 @@ import static org.apache.solr.common.cloud.ZkStateReader.CORE_NAME_PROP;
import static org.apache.solr.common.cloud.ZkStateReader.NODE_NAME_PROP;
import static org.apache.solr.security.AuditEvent.EventType.COMPLETED;
import static org.apache.solr.security.AuditEvent.EventType.ERROR;
-import static org.apache.solr.servlet.SolrDispatchFilter.Action.ADMIN;
-import static org.apache.solr.servlet.SolrDispatchFilter.Action.FORWARD;
-import static org.apache.solr.servlet.SolrDispatchFilter.Action.PASSTHROUGH;
-import static org.apache.solr.servlet.SolrDispatchFilter.Action.PROCESS;
-import static org.apache.solr.servlet.SolrDispatchFilter.Action.REMOTEPROXY;
-import static org.apache.solr.servlet.SolrDispatchFilter.Action.RETRY;
-import static org.apache.solr.servlet.SolrDispatchFilter.Action.RETURN;
+import static org.apache.solr.servlet.HttpSolrCall.Action.ADMIN;
+import static org.apache.solr.servlet.HttpSolrCall.Action.FORWARD;
+import static org.apache.solr.servlet.HttpSolrCall.Action.PROCESS;
+import static org.apache.solr.servlet.HttpSolrCall.Action.REMOTEPROXY;
+import static org.apache.solr.servlet.HttpSolrCall.Action.RETRY;
+import static org.apache.solr.servlet.HttpSolrCall.Action.RETURN;
import io.opentelemetry.api.trace.Span;
import jakarta.servlet.http.HttpServletRequest;
@@ -99,7 +98,6 @@ import org.apache.solr.security.AuthorizationContext.RequestType;
import org.apache.solr.security.AuthorizationUtils;
import org.apache.solr.security.HttpServletAuthorizationContext;
import org.apache.solr.security.PublicKeyHandler;
-import org.apache.solr.servlet.SolrDispatchFilter.Action;
import org.apache.solr.servlet.cache.HttpCacheHeaderUtil;
import org.apache.solr.servlet.cache.Method;
import org.apache.solr.update.processor.DistributingUpdateProcessorFactory;
@@ -117,7 +115,6 @@ public class HttpSolrCall {
public static final String INTERNAL_REQUEST_COUNT = "_forwardedCount";
- protected final SolrDispatchFilter solrDispatchFilter;
protected final CoreContainer cores;
protected final HttpServletRequest req;
protected final HttpServletResponse response;
@@ -143,13 +140,28 @@ public class HttpSolrCall {
protected RequestType requestType;
+ /** Enum to define action that needs to be processed. */
+ public enum Action {
+ /** Forwards the request via {@link jakarta.servlet.RequestDispatcher}. */
+ FORWARD,
+ /**
+ * Returns the control, and no further specific processing is needed. This is generally when an
+ * error is set and returned.
+ */
+ RETURN,
+ /** Retry the request. Currently used when a core isn't found, we refresh state, and retry. */
+ RETRY,
+ ADMIN,
+ REMOTEPROXY,
+ PROCESS,
+ ADMIN_OR_REMOTEPROXY
+ }
+
public HttpSolrCall(
- SolrDispatchFilter solrDispatchFilter,
CoreContainer cores,
HttpServletRequest request,
HttpServletResponse response,
boolean retry) {
- this.solrDispatchFilter = solrDispatchFilter;
this.cores = cores;
this.req = request;
this.response = response;
@@ -304,9 +316,11 @@ public class HttpSolrCall {
return; // we are done with a valid handler
}
}
- log.debug("no handler or core retrieved for {}, follow through...", path);
- action = PASSTHROUGH;
+ String msg = "no handler, collection, or core for " + path;
+ sendError(404, msg);
+ log.info(msg); // not "error" since Solr isn't necessarily at fault
+ action = RETURN;
}
/**
@@ -422,12 +436,6 @@ public class HttpSolrCall {
return RETURN;
}
- if (solrDispatchFilter.abortErrorMessage != null) {
- sendError(500, solrDispatchFilter.abortErrorMessage);
- cores.audit(() -> new AuditEvent(ERROR, getReq()), ERROR);
- return RETURN;
- }
-
try {
init();
diff --git a/solr/core/src/java/org/apache/solr/servlet/LoadAdminUiServlet.java b/solr/core/src/java/org/apache/solr/servlet/LoadAdminUiServlet.java
index 1c381f06f8c..316e0eb59ca 100644
--- a/solr/core/src/java/org/apache/solr/servlet/LoadAdminUiServlet.java
+++ b/solr/core/src/java/org/apache/solr/servlet/LoadAdminUiServlet.java
@@ -16,9 +16,8 @@
*/
package org.apache.solr.servlet;
-import static org.apache.solr.servlet.RequiredSolrRequestFilter.CORE_CONTAINER_REQUEST_ATTRIBUTE;
-
import com.google.common.net.HttpHeaders;
+import jakarta.servlet.UnavailableException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
@@ -31,7 +30,6 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.apache.commons.io.output.CloseShieldOutputStream;
-import org.apache.solr.core.CoreContainer;
import org.apache.solr.core.SolrCore;
/**
@@ -62,11 +60,11 @@ public final class LoadAdminUiServlet extends HttpServlet {
response.addHeader(
"X-Frame-Options", "DENY"); // security: SOLR-7966 - avoid clickjacking for admin interface
- // This attribute is set by the SolrDispatchFilter
String admin = request.getRequestURI().substring(request.getContextPath().length());
- CoreContainer cores = (CoreContainer) request.getAttribute(CORE_CONTAINER_REQUEST_ATTRIBUTE);
try (InputStream in = getServletContext().getResourceAsStream(admin)) {
- if (in != null && cores != null) {
+ CoreContainerProvider.serviceForContext(getServletConfig().getServletContext())
+ .getCoreContainer();
+ if (in != null) {
response.setCharacterEncoding("UTF-8");
response.setContentType("text/html");
String connectSrc = generateCspConnectSrc();
@@ -88,6 +86,8 @@ public final class LoadAdminUiServlet extends HttpServlet {
} else {
response.sendError(404);
}
+ } catch (UnavailableException e) { // from CoreContainer being unavailable
+ response.sendError(404);
}
}
diff --git a/solr/core/src/java/org/apache/solr/servlet/RequiredSolrRequestFilter.java b/solr/core/src/java/org/apache/solr/servlet/RequiredSolrRequestFilter.java
index 7cdddfb2c5b..01b135d5f8c 100644
--- a/solr/core/src/java/org/apache/solr/servlet/RequiredSolrRequestFilter.java
+++ b/solr/core/src/java/org/apache/solr/servlet/RequiredSolrRequestFilter.java
@@ -73,7 +73,6 @@ public class RequiredSolrRequestFilter extends CoreContainerAwareHttpFilter {
req.setAttribute(SolrRequestParsers.REQUEST_TIMER_SERVLET_ATTRIBUTE, new RTimerTree());
// put the core container in request attribute
- // This is required for the LoadAdminUiServlet class. Removing it will cause 404
req.setAttribute(CORE_CONTAINER_REQUEST_ATTRIBUTE, getCores());
// we want to prevent any attempts to close our request or response prematurely
diff --git a/solr/core/src/java/org/apache/solr/servlet/SolrRequestParsers.java b/solr/core/src/java/org/apache/solr/servlet/SolrRequestParsers.java
index e835d844db8..82ef9ec2341 100644
--- a/solr/core/src/java/org/apache/solr/servlet/SolrRequestParsers.java
+++ b/solr/core/src/java/org/apache/solr/servlet/SolrRequestParsers.java
@@ -466,10 +466,9 @@ public class SolrRequestParsers {
@Override
public InputStream getStream() throws IOException {
- // we explicitly protect this servlet stream from being closed
- // so that it does not trip our test assert in our close shield
- // in SolrDispatchFilter - we must allow closes from getStream
- // due to the other impls of ContentStream
+ // We explicitly protect this servlet stream from being closed so it does not trip our test
+ // assert in ServletUtils.closeShield; we must allow closes from getStream due to other
+ // ContentStream impls.
return new CloseShieldInputStream(inputStream);
}
}
@@ -668,10 +667,11 @@ public class SolrRequestParsers {
public static SolrException getParameterIncompatibilityException() {
return new SolrException(
ErrorCode.SERVER_ERROR,
- "Solr requires that request parameters sent using application/x-www-form-urlencoded "
- + "content-type can be read through the request input stream. Unfortunately, the "
- + "stream was empty / not available. This may be caused by another servlet filter calling "
- + "ServletRequest.getParameter*() before SolrDispatchFilter, please remove it.");
+ """
+ Solr requires that request parameters sent using application/x-www-form-urlencoded \
+ content-type can be read through the request input stream. Unfortunately, the \
+ stream was empty / not available. This may be caused by a servlet filter calling \
+ ServletRequest.getParameter*(). Please remove it.""");
}
public boolean isFormData(HttpServletRequest req) {
diff --git a/solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java b/solr/core/src/java/org/apache/solr/servlet/SolrServlet.java
similarity index 53%
rename from solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java
rename to solr/core/src/java/org/apache/solr/servlet/SolrServlet.java
index 300c54ac562..3722ea2a26a 100644
--- a/solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java
+++ b/solr/core/src/java/org/apache/solr/servlet/SolrServlet.java
@@ -18,10 +18,10 @@ package org.apache.solr.servlet;
import static org.apache.solr.util.tracing.TraceUtils.getSpan;
-import jakarta.servlet.FilterChain;
-import jakarta.servlet.FilterConfig;
+import jakarta.servlet.ServletConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.UnavailableException;
+import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
@@ -37,80 +37,58 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
- * This filter instantiates an instance of {@link HttpSolrCall}, invokes it and then based on the
- * action returned handles retry/forward/passthrough dispatch decisions.
- *
- * @since solr 1.2
+ * Solr's interface to Jetty, and passes requests to {@link HttpSolrCall}. Formerly known as {@code
+ * SolrDispatchFilter}.
*/
-public class SolrDispatchFilter extends CoreContainerAwareHttpFilter {
+public class SolrServlet extends HttpServlet {
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
- protected String abortErrorMessage = null;
-
private HttpSolrCallFactory solrCallFactory;
-
- public final boolean isV2Enabled = V2ApiUtils.isEnabled();
-
- /**
- * Enum to define action that needs to be processed. PASSTHROUGH: Pass through to another filter
- * via webapp. FORWARD: Forward rewritten URI (without path prefix and core/collection name) to
- * another filter in the chain RETURN: Returns the control, and no further specific processing is
- * needed. This is generally when an error is set and returned. RETRY:Retry the request. In cases
- * when a core isn't found to work with, this is set.
- */
- public enum Action {
- PASSTHROUGH,
- FORWARD,
- RETURN,
- RETRY,
- ADMIN,
- REMOTEPROXY,
- PROCESS,
- ADMIN_OR_REMOTEPROXY
- }
-
- public SolrDispatchFilter() {}
-
- public static final String PROPERTIES_ATTRIBUTE = "solr.properties";
-
- public static final String SOLRHOME_ATTRIBUTE = "solr.solr.home";
-
- public static final String SOLR_INSTALL_DIR_ATTRIBUTE = "solr.install.dir";
-
- public static final String SOLR_CONFIGSET_DEFAULT_CONFDIR_ATTRIBUTE =
- "solr.configset.default.confdir";
-
- public static final String SOLR_LOG_MUTECONSOLE = "solr.log.muteconsole";
-
- public static final String SOLR_LOG_LEVEL = "solr.log.level";
+ private CoreContainerProvider containerProvider;
@Override
- public void init(FilterConfig config) throws ServletException {
+ public void init(ServletConfig config) throws ServletException {
try {
super.init(config);
+ containerProvider = CoreContainerProvider.serviceForContext(config.getServletContext());
boolean isCoordinator =
NodeRoles.MODE_ON.equals(getCores().nodeRoles.getRoleMode(NodeRoles.Role.COORDINATOR));
solrCallFactory =
isCoordinator ? new CoordinatorHttpSolrCall.Factory() : new HttpSolrCallFactory() {};
if (log.isTraceEnabled()) {
- log.trace("SolrDispatchFilter.init(): {}", this.getClass().getClassLoader());
+ log.trace("init(): {}", this.getClass().getClassLoader());
}
} catch (Throwable t) {
- // catch this so our filter still works
- log.error("Could not start Dispatch Filter.", t);
+ // catch this so our servlet still works
+ log.error("Could not start Servlet.", t);
if (t instanceof Error) {
throw (Error) t;
}
} finally {
- log.trace("SolrDispatchFilter.init() done");
+ log.trace("init() done");
}
}
+ /**
+ * The CoreContainer. It's guaranteed to be constructed before this servlet is initialized, but
+ * could have been shut down. Never null.
+ */
+ public CoreContainer getCores() throws UnavailableException {
+ return containerProvider.getCoreContainer();
+ }
+
@Override
- public void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
+ protected void service(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
- // internal version of doFilter that tracks if we are in a retry
- dispatch(chain, request, response, false);
+ // Handle root path specially - forward to index.html to trigger welcome-file mechanism
+ String path = ServletUtils.getPathAfterContext(request);
+ if (path.isEmpty() || path.equals("/")) {
+ request.getRequestDispatcher("/index.html").forward(request, response);
+ return;
+ }
+
+ // internal version that tracks if we are in a retry
+ dispatch(request, response, false);
}
/*
@@ -119,15 +97,13 @@ public class SolrDispatchFilter extends CoreContainerAwareHttpFilter {
For over a decade this class did anything and everything
In late 2021 SOLR-15590 moved container startup to CoreContainerProvider
In late 2025 SOLR-18040 moved request wrappers to independent ServletFilters
- such as PathExclusionFilter see web.xml for a full, up-to-date list
- This class is only handling dispatch, please think twice before adding anything else to it.
- */
+ This class is now basically only passing the request to HttpSolrCall.
+ Please think twice before adding anything else to it.
+ */
- private void dispatch(
- FilterChain chain, HttpServletRequest request, HttpServletResponse response, boolean retry)
+ private void dispatch(HttpServletRequest request, HttpServletResponse response, boolean retry)
throws IOException, ServletException {
- var span = getSpan(request);
HttpSolrCall call = getHttpSolrCall(request, response, retry);
// this flag LOOKS like it should be in RequiredSolrRequestFilter, but
@@ -136,27 +112,17 @@ public class SolrDispatchFilter extends CoreContainerAwareHttpFilter {
// BUG? Important timing?
ExecutorUtil.setServerThreadFlag(Boolean.TRUE);
try {
- Action result = call.call();
- switch (result) {
- case PASSTHROUGH:
- span.addEvent("SolrDispatchFilter PASSTHROUGH");
- chain.doFilter(request, response);
- break;
- case RETRY:
- span.addEvent("SolrDispatchFilter RETRY");
+ switch (call.call()) {
+ case RETRY -> {
+ getSpan(request).addEvent("Action RETRY");
// RECURSION
- dispatch(chain, request, response, true);
- break;
- case FORWARD:
- span.addEvent("SolrDispatchFilter FORWARD");
+ dispatch(request, response, true);
+ }
+ case FORWARD -> {
+ getSpan(request).addEvent("Action FORWARD");
request.getRequestDispatcher(call.getPath()).forward(request, response);
- break;
- case ADMIN:
- case PROCESS:
- case REMOTEPROXY:
- case ADMIN_OR_REMOTEPROXY:
- case RETURN:
- break;
+ }
+ default -> {}
}
} finally {
call.destroy();
@@ -177,22 +143,23 @@ public class SolrDispatchFilter extends CoreContainerAwareHttpFilter {
} catch (UnavailableException e) {
throw new SolrException(ErrorCode.SERVER_ERROR, "Core Container Unavailable");
}
- return solrCallFactory.createInstance(this, path, cores, request, response, retry);
+ return solrCallFactory.createInstance(path, cores, request, response, retry);
}
- /** internal API */
+ /**
+ * @lucene.internal
+ */
public interface HttpSolrCallFactory {
default HttpSolrCall createInstance(
- SolrDispatchFilter filter,
String path,
CoreContainer cores,
HttpServletRequest request,
HttpServletResponse response,
boolean retry) {
- if (filter.isV2Enabled && (path.startsWith("/____v2/") || path.equals("/____v2"))) {
- return new V2HttpCall(filter, cores, request, response, retry);
+ if (V2ApiUtils.isEnabled() && (path.startsWith("/____v2/") || path.equals("/____v2"))) {
+ return new V2HttpCall(cores, request, response, retry);
} else {
- return new HttpSolrCall(filter, cores, request, response, retry);
+ return new HttpSolrCall(cores, request, response, retry);
}
}
}
diff --git a/solr/core/src/test/org/apache/solr/cloud/DeleteReplicaTest.java b/solr/core/src/test/org/apache/solr/cloud/DeleteReplicaTest.java
index a328fad5589..2932553c6d2 100644
--- a/solr/core/src/test/org/apache/solr/cloud/DeleteReplicaTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/DeleteReplicaTest.java
@@ -486,7 +486,7 @@ public class DeleteReplicaTest extends SolrCloudTestCase {
/**
* see SOLR-16848 working around a timing issue where the callback will be faster than the
- * dispatchFilter's init
+ * SolrServlet's init
*/
private void waitForJettyInit(JettySolrRunner replica1Jetty, String replica1JettyNodeName)
throws InterruptedException {
diff --git a/solr/core/src/test/org/apache/solr/cloud/TestCloudSearcherWarming.java b/solr/core/src/test/org/apache/solr/cloud/TestCloudSearcherWarming.java
index bc44ef1ba00..3e2089af36c 100644
--- a/solr/core/src/test/org/apache/solr/cloud/TestCloudSearcherWarming.java
+++ b/solr/core/src/test/org/apache/solr/cloud/TestCloudSearcherWarming.java
@@ -35,7 +35,6 @@ import org.apache.solr.core.SolrCore;
import org.apache.solr.core.SolrEventListener;
import org.apache.solr.embedded.JettySolrRunner;
import org.apache.solr.search.SolrIndexSearcher;
-import org.apache.solr.servlet.SolrDispatchFilter;
import org.apache.solr.util.LogLevel;
import org.apache.solr.util.RefCounted;
import org.apache.solr.util.TestInjection;
@@ -294,8 +293,7 @@ public class TestCloudSearcherWarming extends SolrCloudTestCase {
log.info("Checking node: {}", jettySolrRunner.getNodeName());
}
if (jettySolrRunner.getNodeName().equals(replica.getNodeName())) {
- SolrDispatchFilter solrDispatchFilter = jettySolrRunner.getSolrDispatchFilter();
- try (SolrCore core = solrDispatchFilter.getCores().getCore(coreName)) {
+ try (SolrCore core = jettySolrRunner.getCoreContainer().getCore(coreName)) {
assertFalse(
"useColdSearcher is enabled! It should not be enabled for this test!",
core.getSolrConfig().useColdSearcher);
diff --git a/solr/core/src/test/org/apache/solr/cloud/TestConfigSetsAPI.java b/solr/core/src/test/org/apache/solr/cloud/TestConfigSetsAPI.java
index 5dc13d0ca3a..6bc03e5ef31 100644
--- a/solr/core/src/test/org/apache/solr/cloud/TestConfigSetsAPI.java
+++ b/solr/core/src/test/org/apache/solr/cloud/TestConfigSetsAPI.java
@@ -100,7 +100,6 @@ import org.apache.solr.security.AuthorizationContext;
import org.apache.solr.security.AuthorizationPlugin;
import org.apache.solr.security.AuthorizationResponse;
import org.apache.solr.security.BasicAuthPlugin;
-import org.apache.solr.servlet.SolrDispatchFilter;
import org.apache.solr.util.ExternalPaths;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
@@ -1650,12 +1649,12 @@ public class TestConfigSetsAPI extends SolrCloudTestCase {
* the real directory which matches what {@link ZkController} finds and uses to bootstrap ZK in
* cloud based tests.
*
- * <p>This assumes the {@link SolrDispatchFilter#SOLR_CONFIGSET_DEFAULT_CONFDIR_ATTRIBUTE} system
- * property has not been externally set in the environment where this test is being run -- which
- * should <b>never</b> be the case, since it would prevent the test-framework from using {@link
+ * <p>This assumes the {@link ConfigSetService#SOLR_CONFIGSET_DEFAULT_CONFDIR} system property has
+ * not been externally set in the environment where this test is being run -- which should
+ * <b>never</b> be the case, since it would prevent the test-framework from using {@link
* ExternalPaths#DEFAULT_CONFIGSET}
*
- * @see SolrDispatchFilter#SOLR_CONFIGSET_DEFAULT_CONFDIR_ATTRIBUTE
+ * @see ConfigSetService#SOLR_CONFIGSET_DEFAULT_CONFDIR
* @see #beforeSolrTestCase
* @see ConfigSetService#getDefaultConfigDirPath
*/
diff --git a/solr/core/src/test/org/apache/solr/core/TestCoreContainer.java b/solr/core/src/test/org/apache/solr/core/TestCoreContainer.java
index 2ded0c3ff4e..221da96aee2 100644
--- a/solr/core/src/test/org/apache/solr/core/TestCoreContainer.java
+++ b/solr/core/src/test/org/apache/solr/core/TestCoreContainer.java
@@ -16,7 +16,7 @@
*/
package org.apache.solr.core;
-import static org.apache.solr.servlet.SolrDispatchFilter.SOLR_INSTALL_DIR_ATTRIBUTE;
+import static org.apache.solr.servlet.CoreContainerProvider.SOLR_INSTALL_DIR;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.CoreMatchers.nullValue;
@@ -43,7 +43,7 @@ import org.apache.solr.handler.admin.CollectionsHandler;
import org.apache.solr.handler.admin.ConfigSetsHandler;
import org.apache.solr.handler.admin.CoreAdminHandler;
import org.apache.solr.handler.admin.InfoHandler;
-import org.apache.solr.servlet.SolrDispatchFilter;
+import org.apache.solr.servlet.CoreContainerProvider;
import org.apache.solr.util.ModuleUtils;
import org.junit.Assume;
import org.junit.BeforeClass;
@@ -429,8 +429,7 @@ public class TestCoreContainer extends SolrTestCaseJ4 {
jar1.closeEntry();
}
- System.setProperty(
- SolrDispatchFilter.SOLR_INSTALL_DIR_ATTRIBUTE, tmpRoot.toAbsolutePath().toString());
+ System.setProperty(CoreContainerProvider.SOLR_INSTALL_DIR, tmpRoot.toAbsolutePath().toString());
final CoreContainer cc1 = init(tmpRoot, "<solr></solr>");
try {
assertThrows(
@@ -466,7 +465,7 @@ public class TestCoreContainer extends SolrTestCaseJ4 {
jar1.closeEntry();
}
- System.setProperty(SOLR_INSTALL_DIR_ATTRIBUTE, installDirPath.toString());
+ System.setProperty(SOLR_INSTALL_DIR, installDirPath.toString());
final CoreContainer cores = init(CONFIGSETS_SOLR_XML);
try {
diff --git a/solr/core/src/test/org/apache/solr/servlet/HttpSolrCallCloudTest.java b/solr/core/src/test/org/apache/solr/servlet/HttpSolrCallCloudTest.java
index c3db96528dd..3ee4555797a 100644
--- a/solr/core/src/test/org/apache/solr/servlet/HttpSolrCallCloudTest.java
+++ b/solr/core/src/test/org/apache/solr/servlet/HttpSolrCallCloudTest.java
@@ -33,6 +33,7 @@ import org.apache.solr.cloud.AbstractFullDistribZkTestBase;
import org.apache.solr.cloud.SolrCloudTestCase;
import org.apache.solr.common.util.SuppressForbidden;
import org.apache.solr.embedded.JettySolrRunner;
+import org.apache.solr.util.LogListener;
import org.junit.BeforeClass;
import org.junit.Test;
import org.mockito.Mockito;
@@ -75,16 +76,37 @@ public class HttpSolrCallCloudTest extends SolrCloudTestCase {
assertEquals(400, connection.getResponseCode());
}
+ @Test
+ public void test404() throws Exception {
+ // once upon a time, Jetty (not Solr) handled 404. Now Solr does.
+ try (LogListener log = LogListener.info(HttpSolrCall.class)) {
+ var baseUrl = cluster.getJettySolrRunner(0).getBaseUrl();
+ final var collection = "nonExistentColl";
+ final var handler = random().nextBoolean() ? "/select" : "/update"; // doesn't matter
+ final var queryKeyVal = "q=myQuery";
+ var request =
+ new URI(baseUrl.toString() + "/" + collection + handler + "?" + queryKeyVal).toURL();
+ var connection = (HttpURLConnection) request.openConnection();
+ assertEquals(404, connection.getResponseCode());
+ assertEquals(1, log.getCount());
+ final var msg = log.pollMessage();
+ // super basic test that merely shows Solr logs contain some basic info
+ // assertTrue(msg, msg.contains("status"));
+ // assertTrue(msg, msg.contains("404"));
+ assertTrue(msg, msg.contains(collection));
+ assertTrue(msg, msg.contains(handler));
+ // assertTrue(msg, msg.contains(queryKeyVal));
+ }
+ }
+
private void assertCoreChosen(int numCores, HttpServletRequest testRequest)
throws UnavailableException {
JettySolrRunner jettySolrRunner = cluster.getJettySolrRunner(0);
Set<String> coreNames = new HashSet<>();
- SolrDispatchFilter dispatchFilter = jettySolrRunner.getSolrDispatchFilter();
for (int i = 0; i < NUM_SHARD * REPLICA_FACTOR * 20; i++) {
if (coreNames.size() == numCores) return;
HttpSolrCall httpSolrCall =
- new HttpSolrCall(
- dispatchFilter, dispatchFilter.getCores(), testRequest, newResponse(), false);
+ new HttpSolrCall(jettySolrRunner.getCoreContainer(), testRequest, newResponse(), false);
try {
httpSolrCall.init();
} catch (Exception e) {
diff --git a/solr/core/src/test/org/apache/solr/servlet/LoadAdminUiServletTest.java b/solr/core/src/test/org/apache/solr/servlet/LoadAdminUiServletTest.java
index f22292e6474..dccd954e1be 100644
--- a/solr/core/src/test/org/apache/solr/servlet/LoadAdminUiServletTest.java
+++ b/solr/core/src/test/org/apache/solr/servlet/LoadAdminUiServletTest.java
@@ -52,6 +52,7 @@ public class LoadAdminUiServletTest extends SolrTestCaseJ4 {
@Mock private ServletConfig servletConfig;
@Mock private ServletContext mockServletContext;
@Mock private ServletOutputStream mockOutputStream;
+ @Mock private CoreContainerProvider coreContainerProvider;
private static final Set<String> CSP_URLS =
Set.of(
@@ -72,6 +73,9 @@ public class LoadAdminUiServletTest extends SolrTestCaseJ4 {
InputStream mockInputStream =
new ByteArrayInputStream("mock content".getBytes(StandardCharsets.UTF_8));
when(mockServletContext.getResourceAsStream(anyString())).thenReturn(mockInputStream);
+ when(mockServletContext.getAttribute(CoreContainerProvider.class.getName()))
+ .thenReturn(coreContainerProvider);
+ when(coreContainerProvider.getCoreContainer()).thenReturn(coreContainer);
}
@BeforeClass
diff --git a/solr/server/solr/configsets/_default/conf/solrconfig.xml b/solr/server/solr/configsets/_default/conf/solrconfig.xml
index 25ca1e5c3c6..236304a74cb 100644
--- a/solr/server/solr/configsets/_default/conf/solrconfig.xml
+++ b/solr/server/solr/configsets/_default/conf/solrconfig.xml
@@ -505,12 +505,6 @@
</query>
- <!-- Request Dispatcher
-
- This section contains instructions for how the SolrDispatchFilter
- should behave when processing requests for this SolrCore.
-
- -->
<requestDispatcher>
<!-- Request Parsing
@@ -895,7 +889,7 @@
<str name="fieldType">pdoubles</str>
</lst>
</updateProcessor>
-
+
<!-- The update.autoCreateFields property can be turned to false to disable schemaless mode -->
<updateRequestProcessorChain name="add-unknown-fields-to-the-schema" default="${update.autoCreateFields:true}"
diff --git a/solr/server/solr/configsets/sample_techproducts_configs/conf/solrconfig.xml b/solr/server/solr/configsets/sample_techproducts_configs/conf/solrconfig.xml
index 739a1a7e462..a6214acf2ee 100644
--- a/solr/server/solr/configsets/sample_techproducts_configs/conf/solrconfig.xml
+++ b/solr/server/solr/configsets/sample_techproducts_configs/conf/solrconfig.xml
@@ -516,12 +516,6 @@
</query>
- <!-- Request Dispatcher
-
- This section contains instructions for how the SolrDispatchFilter
- should behave when processing requests for this SolrCore.
-
- -->
<requestDispatcher>
<!-- Request Parsing
diff --git a/solr/test-framework/src/java/org/apache/solr/SolrTestCase.java b/solr/test-framework/src/java/org/apache/solr/SolrTestCase.java
index a29e3d0da43..d7a0475fc7b 100644
--- a/solr/test-framework/src/java/org/apache/solr/SolrTestCase.java
+++ b/solr/test-framework/src/java/org/apache/solr/SolrTestCase.java
@@ -36,7 +36,7 @@ import org.apache.lucene.tests.util.QuickPatchThreadsFilter;
import org.apache.lucene.tests.util.VerifyTestClassNamingConvention;
import org.apache.solr.common.util.EnvUtils;
import org.apache.solr.common.util.ObjectReleaseTracker;
-import org.apache.solr.servlet.SolrDispatchFilter;
+import org.apache.solr.core.ConfigSetService;
import org.apache.solr.util.ExternalPaths;
import org.apache.solr.util.LogLevelTestRule;
import org.apache.solr.util.RevertDefaultThreadHandlerRule;
@@ -116,16 +116,16 @@ public class SolrTestCase extends LuceneTestCase {
*
* <p>Logs INFO/WARNing messages as appropriate based on these 2 conditions.
*
- * @see SolrDispatchFilter#SOLR_CONFIGSET_DEFAULT_CONFDIR_ATTRIBUTE
+ * @see ConfigSetService#SOLR_CONFIGSET_DEFAULT_CONFDIR
*/
@BeforeClass
public static void beforeSolrTestCase() {
final String existingValue =
- EnvUtils.getProperty(SolrDispatchFilter.SOLR_CONFIGSET_DEFAULT_CONFDIR_ATTRIBUTE);
+ EnvUtils.getProperty(ConfigSetService.SOLR_CONFIGSET_DEFAULT_CONFDIR);
if (null != existingValue) {
log.info(
"Test env includes configset dir system property '{}'='{}'",
- SolrDispatchFilter.SOLR_CONFIGSET_DEFAULT_CONFDIR_ATTRIBUTE,
+ ConfigSetService.SOLR_CONFIGSET_DEFAULT_CONFDIR,
existingValue);
return;
}
@@ -133,18 +133,18 @@ public class SolrTestCase extends LuceneTestCase {
if (Files.isReadable(extPath /* implies exists() */) && Files.isDirectory(extPath)) {
log.info(
"Setting '{}' system property to test-framework derived value of '{}'",
- SolrDispatchFilter.SOLR_CONFIGSET_DEFAULT_CONFDIR_ATTRIBUTE,
+ ConfigSetService.SOLR_CONFIGSET_DEFAULT_CONFDIR,
ExternalPaths.DEFAULT_CONFIGSET);
assert null == existingValue;
System.setProperty(
- SolrDispatchFilter.SOLR_CONFIGSET_DEFAULT_CONFDIR_ATTRIBUTE,
+ ConfigSetService.SOLR_CONFIGSET_DEFAULT_CONFDIR,
ExternalPaths.DEFAULT_CONFIGSET.toString());
} else {
log.warn(
"System property '{}' is not already set, but test-framework derived value ('{}') either "
+ "does not exist or is not a readable directory, you may need to set the property yourself "
+ "for tests to run properly",
- SolrDispatchFilter.SOLR_CONFIGSET_DEFAULT_CONFDIR_ATTRIBUTE,
+ ConfigSetService.SOLR_CONFIGSET_DEFAULT_CONFDIR,
ExternalPaths.DEFAULT_CONFIGSET);
}
diff --git a/solr/test-framework/src/java/org/apache/solr/client/solrj/apache/HttpSolrClient.java b/solr/test-framework/src/java/org/apache/solr/client/solrj/apache/HttpSolrClient.java
index 3e184f5ef3f..e2a849cd4cf 100644
--- a/solr/test-framework/src/java/org/apache/solr/client/solrj/apache/HttpSolrClient.java
+++ b/solr/test-framework/src/java/org/apache/solr/client/solrj/apache/HttpSolrClient.java
@@ -703,7 +703,7 @@ public class HttpSolrClient extends SolrClient {
}
// When raising an error using HTTP sendError, mime types can be mismatched. This is specifically
- // true when SolrDispatchFilter uses the sendError mechanism since the expected MIME type of
+ // true when SolrServlet uses the sendError mechanism since the expected MIME type of
// response is not HTML but HTTP sendError generates an HTML output, which can lead to mismatch
private boolean isUnmatchedErrorCode(String mimeType, int httpStatus) {
if (mimeType == null) {
diff --git a/solr/test-framework/src/java/org/apache/solr/embedded/JettySolrRunner.java b/solr/test-framework/src/java/org/apache/solr/embedded/JettySolrRunner.java
index 05405b37d3c..43261fa7fd9 100644
--- a/solr/test-framework/src/java/org/apache/solr/embedded/JettySolrRunner.java
+++ b/solr/test-framework/src/java/org/apache/solr/embedded/JettySolrRunner.java
@@ -66,7 +66,7 @@ import org.apache.solr.servlet.CoreContainerProvider;
import org.apache.solr.servlet.PathExclusionFilter;
import org.apache.solr.servlet.RateLimitFilter;
import org.apache.solr.servlet.RequiredSolrRequestFilter;
-import org.apache.solr.servlet.SolrDispatchFilter;
+import org.apache.solr.servlet.SolrServlet;
import org.apache.solr.servlet.TracingFilter;
import org.apache.solr.util.SocketProxy;
import org.apache.solr.util.TimeOut;
@@ -120,7 +120,7 @@ public class JettySolrRunner {
volatile FilterHolder requiredFilter;
volatile FilterHolder rateLimitFilter;
volatile FilterHolder authFilter;
- volatile FilterHolder dispatchFilter;
+ volatile ServletHolder solrServlet;
private FilterHolder tracingFilter;
private int jettyPort = -1;
@@ -387,9 +387,8 @@ public class JettySolrRunner {
nodeProperties.setProperty("hostPort", Integer.toString(port));
root.getServletContext()
- .setAttribute(SolrDispatchFilter.PROPERTIES_ATTRIBUTE, nodeProperties);
- root.getServletContext()
- .setAttribute(SolrDispatchFilter.SOLRHOME_ATTRIBUTE, solrHome);
+ .setAttribute(CoreContainerProvider.SOLR_PROPERTIES, nodeProperties);
+ root.getServletContext().setAttribute(CoreContainerProvider.SOLR_SOLR_HOME, solrHome);
SSLConfigurationsFactory.current().init(); // normally happens in jetty-ssl.xml
@@ -434,17 +433,17 @@ public class JettySolrRunner {
authFilter = root.getServletHandler().newFilterHolder(Source.EMBEDDED);
authFilter.setHeldClass(AuthenticationFilter.class);
- // This is our main workhorse
- dispatchFilter = root.getServletHandler().newFilterHolder(Source.EMBEDDED);
- dispatchFilter.setHeldClass(SolrDispatchFilter.class);
-
- // Map dispatchFilter in same path as in web.xml
+ // Map filters in same path as in web.xml
root.addFilter(pathExcludeFilter, "/*", EnumSet.of(DispatcherType.REQUEST));
root.addFilter(requiredFilter, "/*", EnumSet.of(DispatcherType.REQUEST));
root.addFilter(rateLimitFilter, "/*", EnumSet.of(DispatcherType.REQUEST));
root.addFilter(tracingFilter, "/*", EnumSet.of(DispatcherType.REQUEST));
root.addFilter(authFilter, "/*", EnumSet.of(DispatcherType.REQUEST));
- root.addFilter(dispatchFilter, "/*", EnumSet.of(DispatcherType.REQUEST));
+
+ // This is our main workhorse - now a servlet instead of filter
+ solrServlet = root.getServletHandler().newServletHolder(Source.EMBEDDED);
+ solrServlet.setHeldClass(SolrServlet.class);
+ root.addServlet(solrServlet, "/*");
// Default servlet as a fall-through
ServletHolder defaultHolder = root.getServletHandler().newServletHolder(Source.EMBEDDED);
@@ -494,14 +493,7 @@ public class JettySolrRunner {
}
/**
- * @return the {@link SolrDispatchFilter} for this node
- */
- public SolrDispatchFilter getSolrDispatchFilter() {
- return (SolrDispatchFilter) dispatchFilter.getFilter();
- }
-
- /**
- * @return the {@link SolrDispatchFilter} for this node
+ * @return the {@link RateLimitFilter} for this node
*/
public RateLimitFilter getSolrRateLimitFilter() {
return (RateLimitFilter) rateLimitFilter.getFilter();
@@ -511,12 +503,17 @@ public class JettySolrRunner {
* @return the {@link CoreContainer} for this node
*/
public CoreContainer getCoreContainer() {
- final var solrDispatchFilter = getSolrDispatchFilter();
- if (solrDispatchFilter == null) {
+ SolrServlet servlet;
+ try {
+ servlet = (SolrServlet) solrServlet.getServlet();
+ } catch (ServletException e1) {
+ throw new RuntimeException(e1);
+ }
+ if (servlet == null) {
return null;
}
try {
- return solrDispatchFilter.getCores();
+ return servlet.getCores();
} catch (UnavailableException e) {
return null;
}
@@ -530,13 +527,13 @@ public class JettySolrRunner {
}
public boolean isRunning() {
- return server.isRunning() && dispatchFilter != null && dispatchFilter.isRunning();
+ return server.isRunning() && solrServlet != null && solrServlet.isRunning();
}
public boolean isStopped() {
- return (server.isStopped() && dispatchFilter == null)
+ return (server.isStopped() && solrServlet == null)
|| (server.isStopped()
- && dispatchFilter.isStopped()
+ && solrServlet.isStopped()
&& ((QueuedThreadPool) server.getThreadPool()).isStopped());
}
@@ -584,7 +581,7 @@ public class JettySolrRunner {
server.start();
}
}
- assert dispatchFilter.isRunning();
+ assert solrServlet.isRunning();
if (config.waitForLoadingCoresToFinishMs != null
&& config.waitForLoadingCoresToFinishMs > 0L) {
@@ -900,18 +897,11 @@ public class JettySolrRunner {
}
private void waitForLoadingCoresToFinish(long timeoutMs) {
- if (dispatchFilter != null) {
- SolrDispatchFilter solrFilter = (SolrDispatchFilter) dispatchFilter.getFilter();
- CoreContainer cores;
- try {
- cores = solrFilter.getCores();
- } catch (UnavailableException e) {
- throw new IllegalStateException("The CoreContainer is unavailable!");
- }
- cores.waitForLoadingCoresToFinish(timeoutMs);
- } else {
- throw new IllegalStateException("The dispatchFilter is not set!");
+ CoreContainer cores = getCoreContainer();
+ if (cores == null) {
+ throw new IllegalStateException("solrServlet/coreContainer is not set/available!");
}
+ cores.waitForLoadingCoresToFinish(timeoutMs);
}
static class Delay {
diff --git a/solr/test-framework/src/java/org/apache/solr/util/TestHarness.java b/solr/test-framework/src/java/org/apache/solr/util/TestHarness.java
index 8a6abff7a10..d4ff7bc3195 100644
--- a/solr/test-framework/src/java/org/apache/solr/util/TestHarness.java
+++ b/solr/test-framework/src/java/org/apache/solr/util/TestHarness.java
@@ -48,6 +48,7 @@ import org.apache.solr.request.SolrRequestInfo;
import org.apache.solr.response.SolrQueryResponse;
import org.apache.solr.schema.IndexSchema;
import org.apache.solr.schema.IndexSchemaFactory;
+import org.apache.solr.servlet.CoreContainerProvider;
import org.apache.solr.update.UpdateShardHandlerConfig;
/**
@@ -113,16 +114,16 @@ public class TestHarness extends BaseTestHarness {
}
/**
- * Helper method to let us do some home sys prop check in delegated constructor. in "real" code
- * SolrDispatchFilter takes care of checking this sys prop when building NodeConfig/CoreContainer
+ * Helper method to let us do a solr-home sys prop check in a delegated constructor. in "real"
+ * code {@link CoreContainerProvider} takes care of checking this sys prop when building
+ * NodeConfig/CoreContainer
*/
private static Path checkAndReturnSolrHomeSysProp() {
- final String SOLR_HOME = "solr.solr.home";
- final String home = System.getProperty(SOLR_HOME);
+ final String home = System.getProperty(CoreContainerProvider.SOLR_SOLR_HOME);
if (null == home) {
throw new IllegalStateException(
"This TestHarness constructor requires "
- + SOLR_HOME
+ + CoreContainerProvider.SOLR_SOLR_HOME
+ " sys prop to be set by test first");
}
return Path.of(home).toAbsolutePath().normalize();
diff --git a/solr/webapp/web/WEB-INF/web.xml b/solr/webapp/web/WEB-INF/web.xml
index 501cb160f24..988482318c5 100644
--- a/solr/webapp/web/WEB-INF/web.xml
+++ b/solr/webapp/web/WEB-INF/web.xml
@@ -83,15 +83,16 @@
<filter-name>AuthenticationFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
- <filter>
- <filter-name>SolrRequestFilter</filter-name>
- <filter-class>org.apache.solr.servlet.SolrDispatchFilter</filter-class>
- </filter>
- <filter-mapping>
- <filter-name>SolrRequestFilter</filter-name>
+ <servlet>
+ <servlet-name>SolrServlet</servlet-name>
+ <servlet-class>org.apache.solr.servlet.SolrServlet</servlet-class>
+ </servlet>
+
+ <servlet-mapping>
+ <servlet-name>SolrServlet</servlet-name>
<url-pattern>/*</url-pattern>
- </filter-mapping>
+ </servlet-mapping>
<servlet>
<servlet-name>LoadAdminUI</servlet-name>
(solr) 04/06: SOLR-18125 Make LukeRequestHandler::getFirstLiveDoc get the first live doc (#4157)
Posted by ds...@apache.org.
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 a31be2e36a4c2cfd4dd036f4ee841fcf3c8848e3
Author: Luke Kot-Zaniewski <lk...@bloomberg.net>
AuthorDate: Fri Mar 20 21:22:23 2026 -0400
SOLR-18125 Make LukeRequestHandler::getFirstLiveDoc get the first live doc (#4157)
Fix getFirstLiveDoc so it returns the first live doc. Previously it would often return nothing resulting in missing index flags in /admin/luke response.
---
.../unreleased/PR#4157-fix-getFirstLiveDoc.yml | 9 ++++
.../solr/handler/admin/LukeRequestHandler.java | 4 +-
.../solr/handler/admin/LukeRequestHandlerTest.java | 55 ++++++++++++++++++++++
3 files changed, 66 insertions(+), 2 deletions(-)
diff --git a/changelog/unreleased/PR#4157-fix-getFirstLiveDoc.yml b/changelog/unreleased/PR#4157-fix-getFirstLiveDoc.yml
new file mode 100644
index 00000000000..9c744a6b83c
--- /dev/null
+++ b/changelog/unreleased/PR#4157-fix-getFirstLiveDoc.yml
@@ -0,0 +1,9 @@
+title: Fix getFirstLiveDoc so it returns the first live doc. Previously it would often return nothing resulting in missing index flags in /admin/luke response.
+type: fixed # added, changed, fixed, deprecated, removed, dependency_update, security, other
+authors:
+ - name: Luke Kot-Zaniewski
+links:
+ - name: SOLR-18125
+ url: https://issues.apache.org/jira/browse/SOLR-18125
+
+
diff --git a/solr/core/src/java/org/apache/solr/handler/admin/LukeRequestHandler.java b/solr/core/src/java/org/apache/solr/handler/admin/LukeRequestHandler.java
index e3d07b1cbca..b5879024d10 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/LukeRequestHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/LukeRequestHandler.java
@@ -472,7 +472,7 @@ public class LukeRequestHandler extends RequestHandlerBase {
StoredFields storedFields = reader.storedFields();
// Deal with the chance that the first bunch of terms are in deleted documents. Is there a
// better way?
- for (int idx = 0; idx < 1000 && postingsEnum == null; ++idx) {
+ for (int idx = 0; idx < 1000; ++idx) {
text = termsEnum.next();
// Ran off the end of the terms enum without finding any live docs with that field in them.
if (text == null) {
@@ -481,7 +481,7 @@ public class LukeRequestHandler extends RequestHandlerBase {
postingsEnum = termsEnum.postings(postingsEnum, PostingsEnum.NONE);
final Bits liveDocs = reader.getLiveDocs();
if (postingsEnum.nextDoc() != DocIdSetIterator.NO_MORE_DOCS) {
- if (liveDocs != null && liveDocs.get(postingsEnum.docID())) {
+ if (liveDocs != null && !liveDocs.get(postingsEnum.docID())) {
continue;
}
return storedFields.document(postingsEnum.docID());
diff --git a/solr/core/src/test/org/apache/solr/handler/admin/LukeRequestHandlerTest.java b/solr/core/src/test/org/apache/solr/handler/admin/LukeRequestHandlerTest.java
index 02d7066d215..5fa90f325a8 100644
--- a/solr/core/src/test/org/apache/solr/handler/admin/LukeRequestHandlerTest.java
+++ b/solr/core/src/test/org/apache/solr/handler/admin/LukeRequestHandlerTest.java
@@ -20,6 +20,7 @@ import java.util.Arrays;
import java.util.EnumSet;
import org.apache.solr.SolrTestCaseJ4;
import org.apache.solr.common.luke.FieldFlag;
+import org.apache.solr.index.NoMergePolicyFactory;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.schema.CustomAnalyzerStrField;
import org.apache.solr.schema.IndexSchema;
@@ -294,4 +295,58 @@ public class LukeRequestHandlerTest extends SolrTestCaseJ4 {
deleteCore();
initCore("solrconfig.xml", "schema12.xml");
}
+
+ /**
+ * SOLR-18125: Tests that Luke reports index flags for fields even when a document has been
+ * deleted. Uses NoMergePolicy so the deleted doc remains in the same segment, keeping liveDocs
+ * non-null and exposing the inverted liveDocs check in getFirstLiveDoc.
+ */
+ @Test
+ public void testIndexFlagsWithDeletedDocs() throws Exception {
+ deleteCore();
+ systemSetPropertySolrTestsMergePolicyFactory(NoMergePolicyFactory.class.getName());
+ try {
+ initCore("solrconfig.xml", "schema12.xml");
+
+ // Three docs in a single commit so they share one segment. Delete the middle term
+ // ("bbb") and keep the lexical edges ("aaa" and "ccc") live, so the first term
+ // encountered is always a live doc regardless of ascending or descending iteration.
+ // The bug's inverted liveDocs check skips live docs, so getFirstLiveDoc returns null
+ // and index flags go missing.
+ assertU(adoc("id", "1", "solr_s", "aaa"));
+ assertU(adoc("id", "2", "solr_s", "bbb"));
+ assertU(adoc("id", "3", "solr_s", "ccc"));
+ assertU(commit());
+
+ assertU(delI("2"));
+ assertU(commit());
+
+ assertQ(
+ "index flags should be present for solr_s despite deletion in segment",
+ req("qt", "/admin/luke", "fl", "solr_s"),
+ getFieldXPathPrefix("solr_s") + "[@name='index']");
+
+ // Now test the inverse: delete the edges and keep the middle. The first term
+ // encountered ("aaa" or "ccc") is deleted, so getFirstLiveDoc must skip it and
+ // continue to the live term ("bbb"). This exercises the retry loop — without it,
+ // the method gives up after the first deleted term.
+ clearIndex();
+ assertU(adoc("id", "4", "solr_s", "aaa"));
+ assertU(adoc("id", "5", "solr_s", "bbb"));
+ assertU(adoc("id", "6", "solr_s", "ccc"));
+ assertU(commit());
+
+ assertU(delI("4"));
+ assertU(delI("6"));
+ assertU(commit());
+
+ assertQ(
+ "index flags should be present for solr_s when edges are deleted",
+ req("qt", "/admin/luke", "fl", "solr_s"),
+ getFieldXPathPrefix("solr_s") + "[@name='index']");
+ } finally {
+ deleteCore();
+ initCore("solrconfig.xml", "schema12.xml");
+ }
+ }
}