You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@solr.apache.org by ge...@apache.org on 2024/01/30 16:09:16 UTC

(solr) branch main updated: SOLR-17066: Add GenericSolrRequest.setRequiresCollection (#2229)

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

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


The following commit(s) were added to refs/heads/main by this push:
     new d5d7e557f9d SOLR-17066: Add GenericSolrRequest.setRequiresCollection (#2229)
d5d7e557f9d is described below

commit d5d7e557f9dfcae2869c79269ec02d700a861c7b
Author: Jason Gerlowski <ge...@apache.org>
AuthorDate: Tue Jan 30 11:09:11 2024 -0500

    SOLR-17066: Add GenericSolrRequest.setRequiresCollection (#2229)
    
    SOLR-17066 added a 'defaultCollection' field to each SolrClient
    implementation, similar to the one that has long been in use for SolrJ's
    "cloud" clients.  This default collection (or core) is used on a
    request-by-request basis to build the complete HTTP path, based on the
    value of SolrRequest.requiresCollection().
    
    This is a particular challenge for GenericSolrRequest though, which can
    be used to make both collection-agnostic and collection-aware requests.
    This commit adds a GenericSolrRequest setter,
    `setRequiresCollection(boolean)`, to allow GSR users to specify whether
    the client-level default collection should be used on their request or not.
---
 solr/CHANGES.txt                                   |  3 ++
 .../src/java/org/apache/solr/cli/ExportTool.java   | 21 ++++++------
 .../apache/solr/packagemanager/PackageManager.java | 26 ++++++++++-----
 .../apache/solr/packagemanager/PackageUtils.java   | 36 ++++++++++++++++++--
 .../solr/packagemanager/RepositoryManager.java     |  2 +-
 .../processor/DistributedUpdateProcessor.java      |  3 +-
 .../solr/handler/TestReplicationHandler.java       | 38 ++++++++++++----------
 .../solr/handler/TestStressThreadBackup.java       |  3 +-
 .../src/test/org/apache/solr/pkg/TestPackages.java | 15 +++++----
 .../org/apache/solr/search/TestTaskManagement.java | 10 ++++--
 .../solr/security/BasicAuthIntegrationTest.java    |  2 +-
 .../org/apache/solr/util/TestCborDataFormat.java   |  8 +++--
 .../client/solrj/request/GenericSolrRequest.java   | 32 ++++++++++++++++++
 .../client/solrj/impl/Http2SolrClientTest.java     |  7 ++--
 .../apache/solr/handler/BackupStatusChecker.java   |  2 ++
 15 files changed, 147 insertions(+), 61 deletions(-)

diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index 1ef6e91bce4..8e6e5eaabbe 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -103,6 +103,9 @@ Other Changes
 ---------------------
 * SOLR-17126: Cut over System.getProperty to EnvUtils.getProperty (janhoy)
 
+* SOLR-17066: GenericSolrRequest now has a `setRequiresCollection` setter that allows it to specify whether
+  it should make use of the client-level default collection/core. (Jason Gerlowski)
+
 ==================  9.5.0 ==================
 New Features
 ---------------------
diff --git a/solr/core/src/java/org/apache/solr/cli/ExportTool.java b/solr/core/src/java/org/apache/solr/cli/ExportTool.java
index e48d4ea5f91..694049469d2 100644
--- a/solr/core/src/java/org/apache/solr/cli/ExportTool.java
+++ b/solr/core/src/java/org/apache/solr/cli/ExportTool.java
@@ -67,6 +67,7 @@ import org.apache.solr.client.solrj.impl.ClusterStateProvider;
 import org.apache.solr.client.solrj.impl.Http2SolrClient;
 import org.apache.solr.client.solrj.impl.StreamingBinaryResponseParser;
 import org.apache.solr.client.solrj.request.GenericSolrRequest;
+import org.apache.solr.client.solrj.request.QueryRequest;
 import org.apache.solr.common.SolrDocument;
 import org.apache.solr.common.SolrDocumentList;
 import org.apache.solr.common.cloud.DocCollection;
@@ -219,9 +220,10 @@ public class ExportTool extends ToolBase {
       NamedList<Object> response =
           solrClient.request(
               new GenericSolrRequest(
-                  SolrRequest.METHOD.GET,
-                  "/schema/uniquekey",
-                  new MapSolrParams(Collections.singletonMap("collection", coll))));
+                      SolrRequest.METHOD.GET,
+                      "/schema/uniquekey",
+                      new MapSolrParams(Collections.singletonMap("collection", coll)))
+                  .setRequiresCollection(true));
       uniqueKey = (String) response.get("uniqueKey");
     }
 
@@ -620,7 +622,7 @@ public class ExportTool extends ToolBase {
 
         try (SolrClient client = SolrCLI.getSolrClient(baseurl, credentials)) {
           expectedDocs = getDocCount(replica.getCoreName(), client, query);
-          GenericSolrRequest request;
+          QueryRequest request;
           ModifiableSolrParams params = new ModifiableSolrParams();
           params.add(Q, query);
           if (fields != null) params.add(FL, fields);
@@ -644,12 +646,10 @@ public class ExportTool extends ToolBase {
             if (failed) return false;
             if (docsWritten.get() > limit) return true;
             params.set(CursorMarkParams.CURSOR_MARK_PARAM, cursorMark);
-            request =
-                new GenericSolrRequest(
-                    SolrRequest.METHOD.GET, "/" + replica.getCoreName() + "/select", params);
+            request = new QueryRequest(params);
             request.setResponseParser(responseParser);
             try {
-              NamedList<Object> rsp = client.request(request);
+              NamedList<Object> rsp = client.request(request, replica.getCoreName());
               String nextCursorMark = (String) rsp.get(CursorMarkParams.CURSOR_MARK_NEXT);
               if (nextCursorMark == null || Objects.equals(cursorMark, nextCursorMark)) {
                 if (output != null) {
@@ -692,9 +692,8 @@ public class ExportTool extends ToolBase {
     SolrQuery q = new SolrQuery(query);
     q.setRows(0);
     q.add("distrib", "false");
-    GenericSolrRequest request =
-        new GenericSolrRequest(SolrRequest.METHOD.GET, "/" + coreName + "/select", q);
-    NamedList<Object> res = client.request(request);
+    final var request = new QueryRequest(q);
+    NamedList<Object> res = client.request(request, coreName);
     SolrDocumentList sdl = (SolrDocumentList) res.get("response");
     return sdl.getNumFound();
   }
diff --git a/solr/core/src/java/org/apache/solr/packagemanager/PackageManager.java b/solr/core/src/java/org/apache/solr/packagemanager/PackageManager.java
index 7c28fa9e4c2..8732f38356f 100644
--- a/solr/core/src/java/org/apache/solr/packagemanager/PackageManager.java
+++ b/solr/core/src/java/org/apache/solr/packagemanager/PackageManager.java
@@ -237,8 +237,10 @@ public class PackageManager implements Closeable {
       NamedList<Object> result =
           solrClient.request(
               new GenericSolrRequest(
-                  SolrRequest.METHOD.GET,
-                  PackageUtils.getCollectionParamsPath(collection) + "/PKG_VERSIONS"));
+                      SolrRequest.METHOD.GET,
+                      PackageUtils.getCollectionParamsPath(collection) + "/PKG_VERSIONS")
+                  .setRequiresCollection(
+                      false) /* Making a collection request, but already baked into path */);
       packages =
           (Map<String, String>)
               result._get("/response/params/PKG_VERSIONS", Collections.emptyMap());
@@ -420,8 +422,10 @@ public class PackageManager implements Closeable {
             solrClient
                 .request(
                     new GenericSolrRequest(
-                        SolrRequest.METHOD.GET,
-                        PackageUtils.getCollectionParamsPath(collection) + "/packages"))
+                            SolrRequest.METHOD.GET,
+                            PackageUtils.getCollectionParamsPath(collection) + "/packages")
+                        .setRequiresCollection(
+                            false) /* Making a collection-request, but already baked into path */)
                 .asShallowMap()
                 .containsKey("params");
         SolrCLI.postJsonToSolr(
@@ -722,8 +726,10 @@ public class PackageManager implements Closeable {
       NamedList<Object> response =
           solrClient.request(
               new GenericSolrRequest(
-                  SolrRequest.METHOD.GET,
-                  PackageUtils.getCollectionParamsPath(collection) + "/packages"));
+                      SolrRequest.METHOD.GET,
+                      PackageUtils.getCollectionParamsPath(collection) + "/packages")
+                  .setRequiresCollection(
+                      false) /* Making a collection-request, but already baked into path */);
       return (Map<String, String>)
           response._get("/response/params/packages/" + packageName, Collections.emptyMap());
     } catch (Exception ex) {
@@ -766,7 +772,8 @@ public class PackageManager implements Closeable {
 
           if ("GET".equalsIgnoreCase(cmd.method)) {
             String response =
-                PackageUtils.getJsonStringFromUrl(solrClient, path, new ModifiableSolrParams());
+                PackageUtils.getJsonStringFromNonCollectionApi(
+                    solrClient, path, new ModifiableSolrParams());
             PackageUtils.printGreen(response);
             String actualValue = null;
             try {
@@ -816,7 +823,8 @@ public class PackageManager implements Closeable {
 
             if ("GET".equalsIgnoreCase(cmd.method)) {
               String response =
-                  PackageUtils.getJsonStringFromUrl(solrClient, path, new ModifiableSolrParams());
+                  PackageUtils.getJsonStringFromCollectionApi(
+                      solrClient, path, new ModifiableSolrParams());
               PackageUtils.printGreen(response);
               String actualValue = null;
               try {
@@ -1098,7 +1106,7 @@ public class PackageManager implements Closeable {
     for (String collection : allCollections) {
       // Check package version installed
       String paramsJson =
-          PackageUtils.getJsonStringFromUrl(
+          PackageUtils.getJsonStringFromCollectionApi(
               solrClient,
               PackageUtils.getCollectionParamsPath(collection) + "/PKG_VERSIONS",
               new ModifiableSolrParams().add("omitHeader", "true"));
diff --git a/solr/core/src/java/org/apache/solr/packagemanager/PackageUtils.java b/solr/core/src/java/org/apache/solr/packagemanager/PackageUtils.java
index 10a255a81f5..d6e765b0d4a 100644
--- a/solr/core/src/java/org/apache/solr/packagemanager/PackageUtils.java
+++ b/solr/core/src/java/org/apache/solr/packagemanager/PackageUtils.java
@@ -130,7 +130,8 @@ public class PackageUtils {
   public static <T> T getJson(SolrClient client, String path, Class<T> klass) {
     try {
       return getMapper()
-          .readValue(getJsonStringFromUrl(client, path, new ModifiableSolrParams()), klass);
+          .readValue(
+              getJsonStringFromNonCollectionApi(client, path, new ModifiableSolrParams()), klass);
     } catch (IOException e) {
       throw new RuntimeException(e);
     }
@@ -154,10 +155,39 @@ public class PackageUtils {
     return null;
   }
 
+  /**
+   * Returns the response of a collection or core API call as string-ified JSON
+   *
+   * @param client the SolrClient used to make the request
+   * @param path the HTTP path of the Solr API to hit, starting after the collection or core name
+   *     (i.e. omitting '/solr/techproducts')
+   * @param params query parameters to include when making the request
+   */
+  public static String getJsonStringFromCollectionApi(
+      SolrClient client, String path, SolrParams params) {
+    return getJsonStringFromUrl(client, path, params, true);
+  }
+
+  /**
+   * Returns the response of a collection-agnostic API call as string-ified JSON
+   *
+   * @param client the SolrClient used to make the request
+   * @param path the HTTP path of the Solr API to hit, starting after '/solr' (or '/api' for v2
+   *     requests)
+   * @param params query parameters to include when making the request
+   */
+  public static String getJsonStringFromNonCollectionApi(
+      SolrClient client, String path, SolrParams params) {
+    return getJsonStringFromUrl(client, path, params, false);
+  }
+
   /** Returns JSON string from a given Solr URL */
-  public static String getJsonStringFromUrl(SolrClient client, String path, SolrParams params) {
+  private static String getJsonStringFromUrl(
+      SolrClient client, String path, SolrParams params, boolean isCollectionApi) {
     try {
-      GenericSolrRequest request = new GenericSolrRequest(SolrRequest.METHOD.GET, path, params);
+      GenericSolrRequest request =
+          new GenericSolrRequest(SolrRequest.METHOD.GET, path, params)
+              .setRequiresCollection(isCollectionApi);
       request.setResponseParser(new JsonMapResponseParser());
       NamedList<Object> response = client.request(request);
       return response.jsonStr();
diff --git a/solr/core/src/java/org/apache/solr/packagemanager/RepositoryManager.java b/solr/core/src/java/org/apache/solr/packagemanager/RepositoryManager.java
index f7ac243d0a3..109468f854c 100644
--- a/solr/core/src/java/org/apache/solr/packagemanager/RepositoryManager.java
+++ b/solr/core/src/java/org/apache/solr/packagemanager/RepositoryManager.java
@@ -148,7 +148,7 @@ public class RepositoryManager {
     // put the public key into package store's trusted key store and request a sync.
     String path = FileStoreAPI.KEYS_DIR + "/" + destinationKeyFilename;
     PackageUtils.uploadKey(key, path, Paths.get(solrHome));
-    PackageUtils.getJsonStringFromUrl(
+    PackageUtils.getJsonStringFromNonCollectionApi(
         solrClient, "/api/node/files" + path, new ModifiableSolrParams().add("sync", "true"));
   }
 
diff --git a/solr/core/src/java/org/apache/solr/update/processor/DistributedUpdateProcessor.java b/solr/core/src/java/org/apache/solr/update/processor/DistributedUpdateProcessor.java
index 3bbe0a8b38c..48d637d3a39 100644
--- a/solr/core/src/java/org/apache/solr/update/processor/DistributedUpdateProcessor.java
+++ b/solr/core/src/java/org/apache/solr/update/processor/DistributedUpdateProcessor.java
@@ -702,7 +702,8 @@ public class DistributedUpdateProcessor extends UpdateRequestProcessor {
     params.set(DISTRIB, false);
     params.set("getInputDocument", id);
     params.set("onlyIfActive", true);
-    SolrRequest<SimpleSolrResponse> ur = new GenericSolrRequest(METHOD.GET, "/get", params);
+    SolrRequest<SimpleSolrResponse> ur =
+        new GenericSolrRequest(METHOD.GET, "/get", params).setRequiresCollection(true);
 
     String leaderUrl = getLeaderUrl(id);
 
diff --git a/solr/core/src/test/org/apache/solr/handler/TestReplicationHandler.java b/solr/core/src/test/org/apache/solr/handler/TestReplicationHandler.java
index 8fc56376c2a..730b326f30f 100644
--- a/solr/core/src/test/org/apache/solr/handler/TestReplicationHandler.java
+++ b/solr/core/src/test/org/apache/solr/handler/TestReplicationHandler.java
@@ -1617,15 +1617,16 @@ public class TestReplicationHandler extends SolrTestCaseJ4 {
       final String backupName = "empty_backup1";
       final GenericSolrRequest req =
           new GenericSolrRequest(
-              SolrRequest.METHOD.GET,
-              "/replication",
-              params(
-                  "command",
-                  "backup",
-                  "location",
-                  backupDir.getAbsolutePath(),
-                  "name",
-                  backupName));
+                  SolrRequest.METHOD.GET,
+                  "/replication",
+                  params(
+                      "command",
+                      "backup",
+                      "location",
+                      backupDir.getAbsolutePath(),
+                      "name",
+                      backupName))
+              .setRequiresCollection(true);
       final TimeOut timeout = new TimeOut(30, TimeUnit.SECONDS, TimeSource.NANO_TIME);
       final SimpleSolrResponse rsp = req.process(leaderClient);
 
@@ -1645,15 +1646,16 @@ public class TestReplicationHandler extends SolrTestCaseJ4 {
       final String backupName = "empty_backup2";
       final GenericSolrRequest req =
           new GenericSolrRequest(
-              SolrRequest.METHOD.GET,
-              "/replication",
-              params(
-                  "command",
-                  "backup",
-                  "location",
-                  backupDir.getAbsolutePath(),
-                  "name",
-                  backupName));
+                  SolrRequest.METHOD.GET,
+                  "/replication",
+                  params(
+                      "command",
+                      "backup",
+                      "location",
+                      backupDir.getAbsolutePath(),
+                      "name",
+                      backupName))
+              .setRequiresCollection(true);
       final TimeOut timeout = new TimeOut(30, TimeUnit.SECONDS, TimeSource.NANO_TIME);
       final SimpleSolrResponse rsp = req.process(leaderClient);
 
diff --git a/solr/core/src/test/org/apache/solr/handler/TestStressThreadBackup.java b/solr/core/src/test/org/apache/solr/handler/TestStressThreadBackup.java
index 0f494b291a4..8b81b733ab5 100644
--- a/solr/core/src/test/org/apache/solr/handler/TestStressThreadBackup.java
+++ b/solr/core/src/test/org/apache/solr/handler/TestStressThreadBackup.java
@@ -133,7 +133,8 @@ public class TestStressThreadBackup extends SolrCloudTestCase {
 
           /** no solrj API for ReplicationHandler */
           private GenericSolrRequest makeReplicationReq(SolrParams p) {
-            return new GenericSolrRequest(GenericSolrRequest.METHOD.GET, "/replication", p);
+            return new GenericSolrRequest(GenericSolrRequest.METHOD.GET, "/replication", p)
+                .setRequiresCollection(true);
           }
 
           /**
diff --git a/solr/core/src/test/org/apache/solr/pkg/TestPackages.java b/solr/core/src/test/org/apache/solr/pkg/TestPackages.java
index 1bc6810af63..a3f1917473d 100644
--- a/solr/core/src/test/org/apache/solr/pkg/TestPackages.java
+++ b/solr/core/src/test/org/apache/solr/pkg/TestPackages.java
@@ -291,10 +291,11 @@ public class TestPackages extends SolrCloudTestCase {
         10,
         cluster.getSolrClient(),
         new GenericSolrRequest(
-            SolrRequest.METHOD.GET,
-            "/stream",
-            new MapSolrParams(
-                Map.of("collection", COLLECTION_NAME, WT, JAVABIN, "action", "plugins"))),
+                SolrRequest.METHOD.GET,
+                "/stream",
+                new MapSolrParams(
+                    Map.of("collection", COLLECTION_NAME, WT, JAVABIN, "action", "plugins")))
+            .setRequiresCollection(true),
         Map.of(":plugins:mincopy", "org.apache.solr.client.solrj.io.stream.metrics.MinCopyMetric"));
 
     UpdateRequest ur = new UpdateRequest();
@@ -558,7 +559,8 @@ public class TestPackages extends SolrCloudTestCase {
                 "true"));
 
     GenericSolrRequest req1 =
-        new GenericSolrRequest(SolrRequest.METHOD.GET, "/config/" + componentType, params);
+        new GenericSolrRequest(SolrRequest.METHOD.GET, "/config/" + componentType, params)
+            .setRequiresCollection(true);
     TestDistribFileStore.assertResponseValues(
         10,
         client,
@@ -843,7 +845,8 @@ public class TestPackages extends SolrCloudTestCase {
     SolrParams params =
         new MapSolrParams(Map.of("collection", COLLECTION_NAME, WT, JAVABIN, "meta", "true"));
 
-    GenericSolrRequest req = new GenericSolrRequest(SolrRequest.METHOD.GET, path, params);
+    GenericSolrRequest req =
+        new GenericSolrRequest(SolrRequest.METHOD.GET, path, params).setRequiresCollection(true);
     TestDistribFileStore.assertResponseValues(10, client, req, expected);
   }
 
diff --git a/solr/core/src/test/org/apache/solr/search/TestTaskManagement.java b/solr/core/src/test/org/apache/solr/search/TestTaskManagement.java
index 8efeb9fb583..6f7ca6dcdf9 100644
--- a/solr/core/src/test/org/apache/solr/search/TestTaskManagement.java
+++ b/solr/core/src/test/org/apache/solr/search/TestTaskManagement.java
@@ -109,7 +109,8 @@ public class TestTaskManagement extends SolrCloudTestCase {
     params.set("queryUUID", "foobar");
 
     GenericSolrRequest request =
-        new GenericSolrRequest(SolrRequest.METHOD.GET, "/tasks/cancel", params);
+        new GenericSolrRequest(SolrRequest.METHOD.GET, "/tasks/cancel", params)
+            .setRequiresCollection(true);
     NamedList<Object> queryResponse = cluster.getSolrClient(COLLECTION_NAME).request(request);
 
     assertEquals("Query with queryID foobar not found", queryResponse.get("status"));
@@ -185,7 +186,9 @@ public class TestTaskManagement extends SolrCloudTestCase {
     NamedList<Object> response =
         cluster
             .getSolrClient(COLLECTION_NAME)
-            .request(new GenericSolrRequest(SolrRequest.METHOD.GET, "/tasks/list"));
+            .request(
+                new GenericSolrRequest(SolrRequest.METHOD.GET, "/tasks/list")
+                    .setRequiresCollection(true));
     return (NamedList<String>) response.get("taskList");
   }
 
@@ -195,7 +198,8 @@ public class TestTaskManagement extends SolrCloudTestCase {
     params.set("taskUUID", "25");
 
     GenericSolrRequest request =
-        new GenericSolrRequest(SolrRequest.METHOD.GET, "/tasks/list", params);
+        new GenericSolrRequest(SolrRequest.METHOD.GET, "/tasks/list", params)
+            .setRequiresCollection(true);
     NamedList<Object> queryResponse = cluster.getSolrClient(COLLECTION_NAME).request(request);
 
     String result = (String) queryResponse.get("taskStatus");
diff --git a/solr/core/src/test/org/apache/solr/security/BasicAuthIntegrationTest.java b/solr/core/src/test/org/apache/solr/security/BasicAuthIntegrationTest.java
index 3599a4e2ebe..aed2a742632 100644
--- a/solr/core/src/test/org/apache/solr/security/BasicAuthIntegrationTest.java
+++ b/solr/core/src/test/org/apache/solr/security/BasicAuthIntegrationTest.java
@@ -330,7 +330,7 @@ public class BasicAuthIntegrationTest extends SolrCloudAuthTestCase {
       assertPkiAuthMetricsMinimums(3, 3, 0, 0, 0, 0);
 
       // Query that succeeds
-      GenericSolrRequest req = new GenericSolrRequest(SolrRequest.METHOD.GET, "/select", params);
+      final var req = new QueryRequest(params);
       req.setBasicAuthCredentials("harry", "HarryIsUberCool");
       cluster.getSolrClient().request(req, COLLECTION);
 
diff --git a/solr/core/src/test/org/apache/solr/util/TestCborDataFormat.java b/solr/core/src/test/org/apache/solr/util/TestCborDataFormat.java
index b24e649ce28..be423b1a21e 100644
--- a/solr/core/src/test/org/apache/solr/util/TestCborDataFormat.java
+++ b/solr/core/src/test/org/apache/solr/util/TestCborDataFormat.java
@@ -159,6 +159,7 @@ public class TestCborDataFormat extends SolrCloudTestCase {
       throws SolrServerException, IOException {
     GenericSolrRequest req =
         new GenericSolrRequest(SolrRequest.METHOD.POST, "/schema")
+            .setRequiresCollection(true)
             .setContentWriter(
                 new RequestWriter.StringPayloadContentWriter(
                     "{\n"
@@ -180,6 +181,7 @@ public class TestCborDataFormat extends SolrCloudTestCase {
             SolrRequest.METHOD.POST,
             "/update/json/docs",
             new MapSolrParams(Map.of("commit", "true")))
+        .setRequiresCollection(true)
         .withContent(b, "application/json");
   }
 
@@ -191,13 +193,15 @@ public class TestCborDataFormat extends SolrCloudTestCase {
 
     return new GenericSolrRequest(
             SolrRequest.METHOD.POST, "/update", new MapSolrParams(Map.of("commit", "true")))
-        .withContent(baos.toByteArray(), "application/javabin");
+        .withContent(baos.toByteArray(), "application/javabin")
+        .setRequiresCollection(true);
   }
 
   private GenericSolrRequest createCborReq(byte[] b) throws IOException {
     return new GenericSolrRequest(
             SolrRequest.METHOD.POST, "/update/cbor", new MapSolrParams(Map.of("commit", "true")))
-        .withContent(serializeToCbor(b), "application/cbor");
+        .withContent(serializeToCbor(b), "application/cbor")
+        .setRequiresCollection(true);
   }
 
   @SuppressWarnings("unchecked")
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/request/GenericSolrRequest.java b/solr/solrj/src/java/org/apache/solr/client/solrj/request/GenericSolrRequest.java
index e9babd55371..fab6f6470e7 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/request/GenericSolrRequest.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/request/GenericSolrRequest.java
@@ -29,16 +29,48 @@ public class GenericSolrRequest extends SolrRequest<SimpleSolrResponse> {
   public SolrParams params;
   public SimpleSolrResponse response = new SimpleSolrResponse();
   public ContentWriter contentWriter;
+  public boolean requiresCollection;
 
+  /**
+   * @param m the HTTP method to use for this request
+   * @param path the HTTP path to use for this request. If users are making a collection-aware
+   *     request (i.e. {@link #setRequiresCollection(boolean)} is called with 'true'), only the
+   *     section of the API path following the collection or core should be provided here.
+   */
   public GenericSolrRequest(METHOD m, String path) {
     this(m, path, new ModifiableSolrParams());
   }
 
+  /**
+   * @param m the HTTP method to use for this request
+   * @param path the HTTP path to use for this request. If users are making a collection-aware
+   *     request (i.e. {@link #setRequiresCollection(boolean)} is called with 'true'), only the
+   *     section of the API path following the collection or core should be provided here.
+   * @param params query parameter names and values for making this request.
+   */
   public GenericSolrRequest(METHOD m, String path, SolrParams params) {
     super(m, path);
     this.params = params;
   }
 
+  /**
+   * Determines whether the SolrRequest should use a default collection or core from the client
+   *
+   * <p>Should generally be 'true' whenever making a request to a particular collection or core, and
+   * 'false' otherwise.
+   *
+   * @param requiresCollection true if a default collection should be used, false otherwise.
+   */
+  public GenericSolrRequest setRequiresCollection(boolean requiresCollection) {
+    this.requiresCollection = requiresCollection;
+    return this;
+  }
+
+  @Override
+  public boolean requiresCollection() {
+    return requiresCollection;
+  }
+
   public GenericSolrRequest setContentWriter(ContentWriter contentWriter) {
     this.contentWriter = contentWriter;
     return this;
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/Http2SolrClientTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/Http2SolrClientTest.java
index 20b96a782e7..76d23318f93 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/Http2SolrClientTest.java
+++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/Http2SolrClientTest.java
@@ -43,12 +43,10 @@ import org.apache.solr.client.solrj.SolrClient;
 import org.apache.solr.client.solrj.SolrQuery;
 import org.apache.solr.client.solrj.SolrRequest;
 import org.apache.solr.client.solrj.SolrServerException;
-import org.apache.solr.client.solrj.request.GenericSolrRequest;
 import org.apache.solr.client.solrj.request.QueryRequest;
 import org.apache.solr.client.solrj.request.RequestWriter;
 import org.apache.solr.client.solrj.request.SolrPing;
 import org.apache.solr.client.solrj.request.UpdateRequest;
-import org.apache.solr.client.solrj.response.SimpleSolrResponse;
 import org.apache.solr.common.SolrException;
 import org.apache.solr.common.SolrInputDocument;
 import org.apache.solr.common.params.CommonParams;
@@ -1012,10 +1010,9 @@ public class Http2SolrClientTest extends SolrJettyTestBase {
         getHttp2SolrClientBuilder(
                 getBaseUrl() + "/debug/foo", DEFAULT_CONNECTION_TIMEOUT, DEFAULT_CONNECTION_TIMEOUT)
             .build()) {
-      GenericSolrRequest req =
-          new GenericSolrRequest(SolrRequest.METHOD.GET, "/select", params("q", "*:*"));
+      final var req = new QueryRequest(params("q", "*:*"));
       req.setResponseParser(new InputStreamResponseParser("xml"));
-      SimpleSolrResponse rsp = req.process(client);
+      final var rsp = req.process(client);
       Object stream = rsp.getResponse().get("stream");
       assertNotNull(stream);
       MatcherAssert.assertThat(stream, instanceOf(InputStream.class));
diff --git a/solr/test-framework/src/java/org/apache/solr/handler/BackupStatusChecker.java b/solr/test-framework/src/java/org/apache/solr/handler/BackupStatusChecker.java
index 3a9a2b57b16..a3e8571f8c1 100644
--- a/solr/test-framework/src/java/org/apache/solr/handler/BackupStatusChecker.java
+++ b/solr/test-framework/src/java/org/apache/solr/handler/BackupStatusChecker.java
@@ -190,6 +190,7 @@ public final class BackupStatusChecker {
     final String label = (null == backupName ? "latest backup" : backupName);
     final SimpleSolrResponse rsp =
         new GenericSolrRequest(GenericSolrRequest.METHOD.GET, path, params("command", "details"))
+            .setRequiresCollection(true)
             .process(client);
     final NamedList<?> data = rsp.getResponse();
     log.info("Checking Status of {}: {}", label, data);
@@ -266,6 +267,7 @@ public final class BackupStatusChecker {
     assertNotNull("backumpName must not be null", backupName);
     final SimpleSolrResponse rsp =
         new GenericSolrRequest(GenericSolrRequest.METHOD.GET, path, params("command", "details"))
+            .setRequiresCollection(true)
             .process(client);
     final NamedList<?> data = rsp.getResponse();
     log.info("Checking Deletion Status of {}: {}", backupName, data);