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

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

commit 058f15be47e7e55f22b319767dffd653767cf683
Author: Jason Gerlowski <[email protected]>
AuthorDate: Fri Oct 6 10:48:06 2023 -0400

    SOLR-16825: Migrate v2 definitions to 'api' module, pt 5 (#1974)
    
    This commit covers the collection-prop, core-snapshot, migrate-replicas,
    reload-core, and restore-core APIs.
    
    Extracting annotated interfaces for these APIs includes them in the 
SolrRequest-
    generation we now do in SolrJ
---
 .../client/api/endpoint/CollectionPropertyApi.java |  47 ++++++++
 .../solr/client/api/endpoint/CoreSnapshotApi.java  |  79 ++++++++++++
 .../client/api/endpoint/MigrateReplicasApi.java    |  37 ++++++
 .../solr/client/api/endpoint/ReloadCoreApi.java    |  44 +++++++
 .../solr/client/api/endpoint/RestoreCoreApi.java   |  40 ++++++
 .../apache/solr/client/api/model/Constants.java    |   7 ++
 .../api/model/CreateCoreSnapshotResponse.java      |  45 +++++++
 .../client/api/model/DeleteSnapshotResponse.java   |  32 +++++
 .../api/model/ListCoreSnapshotsResponse.java       |  27 +++++
 .../api/model/MigrateReplicasRequestBody.java      |  56 +++++++++
 .../client/api/model/ReloadCoreRequestBody.java    |  26 ++++
 .../client/api/model/RestoreCoreRequestBody.java   |  36 ++++++
 .../solr/client/api/model/SnapshotInformation.java |  41 +++++++
 .../model/UpdateCollectionPropertyRequestBody.java |  30 +++++
 .../solr/core/snapshots/SolrSnapshotManager.java   |   4 +-
 .../solr/handler/admin/CollectionsHandler.java     |  17 ++-
 .../solr/handler/admin/CoreAdminHandler.java       |  12 +-
 .../solr/handler/admin/CoreAdminOperation.java     |  21 ++--
 .../solr/handler/admin/CreateSnapshotOp.java       |   9 +-
 .../solr/handler/admin/DeleteSnapshotOp.java       |   9 +-
 .../apache/solr/handler/admin/RestoreCoreOp.java   |  13 +-
 ...ionPropertyAPI.java => CollectionProperty.java} |  42 ++-----
 .../{CoreSnapshotAPI.java => CoreSnapshot.java}    | 134 +++------------------
 ...igrateReplicasAPI.java => MigrateReplicas.java} |  60 +--------
 .../api/{ReloadCoreAPI.java => ReloadCore.java}    |  35 ++----
 .../api/{RestoreCoreAPI.java => RestoreCore.java}  |  63 +++-------
 .../org/apache/solr/cloud/MigrateReplicasTest.java |  18 +--
 .../test/org/apache/solr/core/PluginBagTest.java   |   4 +-
 .../handler/admin/api/CoreSnapshotAPITest.java     |  16 +--
 .../handler/admin/api/MigrateReplicasAPITest.java  |  23 ++--
 .../solr/handler/admin/api/ReloadCoreAPITest.java  |  12 +-
 31 files changed, 687 insertions(+), 352 deletions(-)

diff --git 
a/solr/api/src/java/org/apache/solr/client/api/endpoint/CollectionPropertyApi.java
 
b/solr/api/src/java/org/apache/solr/client/api/endpoint/CollectionPropertyApi.java
new file mode 100644
index 00000000000..ed26ea443cc
--- /dev/null
+++ 
b/solr/api/src/java/org/apache/solr/client/api/endpoint/CollectionPropertyApi.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.solr.client.api.endpoint;
+
+import io.swagger.v3.oas.annotations.Operation;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import org.apache.solr.client.api.model.SolrJerseyResponse;
+import org.apache.solr.client.api.model.UpdateCollectionPropertyRequestBody;
+
+/** V2 API definitions for modifying collection-level properties. */
+@Path("/collections/{collName}/properties/{propName}")
+public interface CollectionPropertyApi {
+  @PUT
+  @Operation(
+      summary = "Create or update a collection property",
+      tags = {"collection-properties"})
+  SolrJerseyResponse createOrUpdateCollectionProperty(
+      @PathParam("collName") String collName,
+      @PathParam("propName") String propName,
+      UpdateCollectionPropertyRequestBody requestBody)
+      throws Exception;
+
+  @DELETE
+  @Operation(
+      summary = "Delete the specified collection property from the collection",
+      tags = {"collection-properties"})
+  SolrJerseyResponse deleteCollectionProperty(
+      @PathParam("collName") String collName, @PathParam("propName") String 
propName)
+      throws Exception;
+}
diff --git 
a/solr/api/src/java/org/apache/solr/client/api/endpoint/CoreSnapshotApi.java 
b/solr/api/src/java/org/apache/solr/client/api/endpoint/CoreSnapshotApi.java
new file mode 100644
index 00000000000..8d19211da16
--- /dev/null
+++ b/solr/api/src/java/org/apache/solr/client/api/endpoint/CoreSnapshotApi.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.solr.client.api.endpoint;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.QueryParam;
+import org.apache.solr.client.api.model.CreateCoreSnapshotResponse;
+import org.apache.solr.client.api.model.DeleteSnapshotResponse;
+import org.apache.solr.client.api.model.ListCoreSnapshotsResponse;
+
+/** V2 API definitions for Creating, Listing, and Deleting Core Snapshots. */
+@Path("/cores/{coreName}/snapshots")
+public interface CoreSnapshotApi {
+  @POST
+  @Path("/{snapshotName}")
+  @Operation(
+      summary = "Create a new snapshot of the specified core.",
+      tags = {"core-snapshots"})
+  CreateCoreSnapshotResponse createSnapshot(
+      @Parameter(description = "The name of the core to snapshot.", required = 
true)
+          @PathParam("coreName")
+          String coreName,
+      @Parameter(description = "The name to associate with the core 
snapshot.", required = true)
+          @PathParam("snapshotName")
+          String snapshotName,
+      @Parameter(description = "The id to associate with the async task.") 
@QueryParam("async")
+          String taskId)
+      throws Exception;
+
+  @GET
+  @Operation(
+      summary = "List existing snapshots for the specified core.",
+      tags = {"core-snapshots"})
+  ListCoreSnapshotsResponse listSnapshots(
+      @Parameter(
+              description = "The name of the core for which to retrieve 
snapshots.",
+              required = true)
+          @PathParam("coreName")
+          String coreName)
+      throws Exception;
+
+  @DELETE
+  @Path("/{snapshotName}")
+  @Operation(
+      summary = "Delete a single snapshot from the specified core.",
+      tags = {"core-snapshots"})
+  DeleteSnapshotResponse deleteSnapshot(
+      @Parameter(
+              description = "The name of the core for which to delete a 
snapshot.",
+              required = true)
+          @PathParam("coreName")
+          String coreName,
+      @Parameter(description = "The name of the core snapshot to delete.", 
required = true)
+          @PathParam("snapshotName")
+          String snapshotName,
+      @Parameter(description = "The id to associate with the async task.") 
@QueryParam("async")
+          String taskId)
+      throws Exception;
+}
diff --git 
a/solr/api/src/java/org/apache/solr/client/api/endpoint/MigrateReplicasApi.java 
b/solr/api/src/java/org/apache/solr/client/api/endpoint/MigrateReplicasApi.java
new file mode 100644
index 00000000000..82e8110b697
--- /dev/null
+++ 
b/solr/api/src/java/org/apache/solr/client/api/endpoint/MigrateReplicasApi.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.solr.client.api.endpoint;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.parameters.RequestBody;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import org.apache.solr.client.api.model.MigrateReplicasRequestBody;
+import org.apache.solr.client.api.model.SolrJerseyResponse;
+
+/** V2 API definition for migrating replicas from a set of nodes to another 
set of nodes. */
+@Path("cluster/replicas/migrate")
+public interface MigrateReplicasApi {
+  @POST
+  @Operation(
+      summary = "Migrate Replicas from a given set of nodes.",
+      tags = {"cluster"})
+  SolrJerseyResponse migrateReplicas(
+      @RequestBody(description = "Contains user provided parameters", required 
= true)
+          MigrateReplicasRequestBody requestBody)
+      throws Exception;
+}
diff --git 
a/solr/api/src/java/org/apache/solr/client/api/endpoint/ReloadCoreApi.java 
b/solr/api/src/java/org/apache/solr/client/api/endpoint/ReloadCoreApi.java
new file mode 100644
index 00000000000..8a7cced7aa1
--- /dev/null
+++ b/solr/api/src/java/org/apache/solr/client/api/endpoint/ReloadCoreApi.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.solr.client.api.endpoint;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.parameters.RequestBody;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import org.apache.solr.client.api.model.ReloadCoreRequestBody;
+import org.apache.solr.client.api.model.SolrJerseyResponse;
+
+/** V2 API definition for reloading an individual core. */
+@Path("/cores/{coreName}/reload")
+public interface ReloadCoreApi {
+
+  @POST
+  @Operation(
+      summary = "Reload the specified core.",
+      tags = {"cores"})
+  SolrJerseyResponse reloadCore(
+      @Parameter(description = "The name of the core to reload.", required = 
true)
+          @PathParam("coreName")
+          String coreName,
+      @Schema(description = "Additional parameters for reloading the core") 
@RequestBody
+          ReloadCoreRequestBody reloadCoreRequestBody)
+      throws Exception;
+}
diff --git 
a/solr/api/src/java/org/apache/solr/client/api/endpoint/RestoreCoreApi.java 
b/solr/api/src/java/org/apache/solr/client/api/endpoint/RestoreCoreApi.java
new file mode 100644
index 00000000000..3f2264a6562
--- /dev/null
+++ b/solr/api/src/java/org/apache/solr/client/api/endpoint/RestoreCoreApi.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.solr.client.api.endpoint;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import org.apache.solr.client.api.model.RestoreCoreRequestBody;
+import org.apache.solr.client.api.model.SolrJerseyResponse;
+
+/** V2 API definition for restoring a previously taken backup to a core */
+@Path("/cores/{coreName}/restore")
+public interface RestoreCoreApi {
+
+  @POST
+  @Operation(
+      summary = "Restore a previously-taken backup to the specified core",
+      tags = {"cores"})
+  SolrJerseyResponse restoreCore(
+      @Parameter(description = "The name of the core to be restored") 
@PathParam("coreName")
+          String coreName,
+      RestoreCoreRequestBody requestBody)
+      throws Exception;
+}
diff --git a/solr/api/src/java/org/apache/solr/client/api/model/Constants.java 
b/solr/api/src/java/org/apache/solr/client/api/model/Constants.java
index 0992c2c3a17..798e09d5e26 100644
--- a/solr/api/src/java/org/apache/solr/client/api/model/Constants.java
+++ b/solr/api/src/java/org/apache/solr/client/api/model/Constants.java
@@ -19,6 +19,9 @@ package org.apache.solr.client.api.model;
 
 public class Constants {
 
+  // TODO Most of these constants are also defined in various constant files 
in 'solrj' - we should
+  // move those into the 'api' module and eliminate this file.
+
   private Constants() {
     /* Private ctor prevents instantiation */
   }
@@ -51,4 +54,8 @@ public class Constants {
 
   /** The name of the config set to be used for a collection */
   public static final String COLL_CONF = "collection.configName";
+
+  public static final String SNAPSHOT_GENERATION_NUM = "generation";
+
+  public static final String SNAPSHOT_NAME = "snapshotName";
 }
diff --git 
a/solr/api/src/java/org/apache/solr/client/api/model/CreateCoreSnapshotResponse.java
 
b/solr/api/src/java/org/apache/solr/client/api/model/CreateCoreSnapshotResponse.java
new file mode 100644
index 00000000000..9f633a8830b
--- /dev/null
+++ 
b/solr/api/src/java/org/apache/solr/client/api/model/CreateCoreSnapshotResponse.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.solr.client.api.model;
+
+import static org.apache.solr.client.api.model.Constants.SNAPSHOT_NAME;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+import java.util.Collection;
+
+public class CreateCoreSnapshotResponse extends SolrJerseyResponse {
+  @Schema(description = "The name of the core.")
+  @JsonProperty
+  public String core;
+
+  @Schema(name = "commitName", description = "The name of the created 
snapshot.")
+  @JsonProperty(SNAPSHOT_NAME)
+  public String commitName;
+
+  @Schema(description = "The path to the directory containing the index 
files.")
+  @JsonProperty
+  public String indexDirPath;
+
+  @Schema(description = "The generation value for the created snapshot.")
+  @JsonProperty
+  public Long generation;
+
+  @Schema(description = "The list of index filenames contained within the 
created snapshot.")
+  @JsonProperty
+  public Collection<String> files;
+}
diff --git 
a/solr/api/src/java/org/apache/solr/client/api/model/DeleteSnapshotResponse.java
 
b/solr/api/src/java/org/apache/solr/client/api/model/DeleteSnapshotResponse.java
new file mode 100644
index 00000000000..970387edc5e
--- /dev/null
+++ 
b/solr/api/src/java/org/apache/solr/client/api/model/DeleteSnapshotResponse.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.solr.client.api.model;
+
+import static org.apache.solr.client.api.model.Constants.SNAPSHOT_NAME;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+
+public class DeleteSnapshotResponse extends SolrJerseyResponse {
+  @Schema(name = "coreName", description = "The name of the core.")
+  @JsonProperty("core")
+  public String coreName;
+
+  @Schema(name = "commitName", description = "The name of the deleted 
snapshot.")
+  @JsonProperty(SNAPSHOT_NAME)
+  public String commitName;
+}
diff --git 
a/solr/api/src/java/org/apache/solr/client/api/model/ListCoreSnapshotsResponse.java
 
b/solr/api/src/java/org/apache/solr/client/api/model/ListCoreSnapshotsResponse.java
new file mode 100644
index 00000000000..0e6d87709ea
--- /dev/null
+++ 
b/solr/api/src/java/org/apache/solr/client/api/model/ListCoreSnapshotsResponse.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.solr.client.api.model;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+import java.util.Map;
+
+public class ListCoreSnapshotsResponse extends SolrJerseyResponse {
+  @Schema(description = "The collection of snapshots found for the requested 
core.")
+  @JsonProperty
+  public Map<String, SnapshotInformation> snapshots;
+}
diff --git 
a/solr/api/src/java/org/apache/solr/client/api/model/MigrateReplicasRequestBody.java
 
b/solr/api/src/java/org/apache/solr/client/api/model/MigrateReplicasRequestBody.java
new file mode 100644
index 00000000000..8970d6b7c4d
--- /dev/null
+++ 
b/solr/api/src/java/org/apache/solr/client/api/model/MigrateReplicasRequestBody.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.solr.client.api.model;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+import java.util.Set;
+
+public class MigrateReplicasRequestBody {
+
+  public MigrateReplicasRequestBody() {}
+
+  public MigrateReplicasRequestBody(
+      Set<String> sourceNodes, Set<String> targetNodes, Boolean 
waitForFinalState, String async) {
+    this.sourceNodes = sourceNodes;
+    this.targetNodes = targetNodes;
+    this.waitForFinalState = waitForFinalState;
+    this.async = async;
+  }
+
+  @Schema(description = "The set of nodes which all replicas will be migrated 
off of.")
+  @JsonProperty(required = true)
+  public Set<String> sourceNodes;
+
+  @Schema(
+      description =
+          "A set of nodes to migrate the replicas to. If this is not provided, 
then the API will use the live data nodes not in 'sourceNodes'.")
+  @JsonProperty
+  public Set<String> targetNodes;
+
+  @Schema(
+      description =
+          "If true, the request will complete only when all affected replicas 
become active. "
+              + "If false, the API will return the status of the single 
action, which may be "
+              + "before the new replicas are online and active.")
+  @JsonProperty
+  public Boolean waitForFinalState = false;
+
+  @Schema(description = "Request ID to track this action which will be 
processed asynchronously.")
+  @JsonProperty
+  public String async;
+}
diff --git 
a/solr/api/src/java/org/apache/solr/client/api/model/ReloadCoreRequestBody.java 
b/solr/api/src/java/org/apache/solr/client/api/model/ReloadCoreRequestBody.java
new file mode 100644
index 00000000000..51b0d4bd761
--- /dev/null
+++ 
b/solr/api/src/java/org/apache/solr/client/api/model/ReloadCoreRequestBody.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.solr.client.api.model;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+
+public class ReloadCoreRequestBody {
+  @Schema(description = "Request ID to track this action which will be 
processed asynchronously.")
+  @JsonProperty
+  public String async;
+}
diff --git 
a/solr/api/src/java/org/apache/solr/client/api/model/RestoreCoreRequestBody.java
 
b/solr/api/src/java/org/apache/solr/client/api/model/RestoreCoreRequestBody.java
new file mode 100644
index 00000000000..ca2a9f5cf98
--- /dev/null
+++ 
b/solr/api/src/java/org/apache/solr/client/api/model/RestoreCoreRequestBody.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.solr.client.api.model;
+
+import static org.apache.solr.client.api.model.Constants.BACKUP_REPOSITORY;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+
+public class RestoreCoreRequestBody {
+  @JsonProperty public String name;
+
+  @JsonProperty public String shardBackupId;
+
+  @Schema(name = "backupRepository")
+  @JsonProperty(BACKUP_REPOSITORY)
+  public String backupRepository;
+
+  @JsonProperty public String location;
+
+  @JsonProperty public String async;
+}
diff --git 
a/solr/api/src/java/org/apache/solr/client/api/model/SnapshotInformation.java 
b/solr/api/src/java/org/apache/solr/client/api/model/SnapshotInformation.java
new file mode 100644
index 00000000000..31ec92d3045
--- /dev/null
+++ 
b/solr/api/src/java/org/apache/solr/client/api/model/SnapshotInformation.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.solr.client.api.model;
+
+import static 
org.apache.solr.client.api.model.Constants.SNAPSHOT_GENERATION_NUM;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+
+/**
+ * Contained in {@link ListCoreSnapshotsResponse}, this holds information for 
a given core's
+ * Snapshot
+ */
+public class SnapshotInformation {
+  @Schema(name = "generationNumber", description = "The generation value for 
the snapshot.")
+  @JsonProperty(SNAPSHOT_GENERATION_NUM)
+  public final long generationNumber;
+
+  @Schema(description = "The path to the directory containing the index 
files.")
+  @JsonProperty
+  public final String indexDirPath;
+
+  public SnapshotInformation(long generationNumber, String indexDirPath) {
+    this.generationNumber = generationNumber;
+    this.indexDirPath = indexDirPath;
+  }
+}
diff --git 
a/solr/api/src/java/org/apache/solr/client/api/model/UpdateCollectionPropertyRequestBody.java
 
b/solr/api/src/java/org/apache/solr/client/api/model/UpdateCollectionPropertyRequestBody.java
new file mode 100644
index 00000000000..73e6bd200dd
--- /dev/null
+++ 
b/solr/api/src/java/org/apache/solr/client/api/model/UpdateCollectionPropertyRequestBody.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.solr.client.api.model;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class UpdateCollectionPropertyRequestBody {
+  public UpdateCollectionPropertyRequestBody() {}
+
+  public UpdateCollectionPropertyRequestBody(String value) {
+    this.value = value;
+  }
+
+  @JsonProperty(required = true)
+  public String value;
+}
diff --git 
a/solr/core/src/java/org/apache/solr/core/snapshots/SolrSnapshotManager.java 
b/solr/core/src/java/org/apache/solr/core/snapshots/SolrSnapshotManager.java
index dd475b03e6e..acdbd722717 100644
--- a/solr/core/src/java/org/apache/solr/core/snapshots/SolrSnapshotManager.java
+++ b/solr/core/src/java/org/apache/solr/core/snapshots/SolrSnapshotManager.java
@@ -16,6 +16,8 @@
  */
 package org.apache.solr.core.snapshots;
 
+import static 
org.apache.solr.client.api.model.Constants.SNAPSHOT_GENERATION_NUM;
+
 import java.io.IOException;
 import java.lang.invoke.MethodHandles;
 import java.util.ArrayList;
@@ -49,7 +51,7 @@ public class SolrSnapshotManager {
   private static final Logger log = 
LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
 
   public static final String INDEX_DIR_PATH = "indexDirPath";
-  public static final String GENERATION_NUM = "generation";
+  public static final String GENERATION_NUM = SNAPSHOT_GENERATION_NUM;
   public static final String SNAPSHOT_STATUS = "status";
   public static final String CREATION_DATE = "creationDate";
   public static final String SNAPSHOT_REPLICAS = "replicas";
diff --git 
a/solr/core/src/java/org/apache/solr/handler/admin/CollectionsHandler.java 
b/solr/core/src/java/org/apache/solr/handler/admin/CollectionsHandler.java
index d91f4e1914b..0ed22f2f065 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/CollectionsHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/CollectionsHandler.java
@@ -127,6 +127,7 @@ import 
org.apache.solr.client.api.model.InstallShardDataRequestBody;
 import org.apache.solr.client.api.model.ReplaceNodeRequestBody;
 import org.apache.solr.client.api.model.SolrJerseyResponse;
 import org.apache.solr.client.api.model.UpdateAliasPropertiesRequestBody;
+import org.apache.solr.client.api.model.UpdateCollectionPropertyRequestBody;
 import org.apache.solr.client.solrj.SolrResponse;
 import org.apache.solr.client.solrj.request.CollectionAdminRequest;
 import org.apache.solr.client.solrj.response.RequestStatusState;
@@ -170,7 +171,7 @@ import org.apache.solr.handler.admin.api.AdminAPIBase;
 import org.apache.solr.handler.admin.api.AliasProperty;
 import org.apache.solr.handler.admin.api.BalanceReplicas;
 import org.apache.solr.handler.admin.api.BalanceShardUniqueAPI;
-import org.apache.solr.handler.admin.api.CollectionPropertyAPI;
+import org.apache.solr.handler.admin.api.CollectionProperty;
 import org.apache.solr.handler.admin.api.CollectionStatusAPI;
 import org.apache.solr.handler.admin.api.CreateAliasAPI;
 import org.apache.solr.handler.admin.api.CreateCollection;
@@ -193,7 +194,7 @@ import 
org.apache.solr.handler.admin.api.ListCollectionBackups;
 import org.apache.solr.handler.admin.api.ListCollectionSnapshotsAPI;
 import org.apache.solr.handler.admin.api.ListCollections;
 import org.apache.solr.handler.admin.api.MigrateDocsAPI;
-import org.apache.solr.handler.admin.api.MigrateReplicasAPI;
+import org.apache.solr.handler.admin.api.MigrateReplicas;
 import org.apache.solr.handler.admin.api.ModifyCollectionAPI;
 import org.apache.solr.handler.admin.api.MoveReplicaAPI;
 import org.apache.solr.handler.admin.api.RebalanceLeadersAPI;
@@ -786,14 +787,12 @@ public class CollectionsHandler extends 
RequestHandlerBase implements Permission
           final String propName = 
req.getParams().required().get(PROPERTY_NAME);
           final String val = req.getParams().get(PROPERTY_VALUE);
 
-          final CollectionPropertyAPI setCollPropApi =
-              new CollectionPropertyAPI(h.coreContainer, req, rsp);
+          final CollectionProperty setCollPropApi =
+              new CollectionProperty(h.coreContainer, req, rsp);
           final SolrJerseyResponse setPropRsp =
               (val != null)
                   ? setCollPropApi.createOrUpdateCollectionProperty(
-                      collection,
-                      propName,
-                      new 
CollectionPropertyAPI.UpdateCollectionPropertyRequestBody(val))
+                      collection, propName, new 
UpdateCollectionPropertyRequestBody(val))
                   : setCollPropApi.deleteCollectionProperty(collection, 
propName);
           V2ApiUtils.squashIntoSolrResponseWithoutHeader(rsp, setPropRsp);
           return null;
@@ -1381,11 +1380,11 @@ public class CollectionsHandler extends 
RequestHandlerBase implements Permission
         ReloadCollectionAPI.class,
         RenameCollection.class,
         ReplaceNode.class,
-        MigrateReplicasAPI.class,
+        MigrateReplicas.class,
         BalanceReplicas.class,
         RestoreCollectionAPI.class,
         SyncShard.class,
-        CollectionPropertyAPI.class,
+        CollectionProperty.class,
         DeleteNode.class,
         ListAliases.class,
         AliasProperty.class,
diff --git 
a/solr/core/src/java/org/apache/solr/handler/admin/CoreAdminHandler.java 
b/solr/core/src/java/org/apache/solr/handler/admin/CoreAdminHandler.java
index d9ece272f75..25af745db5b 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/CoreAdminHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/CoreAdminHandler.java
@@ -56,21 +56,21 @@ import org.apache.solr.core.CoreDescriptor;
 import org.apache.solr.handler.RequestHandlerBase;
 import org.apache.solr.handler.admin.api.AllCoresStatusAPI;
 import org.apache.solr.handler.admin.api.BackupCoreAPI;
-import org.apache.solr.handler.admin.api.CoreSnapshotAPI;
+import org.apache.solr.handler.admin.api.CoreSnapshot;
 import org.apache.solr.handler.admin.api.CreateCoreAPI;
 import org.apache.solr.handler.admin.api.InstallCoreData;
 import org.apache.solr.handler.admin.api.MergeIndexesAPI;
 import org.apache.solr.handler.admin.api.OverseerOperationAPI;
 import org.apache.solr.handler.admin.api.PrepareCoreRecoveryAPI;
 import org.apache.solr.handler.admin.api.RejoinLeaderElectionAPI;
-import org.apache.solr.handler.admin.api.ReloadCoreAPI;
+import org.apache.solr.handler.admin.api.ReloadCore;
 import org.apache.solr.handler.admin.api.RenameCoreAPI;
 import org.apache.solr.handler.admin.api.RequestApplyCoreUpdatesAPI;
 import org.apache.solr.handler.admin.api.RequestBufferUpdatesAPI;
 import org.apache.solr.handler.admin.api.RequestCoreCommandStatusAPI;
 import org.apache.solr.handler.admin.api.RequestCoreRecoveryAPI;
 import org.apache.solr.handler.admin.api.RequestSyncShardAPI;
-import org.apache.solr.handler.admin.api.RestoreCoreAPI;
+import org.apache.solr.handler.admin.api.RestoreCore;
 import org.apache.solr.handler.admin.api.SingleCoreStatusAPI;
 import org.apache.solr.handler.admin.api.SplitCoreAPI;
 import org.apache.solr.handler.admin.api.SwapCoresAPI;
@@ -402,11 +402,11 @@ public class CoreAdminHandler extends RequestHandlerBase 
implements PermissionNa
   @Override
   public Collection<Class<? extends JerseyResource>> getJerseyResources() {
     return List.of(
-        CoreSnapshotAPI.class,
+        CoreSnapshot.class,
         InstallCoreData.class,
         BackupCoreAPI.class,
-        RestoreCoreAPI.class,
-        ReloadCoreAPI.class);
+        RestoreCore.class,
+        ReloadCore.class);
   }
 
   public interface CoreAdminOp {
diff --git 
a/solr/core/src/java/org/apache/solr/handler/admin/CoreAdminOperation.java 
b/solr/core/src/java/org/apache/solr/handler/admin/CoreAdminOperation.java
index 987c79d376b..5c95a20a36f 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/CoreAdminOperation.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/CoreAdminOperation.java
@@ -58,6 +58,8 @@ import java.lang.invoke.MethodHandles;
 import java.nio.file.Path;
 import java.util.Locale;
 import java.util.Map;
+import org.apache.solr.client.api.model.ListCoreSnapshotsResponse;
+import org.apache.solr.client.api.model.ReloadCoreRequestBody;
 import org.apache.solr.client.api.model.SolrJerseyResponse;
 import org.apache.solr.cloud.ZkController;
 import org.apache.solr.common.SolrException;
@@ -72,8 +74,8 @@ import org.apache.solr.core.CoreContainer;
 import org.apache.solr.core.CoreDescriptor;
 import org.apache.solr.core.SolrCore;
 import org.apache.solr.handler.admin.CoreAdminHandler.CoreAdminOp;
-import org.apache.solr.handler.admin.api.CoreSnapshotAPI;
-import org.apache.solr.handler.admin.api.ReloadCoreAPI;
+import org.apache.solr.handler.admin.api.CoreSnapshot;
+import org.apache.solr.handler.admin.api.ReloadCore;
 import org.apache.solr.handler.api.V2ApiUtils;
 import org.apache.solr.search.SolrIndexSearcher;
 import org.apache.solr.update.UpdateLog;
@@ -135,11 +137,10 @@ public enum CoreAdminOperation implements CoreAdminOp {
         SolrParams params = it.req.getParams();
         String cname = params.required().get(CoreAdminParams.CORE);
 
-        ReloadCoreAPI reloadCoreAPI =
-            new ReloadCoreAPI(
+        ReloadCore reloadCoreAPI =
+            new ReloadCore(
                 it.req, it.rsp, it.handler.coreContainer, 
it.handler.getCoreAdminAsyncTracker());
-        ReloadCoreAPI.ReloadCoreRequestBody reloadCoreRequestBody =
-            new ReloadCoreAPI.ReloadCoreRequestBody();
+        ReloadCoreRequestBody reloadCoreRequestBody = new 
ReloadCoreRequestBody();
         SolrJerseyResponse response = reloadCoreAPI.reloadCore(cname, 
reloadCoreRequestBody);
         V2ApiUtils.squashIntoSolrResponseWithoutHeader(it.rsp, response);
       }),
@@ -293,12 +294,10 @@ public enum CoreAdminOperation implements CoreAdminOp {
         final String coreName = params.required().get(CoreAdminParams.CORE);
 
         final CoreContainer coreContainer = it.handler.getCoreContainer();
-        final CoreSnapshotAPI coreSnapshotAPI =
-            new CoreSnapshotAPI(
-                it.req, it.rsp, coreContainer, 
it.handler.getCoreAdminAsyncTracker());
+        final CoreSnapshot coreSnapshotAPI =
+            new CoreSnapshot(it.req, it.rsp, coreContainer, 
it.handler.getCoreAdminAsyncTracker());
 
-        final CoreSnapshotAPI.ListSnapshotsResponse response =
-            coreSnapshotAPI.listSnapshots(coreName);
+        final ListCoreSnapshotsResponse response = 
coreSnapshotAPI.listSnapshots(coreName);
 
         V2ApiUtils.squashIntoSolrResponseWithoutHeader(it.rsp, response);
       });
diff --git 
a/solr/core/src/java/org/apache/solr/handler/admin/CreateSnapshotOp.java 
b/solr/core/src/java/org/apache/solr/handler/admin/CreateSnapshotOp.java
index 6b27293c47f..4dea78c92b2 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/CreateSnapshotOp.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/CreateSnapshotOp.java
@@ -17,10 +17,11 @@
 
 package org.apache.solr.handler.admin;
 
+import org.apache.solr.client.api.model.CreateCoreSnapshotResponse;
 import org.apache.solr.common.params.CoreAdminParams;
 import org.apache.solr.common.params.SolrParams;
 import org.apache.solr.core.CoreContainer;
-import org.apache.solr.handler.admin.api.CoreSnapshotAPI;
+import org.apache.solr.handler.admin.api.CoreSnapshot;
 import org.apache.solr.handler.api.V2ApiUtils;
 
 class CreateSnapshotOp implements CoreAdminHandler.CoreAdminOp {
@@ -31,10 +32,10 @@ class CreateSnapshotOp implements 
CoreAdminHandler.CoreAdminOp {
     final String commitName = 
params.required().get(CoreAdminParams.COMMIT_NAME);
 
     final CoreContainer coreContainer = it.handler.getCoreContainer();
-    final CoreSnapshotAPI coreSnapshotAPI =
-        new CoreSnapshotAPI(it.req, it.rsp, coreContainer, 
it.handler.getCoreAdminAsyncTracker());
+    final CoreSnapshot coreSnapshotAPI =
+        new CoreSnapshot(it.req, it.rsp, coreContainer, 
it.handler.getCoreAdminAsyncTracker());
 
-    final CoreSnapshotAPI.CreateSnapshotResponse response =
+    final CreateCoreSnapshotResponse response =
         coreSnapshotAPI.createSnapshot(coreName, commitName, null);
 
     V2ApiUtils.squashIntoSolrResponseWithoutHeader(it.rsp, response);
diff --git 
a/solr/core/src/java/org/apache/solr/handler/admin/DeleteSnapshotOp.java 
b/solr/core/src/java/org/apache/solr/handler/admin/DeleteSnapshotOp.java
index a13bd6d5163..d49863ff7a4 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/DeleteSnapshotOp.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/DeleteSnapshotOp.java
@@ -17,10 +17,11 @@
 
 package org.apache.solr.handler.admin;
 
+import org.apache.solr.client.api.model.DeleteSnapshotResponse;
 import org.apache.solr.common.params.CoreAdminParams;
 import org.apache.solr.common.params.SolrParams;
 import org.apache.solr.core.CoreContainer;
-import org.apache.solr.handler.admin.api.CoreSnapshotAPI;
+import org.apache.solr.handler.admin.api.CoreSnapshot;
 import org.apache.solr.handler.api.V2ApiUtils;
 
 class DeleteSnapshotOp implements CoreAdminHandler.CoreAdminOp {
@@ -32,10 +33,10 @@ class DeleteSnapshotOp implements 
CoreAdminHandler.CoreAdminOp {
     final String coreName = params.required().get(CoreAdminParams.CORE);
 
     final CoreContainer coreContainer = it.handler.getCoreContainer();
-    final CoreSnapshotAPI coreSnapshotAPI =
-        new CoreSnapshotAPI(it.req, it.rsp, coreContainer, 
it.handler.getCoreAdminAsyncTracker());
+    final CoreSnapshot coreSnapshotAPI =
+        new CoreSnapshot(it.req, it.rsp, coreContainer, 
it.handler.getCoreAdminAsyncTracker());
 
-    final CoreSnapshotAPI.DeleteSnapshotResponse response =
+    final DeleteSnapshotResponse response =
         coreSnapshotAPI.deleteSnapshot(coreName, commitName, null);
 
     V2ApiUtils.squashIntoSolrResponseWithoutHeader(it.rsp, response);
diff --git 
a/solr/core/src/java/org/apache/solr/handler/admin/RestoreCoreOp.java 
b/solr/core/src/java/org/apache/solr/handler/admin/RestoreCoreOp.java
index c95af84d3d7..1bfc32f56e0 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/RestoreCoreOp.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/RestoreCoreOp.java
@@ -17,10 +17,11 @@
 
 package org.apache.solr.handler.admin;
 
+import org.apache.solr.client.api.model.RestoreCoreRequestBody;
 import org.apache.solr.common.params.CoreAdminParams;
 import org.apache.solr.common.params.SolrParams;
 import org.apache.solr.core.CoreContainer;
-import org.apache.solr.handler.admin.api.RestoreCoreAPI;
+import org.apache.solr.handler.admin.api.RestoreCore;
 import org.apache.solr.handler.api.V2ApiUtils;
 
 class RestoreCoreOp implements CoreAdminHandler.CoreAdminOp {
@@ -34,23 +35,23 @@ class RestoreCoreOp implements CoreAdminHandler.CoreAdminOp 
{
   public void execute(CoreAdminHandler.CallInfo it) throws Exception {
     final SolrParams params = it.req.getParams();
     String cname = params.required().get(CoreAdminParams.CORE);
-    final var requestBody = new RestoreCoreAPI.RestoreCoreRequestBody();
+    final var requestBody = new RestoreCoreRequestBody();
     // "async" param intentionally omitted because CoreAdminHandler has 
already processed
     requestBody.name = params.get(CoreAdminParams.NAME);
     requestBody.shardBackupId = params.get(CoreAdminParams.SHARD_BACKUP_ID);
     requestBody.location = params.get(CoreAdminParams.BACKUP_LOCATION);
     requestBody.backupRepository = 
params.get(CoreAdminParams.BACKUP_REPOSITORY);
-    requestBody.validate();
+    RestoreCore.validateRequestBody(requestBody);
 
     final CoreContainer coreContainer = it.handler.getCoreContainer();
     final var api =
-        new RestoreCoreAPI(coreContainer, it.req, it.rsp, 
it.handler.getCoreAdminAsyncTracker());
+        new RestoreCore(coreContainer, it.req, it.rsp, 
it.handler.getCoreAdminAsyncTracker());
     final var response = api.restoreCore(cname, requestBody);
     V2ApiUtils.squashIntoSolrResponseWithoutHeader(it.rsp, response);
   }
 
-  public static RestoreCoreAPI.RestoreCoreRequestBody 
createRequestFromV1Params(SolrParams params) {
-    final var requestBody = new RestoreCoreAPI.RestoreCoreRequestBody();
+  public static RestoreCoreRequestBody createRequestFromV1Params(SolrParams 
params) {
+    final var requestBody = new RestoreCoreRequestBody();
     // "async" param intentionally omitted because CoreAdminHandler has 
already processed
     requestBody.name = params.get(CoreAdminParams.NAME);
     requestBody.shardBackupId = params.get(CoreAdminParams.SHARD_BACKUP_ID);
diff --git 
a/solr/core/src/java/org/apache/solr/handler/admin/api/CollectionPropertyAPI.java
 b/solr/core/src/java/org/apache/solr/handler/admin/api/CollectionProperty.java
similarity index 68%
rename from 
solr/core/src/java/org/apache/solr/handler/admin/api/CollectionPropertyAPI.java
rename to 
solr/core/src/java/org/apache/solr/handler/admin/api/CollectionProperty.java
index a50e2895d52..55da1ea0d53 100644
--- 
a/solr/core/src/java/org/apache/solr/handler/admin/api/CollectionPropertyAPI.java
+++ 
b/solr/core/src/java/org/apache/solr/handler/admin/api/CollectionProperty.java
@@ -17,49 +17,38 @@
 
 package org.apache.solr.handler.admin.api;
 
-import static 
org.apache.solr.client.solrj.impl.BinaryResponseParser.BINARY_CONTENT_TYPE_V2;
 import static 
org.apache.solr.security.PermissionNameProvider.Name.COLL_EDIT_PERM;
 
-import com.fasterxml.jackson.annotation.JsonProperty;
 import java.io.IOException;
-import javax.ws.rs.DELETE;
-import javax.ws.rs.PUT;
-import javax.ws.rs.Path;
-import javax.ws.rs.PathParam;
-import javax.ws.rs.Produces;
-import javax.ws.rs.core.MediaType;
+import org.apache.solr.client.api.endpoint.CollectionPropertyApi;
 import org.apache.solr.client.api.model.SolrJerseyResponse;
+import org.apache.solr.client.api.model.UpdateCollectionPropertyRequestBody;
 import org.apache.solr.common.SolrException;
 import org.apache.solr.common.cloud.CollectionProperties;
 import org.apache.solr.core.CoreContainer;
-import org.apache.solr.jersey.JacksonReflectMapWriter;
 import org.apache.solr.jersey.PermissionName;
 import org.apache.solr.request.SolrQueryRequest;
 import org.apache.solr.response.SolrQueryResponse;
 
 /**
- * V2 API for modifying collection-level properties.
+ * V2 API implementations for modifying collection-level properties.
  *
  * <p>These APIs (PUT and DELETE 
/api/collections/collName/properties/propName) are analogous to the
  * v1 /admin/collections?action=COLLECTIONPROP command.
  */
-@Path("/collections/{collName}/properties/{propName}")
-public class CollectionPropertyAPI extends AdminAPIBase {
+public class CollectionProperty extends AdminAPIBase implements 
CollectionPropertyApi {
 
-  public CollectionPropertyAPI(
+  public CollectionProperty(
       CoreContainer coreContainer,
       SolrQueryRequest solrQueryRequest,
       SolrQueryResponse solrQueryResponse) {
     super(coreContainer, solrQueryRequest, solrQueryResponse);
   }
 
-  @PUT
+  @Override
   @PermissionName(COLL_EDIT_PERM)
-  @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, 
BINARY_CONTENT_TYPE_V2})
   public SolrJerseyResponse createOrUpdateCollectionProperty(
-      @PathParam("collName") String collName,
-      @PathParam("propName") String propName,
-      UpdateCollectionPropertyRequestBody requestBody)
+      String collName, String propName, UpdateCollectionPropertyRequestBody 
requestBody)
       throws Exception {
     if (requestBody == null) {
       throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Missing 
required request body");
@@ -70,11 +59,9 @@ public class CollectionPropertyAPI extends AdminAPIBase {
     return response;
   }
 
-  @DELETE
+  @Override
   @PermissionName(COLL_EDIT_PERM)
-  @Produces({"application/json", "application/xml", BINARY_CONTENT_TYPE_V2})
-  public SolrJerseyResponse deleteCollectionProperty(
-      @PathParam("collName") String collName, @PathParam("propName") String 
propName)
+  public SolrJerseyResponse deleteCollectionProperty(String collName, String 
propName)
       throws Exception {
     final SolrJerseyResponse response = 
instantiateJerseyResponse(SolrJerseyResponse.class);
     recordCollectionForLogAndTracing(collName, solrQueryRequest);
@@ -90,15 +77,4 @@ public class CollectionPropertyAPI extends AdminAPIBase {
         new 
CollectionProperties(coreContainer.getZkController().getZkClient());
     cp.setCollectionProperty(resolvedCollection, propertyName, propertyValue);
   }
-
-  public static class UpdateCollectionPropertyRequestBody implements 
JacksonReflectMapWriter {
-    public UpdateCollectionPropertyRequestBody() {}
-
-    public UpdateCollectionPropertyRequestBody(String value) {
-      this.value = value;
-    }
-
-    @JsonProperty(required = true)
-    public String value;
-  }
 }
diff --git 
a/solr/core/src/java/org/apache/solr/handler/admin/api/CoreSnapshotAPI.java 
b/solr/core/src/java/org/apache/solr/handler/admin/api/CoreSnapshot.java
similarity index 54%
rename from 
solr/core/src/java/org/apache/solr/handler/admin/api/CoreSnapshotAPI.java
rename to solr/core/src/java/org/apache/solr/handler/admin/api/CoreSnapshot.java
index 634c1f105b9..d043894d128 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/api/CoreSnapshotAPI.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/api/CoreSnapshot.java
@@ -16,47 +16,35 @@
  */
 package org.apache.solr.handler.admin.api;
 
-import static 
org.apache.solr.client.solrj.impl.BinaryResponseParser.BINARY_CONTENT_TYPE_V2;
 import static 
org.apache.solr.security.PermissionNameProvider.Name.CORE_EDIT_PERM;
 import static 
org.apache.solr.security.PermissionNameProvider.Name.CORE_READ_PERM;
 
-import com.fasterxml.jackson.annotation.JsonProperty;
-import io.swagger.v3.oas.annotations.Parameter;
-import io.swagger.v3.oas.annotations.media.Schema;
 import java.io.IOException;
-import java.util.Collection;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Optional;
 import javax.inject.Inject;
-import javax.ws.rs.DELETE;
-import javax.ws.rs.GET;
-import javax.ws.rs.POST;
-import javax.ws.rs.Path;
-import javax.ws.rs.PathParam;
-import javax.ws.rs.Produces;
-import javax.ws.rs.QueryParam;
 import org.apache.lucene.index.IndexCommit;
-import org.apache.solr.client.api.model.SolrJerseyResponse;
+import org.apache.solr.client.api.endpoint.CoreSnapshotApi;
+import org.apache.solr.client.api.model.CreateCoreSnapshotResponse;
+import org.apache.solr.client.api.model.DeleteSnapshotResponse;
+import org.apache.solr.client.api.model.ListCoreSnapshotsResponse;
+import org.apache.solr.client.api.model.SnapshotInformation;
 import org.apache.solr.common.SolrException;
-import org.apache.solr.common.params.CoreAdminParams;
 import org.apache.solr.core.CoreContainer;
 import org.apache.solr.core.IndexDeletionPolicyWrapper;
 import org.apache.solr.core.SolrCore;
-import org.apache.solr.core.snapshots.SolrSnapshotManager;
 import org.apache.solr.core.snapshots.SolrSnapshotMetaDataManager;
 import org.apache.solr.handler.admin.CoreAdminHandler;
-import org.apache.solr.jersey.JacksonReflectMapWriter;
 import org.apache.solr.jersey.PermissionName;
 import org.apache.solr.request.SolrQueryRequest;
 import org.apache.solr.response.SolrQueryResponse;
 
 /** V2 API for Creating, Listing, and Deleting Core Snapshots. */
-@Path("/cores/{coreName}/snapshots")
-public class CoreSnapshotAPI extends CoreAdminAPIBase {
+public class CoreSnapshot extends CoreAdminAPIBase implements CoreSnapshotApi {
 
   @Inject
-  public CoreSnapshotAPI(
+  public CoreSnapshot(
       SolrQueryRequest request,
       SolrQueryResponse response,
       CoreContainer coreContainer,
@@ -65,21 +53,12 @@ public class CoreSnapshotAPI extends CoreAdminAPIBase {
   }
 
   /** This API is analogous to V1 (POST 
/solr/admin/cores?action=CREATESNAPSHOT) */
-  @POST
-  @Path("/{snapshotName}")
-  @Produces({"application/json", "application/xml", BINARY_CONTENT_TYPE_V2})
+  @Override
   @PermissionName(CORE_EDIT_PERM)
-  public CreateSnapshotResponse createSnapshot(
-      @Parameter(description = "The name of the core to snapshot.", required = 
true)
-          @PathParam("coreName")
-          String coreName,
-      @Parameter(description = "The name to associate with the core 
snapshot.", required = true)
-          @PathParam("snapshotName")
-          String snapshotName,
-      @Parameter(description = "The id to associate with the async task.") 
@QueryParam("async")
-          String taskId)
-      throws Exception {
-    final CreateSnapshotResponse response = 
instantiateJerseyResponse(CreateSnapshotResponse.class);
+  public CreateCoreSnapshotResponse createSnapshot(
+      String coreName, String snapshotName, String taskId) throws Exception {
+    final CreateCoreSnapshotResponse response =
+        instantiateJerseyResponse(CreateCoreSnapshotResponse.class);
 
     return handlePotentiallyAsynchronousTask(
         response,
@@ -121,41 +100,12 @@ public class CoreSnapshotAPI extends CoreAdminAPIBase {
         });
   }
 
-  /** The Response for {@link CoreSnapshotAPI}'s {@link 
#createSnapshot(String, String, String)} */
-  public static class CreateSnapshotResponse extends SolrJerseyResponse {
-    @Schema(description = "The name of the core.")
-    @JsonProperty(CoreAdminParams.CORE)
-    public String core;
-
-    @Schema(description = "The name of the created snapshot.")
-    @JsonProperty(CoreAdminParams.SNAPSHOT_NAME)
-    public String commitName;
-
-    @Schema(description = "The path to the directory containing the index 
files.")
-    @JsonProperty(SolrSnapshotManager.INDEX_DIR_PATH)
-    public String indexDirPath;
-
-    @Schema(description = "The generation value for the created snapshot.")
-    @JsonProperty(SolrSnapshotManager.GENERATION_NUM)
-    public Long generation;
-
-    @Schema(description = "The list of index filenames contained within the 
created snapshot.")
-    @JsonProperty(SolrSnapshotManager.FILE_LIST)
-    public Collection<String> files;
-  }
-
   /** This API is analogous to V1 (GET /solr/admin/cores?action=LISTSNAPSHOTS) 
*/
-  @GET
-  @Produces({"application/json", "application/xml", BINARY_CONTENT_TYPE_V2})
+  @Override
   @PermissionName(CORE_READ_PERM)
-  public ListSnapshotsResponse listSnapshots(
-      @Parameter(
-              description = "The name of the core for which to retrieve 
snapshots.",
-              required = true)
-          @PathParam("coreName")
-          String coreName)
-      throws Exception {
-    final ListSnapshotsResponse response = 
instantiateJerseyResponse(ListSnapshotsResponse.class);
+  public ListCoreSnapshotsResponse listSnapshots(String coreName) throws 
Exception {
+    final ListCoreSnapshotsResponse response =
+        instantiateJerseyResponse(ListCoreSnapshotsResponse.class);
 
     return handlePotentiallyAsynchronousTask(
         response,
@@ -190,47 +140,10 @@ public class CoreSnapshotAPI extends CoreAdminAPIBase {
         });
   }
 
-  /** The Response for {@link CoreSnapshotAPI}'s {@link 
#listSnapshots(String)} */
-  public static class ListSnapshotsResponse extends SolrJerseyResponse {
-    @Schema(description = "The collection of snapshots found for the requested 
core.")
-    @JsonProperty(SolrSnapshotManager.SNAPSHOTS_INFO)
-    public Map<String, SnapshotInformation> snapshots;
-  }
-
-  /**
-   * Contained in {@link ListSnapshotsResponse}, this holds information for a 
given core's Snapshot
-   */
-  public static class SnapshotInformation implements JacksonReflectMapWriter {
-    @Schema(description = "The generation value for the snapshot.")
-    @JsonProperty(SolrSnapshotManager.GENERATION_NUM)
-    public final long generationNumber;
-
-    @Schema(description = "The path to the directory containing the index 
files.")
-    @JsonProperty(SolrSnapshotManager.INDEX_DIR_PATH)
-    public final String indexDirPath;
-
-    public SnapshotInformation(long generationNumber, String indexDirPath) {
-      this.generationNumber = generationNumber;
-      this.indexDirPath = indexDirPath;
-    }
-  }
-
   /** This API is analogous to V1 (DELETE 
/solr/admin/cores?action=DELETESNAPSHOT) */
-  @DELETE
-  @Path("/{snapshotName}")
-  @Produces({"application/json", "application/xml", BINARY_CONTENT_TYPE_V2})
+  @Override
   @PermissionName(CORE_EDIT_PERM)
-  public DeleteSnapshotResponse deleteSnapshot(
-      @Parameter(
-              description = "The name of the core for which to delete a 
snapshot.",
-              required = true)
-          @PathParam("coreName")
-          String coreName,
-      @Parameter(description = "The name of the core snapshot to delete.", 
required = true)
-          @PathParam("snapshotName")
-          String snapshotName,
-      @Parameter(description = "The id to associate with the async task.") 
@QueryParam("async")
-          String taskId)
+  public DeleteSnapshotResponse deleteSnapshot(String coreName, String 
snapshotName, String taskId)
       throws Exception {
     final DeleteSnapshotResponse response = 
instantiateJerseyResponse(DeleteSnapshotResponse.class);
 
@@ -264,15 +177,4 @@ public class CoreSnapshotAPI extends CoreAdminAPIBase {
           return response;
         });
   }
-
-  /** The Response for {@link CoreSnapshotAPI}'s {@link 
#deleteSnapshot(String, String, String)} */
-  public static class DeleteSnapshotResponse extends SolrJerseyResponse {
-    @Schema(description = "The name of the core.")
-    @JsonProperty(CoreAdminParams.CORE)
-    public String coreName;
-
-    @Schema(description = "The name of the deleted snapshot.")
-    @JsonProperty(CoreAdminParams.SNAPSHOT_NAME)
-    public String commitName;
-  }
 }
diff --git 
a/solr/core/src/java/org/apache/solr/handler/admin/api/MigrateReplicasAPI.java 
b/solr/core/src/java/org/apache/solr/handler/admin/api/MigrateReplicas.java
similarity index 64%
rename from 
solr/core/src/java/org/apache/solr/handler/admin/api/MigrateReplicasAPI.java
rename to 
solr/core/src/java/org/apache/solr/handler/admin/api/MigrateReplicas.java
index 6f353707504..4a1e3b657b8 100644
--- 
a/solr/core/src/java/org/apache/solr/handler/admin/api/MigrateReplicasAPI.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/api/MigrateReplicas.java
@@ -16,7 +16,6 @@
  */
 package org.apache.solr.handler.admin.api;
 
-import static 
org.apache.solr.client.solrj.impl.BinaryResponseParser.BINARY_CONTENT_TYPE_V2;
 import static org.apache.solr.cloud.Overseer.QUEUE_OPERATION;
 import static org.apache.solr.common.params.CollectionParams.SOURCE_NODES;
 import static org.apache.solr.common.params.CollectionParams.TARGET_NODES;
@@ -25,17 +24,11 @@ import static 
org.apache.solr.common.params.CommonAdminParams.WAIT_FOR_FINAL_STA
 import static 
org.apache.solr.handler.admin.CollectionsHandler.DEFAULT_COLLECTION_OP_TIMEOUT;
 import static 
org.apache.solr.security.PermissionNameProvider.Name.COLL_EDIT_PERM;
 
-import com.fasterxml.jackson.annotation.JsonProperty;
-import io.swagger.v3.oas.annotations.Operation;
-import io.swagger.v3.oas.annotations.media.Schema;
-import io.swagger.v3.oas.annotations.parameters.RequestBody;
 import java.util.HashMap;
 import java.util.Map;
-import java.util.Set;
 import javax.inject.Inject;
-import javax.ws.rs.POST;
-import javax.ws.rs.Path;
-import javax.ws.rs.Produces;
+import org.apache.solr.client.api.endpoint.MigrateReplicasApi;
+import org.apache.solr.client.api.model.MigrateReplicasRequestBody;
 import org.apache.solr.client.api.model.SolrJerseyResponse;
 import org.apache.solr.client.solrj.SolrResponse;
 import org.apache.solr.common.SolrException;
@@ -43,30 +36,24 @@ import org.apache.solr.common.cloud.ZkNodeProps;
 import org.apache.solr.common.params.CollectionParams.CollectionAction;
 import org.apache.solr.core.CoreContainer;
 import org.apache.solr.handler.admin.CollectionsHandler;
-import org.apache.solr.jersey.JacksonReflectMapWriter;
 import org.apache.solr.jersey.PermissionName;
 import org.apache.solr.request.SolrQueryRequest;
 import org.apache.solr.response.SolrQueryResponse;
 
 /** V2 API for migrating replicas from a set of nodes to another set of nodes. 
*/
-@Path("cluster/replicas/migrate")
-public class MigrateReplicasAPI extends AdminAPIBase {
+public class MigrateReplicas extends AdminAPIBase implements 
MigrateReplicasApi {
 
   @Inject
-  public MigrateReplicasAPI(
+  public MigrateReplicas(
       CoreContainer coreContainer,
       SolrQueryRequest solrQueryRequest,
       SolrQueryResponse solrQueryResponse) {
     super(coreContainer, solrQueryRequest, solrQueryResponse);
   }
 
-  @POST
-  @Produces({"application/json", "application/xml", BINARY_CONTENT_TYPE_V2})
+  @Override
   @PermissionName(COLL_EDIT_PERM)
-  @Operation(summary = "Migrate Replicas from a given set of nodes.")
-  public SolrJerseyResponse migrateReplicas(
-      @RequestBody(description = "Contains user provided parameters", required 
= true)
-          MigrateReplicasRequestBody requestBody)
+  public SolrJerseyResponse migrateReplicas(MigrateReplicasRequestBody 
requestBody)
       throws Exception {
     final SolrJerseyResponse response = 
instantiateJerseyResponse(SolrJerseyResponse.class);
     final CoreContainer coreContainer = 
fetchAndValidateZooKeeperAwareCoreContainer();
@@ -108,39 +95,4 @@ public class MigrateReplicasAPI extends AdminAPIBase {
 
     return new ZkNodeProps(remoteMessage);
   }
-
-  public static class MigrateReplicasRequestBody implements 
JacksonReflectMapWriter {
-
-    public MigrateReplicasRequestBody() {}
-
-    public MigrateReplicasRequestBody(
-        Set<String> sourceNodes, Set<String> targetNodes, Boolean 
waitForFinalState, String async) {
-      this.sourceNodes = sourceNodes;
-      this.targetNodes = targetNodes;
-      this.waitForFinalState = waitForFinalState;
-      this.async = async;
-    }
-
-    @Schema(description = "The set of nodes which all replicas will be 
migrated off of.")
-    @JsonProperty(value = "sourceNodes", required = true)
-    public Set<String> sourceNodes;
-
-    @Schema(
-        description =
-            "A set of nodes to migrate the replicas to. If this is not 
provided, then the API will use the live data nodes not in 'sourceNodes'.")
-    @JsonProperty(value = "targetNodes")
-    public Set<String> targetNodes;
-
-    @Schema(
-        description =
-            "If true, the request will complete only when all affected 
replicas become active. "
-                + "If false, the API will return the status of the single 
action, which may be "
-                + "before the new replicas are online and active.")
-    @JsonProperty("waitForFinalState")
-    public Boolean waitForFinalState = false;
-
-    @Schema(description = "Request ID to track this action which will be 
processed asynchronously.")
-    @JsonProperty("async")
-    public String async;
-  }
 }
diff --git 
a/solr/core/src/java/org/apache/solr/handler/admin/api/ReloadCoreAPI.java 
b/solr/core/src/java/org/apache/solr/handler/admin/api/ReloadCore.java
similarity index 62%
rename from 
solr/core/src/java/org/apache/solr/handler/admin/api/ReloadCoreAPI.java
rename to solr/core/src/java/org/apache/solr/handler/admin/api/ReloadCore.java
index b0252b83fa9..2210d43add1 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/api/ReloadCoreAPI.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/api/ReloadCore.java
@@ -17,37 +17,28 @@
 
 package org.apache.solr.handler.admin.api;
 
-import static 
org.apache.solr.client.solrj.impl.BinaryResponseParser.BINARY_CONTENT_TYPE_V2;
 import static 
org.apache.solr.security.PermissionNameProvider.Name.CORE_EDIT_PERM;
 
-import com.fasterxml.jackson.annotation.JsonProperty;
-import io.swagger.v3.oas.annotations.Parameter;
-import io.swagger.v3.oas.annotations.media.Schema;
-import io.swagger.v3.oas.annotations.parameters.RequestBody;
 import javax.inject.Inject;
-import javax.ws.rs.POST;
-import javax.ws.rs.Path;
-import javax.ws.rs.PathParam;
-import javax.ws.rs.Produces;
+import org.apache.solr.client.api.endpoint.ReloadCoreApi;
+import org.apache.solr.client.api.model.ReloadCoreRequestBody;
 import org.apache.solr.client.api.model.SolrJerseyResponse;
 import org.apache.solr.core.CoreContainer;
 import org.apache.solr.handler.admin.CoreAdminHandler;
-import org.apache.solr.jersey.JacksonReflectMapWriter;
 import org.apache.solr.jersey.PermissionName;
 import org.apache.solr.request.SolrQueryRequest;
 import org.apache.solr.response.SolrQueryResponse;
 
 /**
- * V2 API for reloading an individual core.
+ * V2 API implementation for reloading an individual core.
  *
  * <p>The new API (POST /v2/cores/coreName/reload is analogous to the v1 
/admin/cores?action=RELOAD
  * command.
  */
-@Path("/cores/{coreName}/reload")
-public class ReloadCoreAPI extends CoreAdminAPIBase {
+public class ReloadCore extends CoreAdminAPIBase implements ReloadCoreApi {
 
   @Inject
-  public ReloadCoreAPI(
+  public ReloadCore(
       SolrQueryRequest solrQueryRequest,
       SolrQueryResponse solrQueryResponse,
       CoreContainer coreContainer,
@@ -55,15 +46,9 @@ public class ReloadCoreAPI extends CoreAdminAPIBase {
     super(coreContainer, coreAdminAsyncTracker, solrQueryRequest, 
solrQueryResponse);
   }
 
-  @POST
-  @Produces({"application/json", "application/xml", BINARY_CONTENT_TYPE_V2})
+  @Override
   @PermissionName(CORE_EDIT_PERM)
-  public SolrJerseyResponse reloadCore(
-      @Parameter(description = "The name of the core to reload.", required = 
true)
-          @PathParam("coreName")
-          String coreName,
-      @Schema(description = "Additional parameters for reloading the core") 
@RequestBody
-          ReloadCoreAPI.ReloadCoreRequestBody reloadCoreRequestBody)
+  public SolrJerseyResponse reloadCore(String coreName, ReloadCoreRequestBody 
reloadCoreRequestBody)
       throws Exception {
     SolrJerseyResponse solrJerseyResponse = 
instantiateJerseyResponse(SolrJerseyResponse.class);
     return handlePotentiallyAsynchronousTask(
@@ -76,10 +61,4 @@ public class ReloadCoreAPI extends CoreAdminAPIBase {
           return solrJerseyResponse;
         });
   }
-
-  public static class ReloadCoreRequestBody implements JacksonReflectMapWriter 
{
-    @Schema(description = "Request ID to track this action which will be 
processed asynchronously.")
-    @JsonProperty("async")
-    public String async;
-  }
 }
diff --git 
a/solr/core/src/java/org/apache/solr/handler/admin/api/RestoreCoreAPI.java 
b/solr/core/src/java/org/apache/solr/handler/admin/api/RestoreCore.java
similarity index 73%
rename from 
solr/core/src/java/org/apache/solr/handler/admin/api/RestoreCoreAPI.java
rename to solr/core/src/java/org/apache/solr/handler/admin/api/RestoreCore.java
index 7e8ccf29862..9cfbdf64460 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/api/RestoreCoreAPI.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/api/RestoreCore.java
@@ -16,44 +16,35 @@
  */
 package org.apache.solr.handler.admin.api;
 
-import static 
org.apache.solr.client.solrj.impl.BinaryResponseParser.BINARY_CONTENT_TYPE_V2;
 import static 
org.apache.solr.security.PermissionNameProvider.Name.CORE_EDIT_PERM;
 
-import com.fasterxml.jackson.annotation.JsonProperty;
-import io.swagger.v3.oas.annotations.Parameter;
 import java.net.URI;
 import javax.inject.Inject;
-import javax.ws.rs.POST;
-import javax.ws.rs.Path;
-import javax.ws.rs.PathParam;
-import javax.ws.rs.Produces;
+import org.apache.solr.client.api.endpoint.RestoreCoreApi;
+import org.apache.solr.client.api.model.RestoreCoreRequestBody;
 import org.apache.solr.client.api.model.SolrJerseyResponse;
 import org.apache.solr.cloud.CloudDescriptor;
 import org.apache.solr.common.SolrException;
 import org.apache.solr.common.cloud.Slice;
-import org.apache.solr.common.params.CoreAdminParams;
 import org.apache.solr.core.CoreContainer;
 import org.apache.solr.core.SolrCore;
 import org.apache.solr.core.backup.ShardBackupId;
 import org.apache.solr.core.backup.repository.BackupRepository;
-import org.apache.solr.handler.RestoreCore;
 import org.apache.solr.handler.admin.CoreAdminHandler;
-import org.apache.solr.jersey.JacksonReflectMapWriter;
 import org.apache.solr.jersey.PermissionName;
 import org.apache.solr.request.SolrQueryRequest;
 import org.apache.solr.response.SolrQueryResponse;
 
 /**
- * V2 API for restoring a previously taken backup to a core
+ * V2 API implementation for restoring a previously taken backup to a core
  *
  * <p>Only valid in SolrCloud mode. This API (POST /api/cores/coreName/restore 
{}) is analogous to
  * the v1 GET /solr/admin/cores?action=RESTORECORE command.
  */
-@Path("/cores/{coreName}/restore")
-public class RestoreCoreAPI extends CoreAdminAPIBase {
+public class RestoreCore extends CoreAdminAPIBase implements RestoreCoreApi {
 
   @Inject
-  public RestoreCoreAPI(
+  public RestoreCore(
       CoreContainer coreContainer,
       SolrQueryRequest solrQueryRequest,
       SolrQueryResponse solrQueryResponse,
@@ -66,20 +57,16 @@ public class RestoreCoreAPI extends CoreAdminAPIBase {
     return true;
   }
 
-  @POST
-  @Produces({"application/json", "application/xml", BINARY_CONTENT_TYPE_V2})
+  @Override
   @PermissionName(CORE_EDIT_PERM)
-  public SolrJerseyResponse restoreCore(
-      @Parameter(description = "The name of the core to be restored") 
@PathParam("coreName")
-          String coreName,
-      RestoreCoreRequestBody requestBody)
+  public SolrJerseyResponse restoreCore(String coreName, 
RestoreCoreRequestBody requestBody)
       throws Exception {
     final var response = instantiateJerseyResponse(SolrJerseyResponse.class);
     ensureRequiredParameterProvided("coreName", coreName);
     if (requestBody == null) {
       throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Missing 
required request body");
     }
-    requestBody.validate();
+    validateRequestBody(requestBody);
     AdminAPIBase.validateZooKeeperAwareCoreContainer(coreContainer);
     return handlePotentiallyAsynchronousTask(
         response,
@@ -128,12 +115,16 @@ public class RestoreCoreAPI extends CoreAdminAPIBase {
                 + ", the core must be the only replica in its shard or it must 
be read only");
       }
 
-      RestoreCore restoreCore;
+      org.apache.solr.handler.RestoreCore restoreCore;
       if (requestBody.shardBackupId != null) {
         final ShardBackupId shardBackupId = 
ShardBackupId.from(requestBody.shardBackupId);
-        restoreCore = RestoreCore.createWithMetaFile(repository, core, 
locationUri, shardBackupId);
+        restoreCore =
+            org.apache.solr.handler.RestoreCore.createWithMetaFile(
+                repository, core, locationUri, shardBackupId);
       } else {
-        restoreCore = RestoreCore.create(repository, core, locationUri, 
requestBody.name);
+        restoreCore =
+            org.apache.solr.handler.RestoreCore.create(
+                repository, core, locationUri, requestBody.name);
       }
       boolean success = restoreCore.doRestore();
       if (!success) {
@@ -149,25 +140,11 @@ public class RestoreCoreAPI extends CoreAdminAPIBase {
     }
   }
 
-  public static class RestoreCoreRequestBody implements 
JacksonReflectMapWriter {
-    @JsonProperty public String name;
-
-    @JsonProperty public String shardBackupId;
-
-    @JsonProperty(CoreAdminParams.BACKUP_REPOSITORY)
-    public String backupRepository;
-
-    @JsonProperty(CoreAdminParams.BACKUP_LOCATION)
-    public String location;
-
-    @JsonProperty public String async;
-
-    public void validate() {
-      if (shardBackupId == null && name == null) {
-        throw new SolrException(
-            SolrException.ErrorCode.BAD_REQUEST,
-            "Either 'name' or 'shardBackupId' must be specified");
-      }
+  public static void validateRequestBody(RestoreCoreRequestBody requestBody) {
+    if (requestBody.shardBackupId == null && requestBody.name == null) {
+      throw new SolrException(
+          SolrException.ErrorCode.BAD_REQUEST,
+          "Either 'name' or 'shardBackupId' must be specified");
     }
   }
 }
diff --git a/solr/core/src/test/org/apache/solr/cloud/MigrateReplicasTest.java 
b/solr/core/src/test/org/apache/solr/cloud/MigrateReplicasTest.java
index 0935770b9f5..0be4ca1746b 100644
--- a/solr/core/src/test/org/apache/solr/cloud/MigrateReplicasTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/MigrateReplicasTest.java
@@ -36,6 +36,7 @@ import org.apache.http.client.methods.HttpPost;
 import org.apache.http.entity.ByteArrayEntity;
 import org.apache.http.entity.ContentType;
 import org.apache.http.util.EntityUtils;
+import org.apache.solr.client.api.model.MigrateReplicasRequestBody;
 import org.apache.solr.client.solrj.SolrClient;
 import org.apache.solr.client.solrj.impl.CloudLegacySolrClient;
 import org.apache.solr.client.solrj.impl.CloudSolrClient;
@@ -49,7 +50,6 @@ import org.apache.solr.common.cloud.ZkStateReader;
 import org.apache.solr.common.util.StrUtils;
 import org.apache.solr.common.util.Utils;
 import org.apache.solr.embedded.JettySolrRunner;
-import org.apache.solr.handler.admin.api.MigrateReplicasAPI;
 import org.apache.solr.metrics.MetricsMap;
 import org.apache.solr.metrics.SolrMetricManager;
 import org.junit.Before;
@@ -125,7 +125,7 @@ public class MigrateReplicasTest extends SolrCloudTestCase {
     Map<?, ?> response =
         callMigrateReplicas(
             cloudClient,
-            new MigrateReplicasAPI.MigrateReplicasRequestBody(
+            new MigrateReplicasRequestBody(
                 Set.of(nodeToBeDecommissioned), Set.of(emptyNode), true, 
null));
     assertEquals(
         "MigrateReplicas request was unsuccessful",
@@ -153,7 +153,7 @@ public class MigrateReplicasTest extends SolrCloudTestCase {
     response =
         callMigrateReplicas(
             cloudClient,
-            new MigrateReplicasAPI.MigrateReplicasRequestBody(
+            new MigrateReplicasRequestBody(
                 Set.of(emptyNode), Set.of(nodeToBeDecommissioned), true, 
null));
     assertEquals(
         "MigrateReplicas request was unsuccessful",
@@ -277,7 +277,7 @@ public class MigrateReplicasTest extends SolrCloudTestCase {
     Map<?, ?> response =
         callMigrateReplicas(
             cloudClient,
-            new MigrateReplicasAPI.MigrateReplicasRequestBody(
+            new MigrateReplicasRequestBody(
                 new HashSet<>(nodesToBeDecommissioned), 
Collections.emptySet(), true, null));
     assertEquals(
         "MigrateReplicas request was unsuccessful",
@@ -327,8 +327,7 @@ public class MigrateReplicasTest extends SolrCloudTestCase {
     Map<?, ?> response =
         callMigrateReplicas(
             cloudClient,
-            new MigrateReplicasAPI.MigrateReplicasRequestBody(
-                Set.of(liveNode), Collections.emptySet(), true, null));
+            new MigrateReplicasRequestBody(Set.of(liveNode), 
Collections.emptySet(), true, null));
     assertNotNull(
         "No error in response, when the request should have failed", 
response.get("error"));
     assertEquals(
@@ -337,8 +336,7 @@ public class MigrateReplicasTest extends SolrCloudTestCase {
         ((Map<?, ?>) response.get("error")).get("msg"));
   }
 
-  public Map<?, ?> callMigrateReplicas(
-      CloudSolrClient cloudClient, 
MigrateReplicasAPI.MigrateReplicasRequestBody body)
+  public Map<?, ?> callMigrateReplicas(CloudSolrClient cloudClient, 
MigrateReplicasRequestBody body)
       throws IOException {
     HttpEntityEnclosingRequestBase httpRequest = null;
     HttpEntity entity;
@@ -351,7 +349,9 @@ public class MigrateReplicasTest extends SolrCloudTestCase {
     try {
       httpRequest = new HttpPost(uri);
 
-      httpRequest.setEntity(new ByteArrayEntity(Utils.toJSON(body), 
ContentType.APPLICATION_JSON));
+      httpRequest.setEntity(
+          new ByteArrayEntity(
+              Utils.toJSON(Utils.getReflectWriter(body)), 
ContentType.APPLICATION_JSON));
       httpRequest.setHeader("Accept", "application/json");
       entity =
           ((CloudLegacySolrClient) 
cloudClient).getHttpClient().execute(httpRequest).getEntity();
diff --git a/solr/core/src/test/org/apache/solr/core/PluginBagTest.java 
b/solr/core/src/test/org/apache/solr/core/PluginBagTest.java
index d3831489edb..b6a24ab366f 100644
--- a/solr/core/src/test/org/apache/solr/core/PluginBagTest.java
+++ b/solr/core/src/test/org/apache/solr/core/PluginBagTest.java
@@ -26,7 +26,7 @@ import org.apache.solr.api.ApiSupport;
 import org.apache.solr.api.JerseyResource;
 import org.apache.solr.handler.RequestHandlerBase;
 import org.apache.solr.handler.admin.ConfigSetsHandler;
-import org.apache.solr.handler.admin.api.CollectionPropertyAPI;
+import org.apache.solr.handler.admin.api.CollectionProperty;
 import org.apache.solr.handler.component.SearchComponent;
 import org.apache.solr.handler.configsets.ListConfigSets;
 import org.apache.solr.jersey.APIConfigProvider;
@@ -149,7 +149,7 @@ public class PluginBagTest extends SolrTestCaseJ4 {
     @Override
     public Collection<Class<? extends JerseyResource>> getJerseyResources() {
       // random pick of v2 api
-      return List.of(CollectionPropertyAPI.class);
+      return List.of(CollectionProperty.class);
     }
 
     @Override
diff --git 
a/solr/core/src/test/org/apache/solr/handler/admin/api/CoreSnapshotAPITest.java 
b/solr/core/src/test/org/apache/solr/handler/admin/api/CoreSnapshotAPITest.java
index 7f3f566bd8b..5ada271d324 100644
--- 
a/solr/core/src/test/org/apache/solr/handler/admin/api/CoreSnapshotAPITest.java
+++ 
b/solr/core/src/test/org/apache/solr/handler/admin/api/CoreSnapshotAPITest.java
@@ -19,6 +19,9 @@ package org.apache.solr.handler.admin.api;
 import java.util.ArrayList;
 import java.util.List;
 import org.apache.solr.SolrTestCaseJ4;
+import org.apache.solr.client.api.model.CreateCoreSnapshotResponse;
+import org.apache.solr.client.api.model.DeleteSnapshotResponse;
+import org.apache.solr.client.api.model.ListCoreSnapshotsResponse;
 import org.apache.solr.common.SolrException;
 import org.apache.solr.core.CoreContainer;
 import org.apache.solr.handler.admin.CoreAdminHandler;
@@ -31,7 +34,7 @@ import org.junit.Test;
 
 public class CoreSnapshotAPITest extends SolrTestCaseJ4 {
 
-  private CoreSnapshotAPI coreSnapshotAPI;
+  private CoreSnapshot coreSnapshotAPI;
 
   @BeforeClass
   public static void initializeCoreAndRequestFactory() throws Exception {
@@ -52,8 +55,7 @@ public class CoreSnapshotAPITest extends SolrTestCaseJ4 {
         new CoreAdminHandler.CoreAdminAsyncTracker();
 
     coreSnapshotAPI =
-        new CoreSnapshotAPI(
-            solrQueryRequest, solrQueryResponse, coreContainer, 
coreAdminAsyncTracker);
+        new CoreSnapshot(solrQueryRequest, solrQueryResponse, coreContainer, 
coreAdminAsyncTracker);
   }
 
   private List<String> snapshotsToCleanup = new ArrayList<>();
@@ -71,7 +73,7 @@ public class CoreSnapshotAPITest extends SolrTestCaseJ4 {
   public void testCreateSnapshotReturnsValidResponse() throws Exception {
     final String snapshotName = "my-new-snapshot";
 
-    final CoreSnapshotAPI.CreateSnapshotResponse response =
+    final CreateCoreSnapshotResponse response =
         coreSnapshotAPI.createSnapshot(coreName, snapshotName, null);
     snapshotsToCleanup.add(snapshotName);
 
@@ -108,7 +110,7 @@ public class CoreSnapshotAPITest extends SolrTestCaseJ4 {
       snapshotsToCleanup.add(snapshotName);
     }
 
-    final CoreSnapshotAPI.ListSnapshotsResponse response = 
coreSnapshotAPI.listSnapshots(coreName);
+    final ListCoreSnapshotsResponse response = 
coreSnapshotAPI.listSnapshots(coreName);
 
     assertEquals(5, response.snapshots.size());
   }
@@ -135,13 +137,13 @@ public class CoreSnapshotAPITest extends SolrTestCaseJ4 {
 
     coreSnapshotAPI.createSnapshot(coreName, snapshotName, null);
 
-    final CoreSnapshotAPI.DeleteSnapshotResponse deleteResponse =
+    final DeleteSnapshotResponse deleteResponse =
         coreSnapshotAPI.deleteSnapshot(coreName, snapshotName, null);
 
     assertEquals(coreName, deleteResponse.coreName);
     assertEquals(snapshotName, deleteResponse.commitName);
 
-    final CoreSnapshotAPI.ListSnapshotsResponse response = 
coreSnapshotAPI.listSnapshots(coreName);
+    final ListCoreSnapshotsResponse response = 
coreSnapshotAPI.listSnapshots(coreName);
 
     assertEquals(0, response.snapshots.size());
   }
diff --git 
a/solr/core/src/test/org/apache/solr/handler/admin/api/MigrateReplicasAPITest.java
 
b/solr/core/src/test/org/apache/solr/handler/admin/api/MigrateReplicasAPITest.java
index f9bf865d4e0..f6124c99703 100644
--- 
a/solr/core/src/test/org/apache/solr/handler/admin/api/MigrateReplicasAPITest.java
+++ 
b/solr/core/src/test/org/apache/solr/handler/admin/api/MigrateReplicasAPITest.java
@@ -27,6 +27,7 @@ import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
 import org.apache.solr.SolrTestCaseJ4;
+import org.apache.solr.client.api.model.MigrateReplicasRequestBody;
 import org.apache.solr.cloud.OverseerSolrResponse;
 import 
org.apache.solr.cloud.api.collections.DistributedCollectionConfigSetCommandRunner;
 import org.apache.solr.common.SolrException;
@@ -46,7 +47,7 @@ public class MigrateReplicasAPITest extends SolrTestCaseJ4 {
   private CoreContainer mockCoreContainer;
   private SolrQueryRequest mockQueryRequest;
   private SolrQueryResponse queryResponse;
-  private MigrateReplicasAPI migrateReplicasAPI;
+  private MigrateReplicas migrateReplicasAPI;
   private DistributedCollectionConfigSetCommandRunner mockCommandRunner;
   private ArgumentCaptor<ZkNodeProps> messageCapturer;
 
@@ -68,7 +69,7 @@ public class MigrateReplicasAPITest extends SolrTestCaseJ4 {
         .thenReturn(new OverseerSolrResponse(new NamedList<>()));
     mockQueryRequest = mock(SolrQueryRequest.class);
     queryResponse = new SolrQueryResponse();
-    migrateReplicasAPI = new MigrateReplicasAPI(mockCoreContainer, 
mockQueryRequest, queryResponse);
+    migrateReplicasAPI = new MigrateReplicas(mockCoreContainer, 
mockQueryRequest, queryResponse);
     messageCapturer = ArgumentCaptor.forClass(ZkNodeProps.class);
 
     when(mockCoreContainer.isZooKeeperAware()).thenReturn(true);
@@ -76,8 +77,8 @@ public class MigrateReplicasAPITest extends SolrTestCaseJ4 {
 
   @Test
   public void testCreatesValidOverseerMessage() throws Exception {
-    MigrateReplicasAPI.MigrateReplicasRequestBody requestBody =
-        new MigrateReplicasAPI.MigrateReplicasRequestBody(
+    MigrateReplicasRequestBody requestBody =
+        new MigrateReplicasRequestBody(
             Set.of("demoSourceNode"), Set.of("demoTargetNode"), false, 
"async");
     migrateReplicasAPI.migrateReplicas(requestBody);
     verify(mockCommandRunner).runCollectionCommand(messageCapturer.capture(), 
any(), anyLong());
@@ -94,9 +95,8 @@ public class MigrateReplicasAPITest extends SolrTestCaseJ4 {
 
   @Test
   public void testNoTargetNodes() throws Exception {
-    MigrateReplicasAPI.MigrateReplicasRequestBody requestBody =
-        new MigrateReplicasAPI.MigrateReplicasRequestBody(
-            Set.of("demoSourceNode"), null, null, null);
+    MigrateReplicasRequestBody requestBody =
+        new MigrateReplicasRequestBody(Set.of("demoSourceNode"), null, null, 
null);
     migrateReplicasAPI.migrateReplicas(requestBody);
     verify(mockCommandRunner).runCollectionCommand(messageCapturer.capture(), 
any(), anyLong());
 
@@ -109,13 +109,12 @@ public class MigrateReplicasAPITest extends 
SolrTestCaseJ4 {
 
   @Test
   public void testNoSourceNodesThrowsError() throws Exception {
-    MigrateReplicasAPI.MigrateReplicasRequestBody requestBody1 =
-        new MigrateReplicasAPI.MigrateReplicasRequestBody(
+    MigrateReplicasRequestBody requestBody1 =
+        new MigrateReplicasRequestBody(
             Collections.emptySet(), Set.of("demoTargetNode"), null, null);
     assertThrows(SolrException.class, () -> 
migrateReplicasAPI.migrateReplicas(requestBody1));
-    MigrateReplicasAPI.MigrateReplicasRequestBody requestBody2 =
-        new MigrateReplicasAPI.MigrateReplicasRequestBody(
-            null, Set.of("demoTargetNode"), null, null);
+    MigrateReplicasRequestBody requestBody2 =
+        new MigrateReplicasRequestBody(null, Set.of("demoTargetNode"), null, 
null);
     assertThrows(SolrException.class, () -> 
migrateReplicasAPI.migrateReplicas(requestBody2));
   }
 }
diff --git 
a/solr/core/src/test/org/apache/solr/handler/admin/api/ReloadCoreAPITest.java 
b/solr/core/src/test/org/apache/solr/handler/admin/api/ReloadCoreAPITest.java
index cd0fdc32324..304717a167a 100644
--- 
a/solr/core/src/test/org/apache/solr/handler/admin/api/ReloadCoreAPITest.java
+++ 
b/solr/core/src/test/org/apache/solr/handler/admin/api/ReloadCoreAPITest.java
@@ -17,6 +17,7 @@
 package org.apache.solr.handler.admin.api;
 
 import org.apache.solr.SolrTestCaseJ4;
+import org.apache.solr.client.api.model.ReloadCoreRequestBody;
 import org.apache.solr.client.api.model.SolrJerseyResponse;
 import org.apache.solr.common.SolrException;
 import org.apache.solr.core.CoreContainer;
@@ -30,7 +31,7 @@ import org.junit.Test;
 
 public class ReloadCoreAPITest extends SolrTestCaseJ4 {
 
-  private ReloadCoreAPI reloadCoreAPI;
+  private ReloadCore reloadCoreAPI;
   private static final String NON_EXISTENT_CORE = "non_existent_core";
 
   @BeforeClass
@@ -49,14 +50,12 @@ public class ReloadCoreAPITest extends SolrTestCaseJ4 {
     CoreAdminHandler.CoreAdminAsyncTracker coreAdminAsyncTracker =
         new CoreAdminHandler.CoreAdminAsyncTracker();
     reloadCoreAPI =
-        new ReloadCoreAPI(
-            solrQueryRequest, solrQueryResponse, coreContainer, 
coreAdminAsyncTracker);
+        new ReloadCore(solrQueryRequest, solrQueryResponse, coreContainer, 
coreAdminAsyncTracker);
   }
 
   @Test
   public void testValidReloadCoreAPIResponse() throws Exception {
-    SolrJerseyResponse response =
-        reloadCoreAPI.reloadCore(coreName, new 
ReloadCoreAPI.ReloadCoreRequestBody());
+    SolrJerseyResponse response = reloadCoreAPI.reloadCore(coreName, new 
ReloadCoreRequestBody());
     assertEquals(0, response.responseHeader.status);
     assertNotNull(response.responseHeader.qTime);
   }
@@ -67,8 +66,7 @@ public class ReloadCoreAPITest extends SolrTestCaseJ4 {
         expectThrows(
             SolrException.class,
             () -> {
-              reloadCoreAPI.reloadCore(
-                  NON_EXISTENT_CORE, new 
ReloadCoreAPI.ReloadCoreRequestBody());
+              reloadCoreAPI.reloadCore(NON_EXISTENT_CORE, new 
ReloadCoreRequestBody());
             });
     assertEquals(400, solrException.code());
     assertTrue(solrException.getMessage().contains("No such core: " + 
NON_EXISTENT_CORE));


Reply via email to