Skip to content
Open
7 changes: 7 additions & 0 deletions changelog/unreleased/migrate-packageapi-to-jax-rs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
title: Migrate PackageAPI to JAX-RS. PackageAPI now has OpenAPI and SolrJ support.
type: changed
authors:
- name: Eric Pugh
links:
- name: PR#4178
url: https://github.com/apache/solr/pull/4178
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/*
* 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.parameters.RequestBody;
import jakarta.ws.rs.DELETE;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.QueryParam;
import org.apache.solr.client.api.model.AddPackageVersionRequestBody;
import org.apache.solr.client.api.model.PackagesResponse;
import org.apache.solr.client.api.model.SolrJerseyResponse;

/** V2 API definitions for managing Solr packages. */
@Path("/cluster/package")
public interface PackageApis {

@GET
@Operation(
summary = "List all packages registered in this Solr cluster.",
tags = {"package"})
PackagesResponse listPackages(
@Parameter(description = "If provided, the named package is refreshed on this node.")
@QueryParam("refreshPackage")
String refreshPackage,
@Parameter(
description =
"If provided, the node waits until its package data matches this ZooKeeper version.")
@QueryParam("expectedVersion")
Integer expectedVersion);

@GET
@Path("/{packageName}")
@Operation(
summary = "Get information about a specific package in this Solr cluster.",
tags = {"package"})
PackagesResponse getPackage(
@Parameter(description = "The name of the package.", required = true)
@PathParam("packageName")
String packageName);

@POST
@Path("/{packageName}/versions")
@Operation(
summary = "Add a version of a package to this Solr cluster.",
tags = {"package"})
SolrJerseyResponse addPackageVersion(
@Parameter(description = "The name of the package.", required = true)
@PathParam("packageName")
String packageName,
@RequestBody(description = "Details of the package version to add.", required = true)
AddPackageVersionRequestBody requestBody);

@DELETE
@Path("/{packageName}/versions/{version}")
@Operation(
summary = "Delete a specific version of a package from this Solr cluster.",
tags = {"package"})
SolrJerseyResponse deletePackageVersion(
@Parameter(description = "The name of the package.", required = true)
@PathParam("packageName")
String packageName,
@Parameter(description = "The version of the package to delete.", required = true)
@PathParam("version")
String version);

@POST
@Path("/{packageName}/refresh")
@Operation(
summary = "Refresh a package on all nodes in this Solr cluster.",
tags = {"package"})
SolrJerseyResponse refreshPackage(
@Parameter(description = "The name of the package to refresh.", required = true)
@PathParam("packageName")
String packageName);
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,34 +14,30 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.solr.client.api.model;

package org.apache.solr.client.solrj.request.beans;

import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import java.util.List;
import org.apache.solr.common.annotation.JsonProperty;
import org.apache.solr.common.util.ReflectMapWriter;

/** Just a container class for POJOs used in Package APIs */
public class PackagePayload {
public static class AddVersion implements ReflectMapWriter {
@JsonProperty(value = "package", required = true)
public String pkg;

@JsonProperty(required = true)
public String version;
/** Request body for adding a version of a package. */
public class AddPackageVersionRequestBody {

@JsonProperty(required = true)
public List<String> files;
@JsonProperty("version")
@Schema(description = "The version string for this package version.", required = true)
public String version;

@JsonProperty public String manifest;
@JsonProperty public String manifestSHA512;
}
@JsonProperty("files")
@Schema(
description = "File paths from the file store to include in this version.",
required = true)
public List<String> files;

public static class DelVersion implements ReflectMapWriter {
@JsonProperty(value = "package", required = true)
public String pkg;
@JsonProperty("manifest")
@Schema(description = "Optional path to a manifest file in the file store.")
public String manifest;

@JsonProperty(required = true)
public String version;
}
@JsonProperty("manifestSHA512")
@Schema(description = "Optional SHA-512 hash of the manifest file.")
public String manifestSHA512;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* 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.List;
import java.util.Map;

/** Response for the package listing API. */
public class PackagesResponse extends SolrJerseyResponse {

@JsonProperty("result")
@Schema(description = "The package data including znode version and package definitions.")
public PackageData result;

/** Package data returned by the package API. */
public static class PackageData {
@JsonProperty("znodeVersion")
@Schema(description = "The ZooKeeper version of the packages.json node.")
public int znodeVersion;

@JsonProperty("packages")
@Schema(description = "Map from package name to list of package versions.")
public Map<String, List<PackageVersion>> packages;
}

/** Describes a single version of a package. */
public static class PackageVersion {
@JsonProperty("package")
@Schema(description = "The package name.")
public String pkg;

@JsonProperty("version")
@Schema(description = "The version string.")
public String version;

@JsonProperty("files")
@Schema(description = "List of file paths from the file store included in this version.")
public List<String> files;

@JsonProperty("manifest")
@Schema(description = "Optional manifest reference.")
public String manifest;

@JsonProperty("manifestSHA512")
@Schema(description = "Optional SHA-512 hash of the manifest.")
public String manifestSHA512;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,7 @@ public ApiInfo(PluginMetaHolder infoHolder, List<String> errs) {
coreContainer.getPackageLoader().getPackageVersion(pkg, info.version);
if (ver.isEmpty()) {
// may be we are a bit early. Do a refresh and try again
coreContainer.getPackageLoader().getPackageAPI().refreshPackages(null);
coreContainer.getPackageLoader().getPackageStore().refreshPackages(null);
ver = coreContainer.getPackageLoader().getPackageVersion(pkg, info.version);
}
if (ver.isEmpty()) {
Expand Down
4 changes: 2 additions & 2 deletions solr/core/src/java/org/apache/solr/core/CoreContainer.java
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@
import org.apache.solr.metrics.SolrMetricProducer;
import org.apache.solr.metrics.SolrMetricsContext;
import org.apache.solr.metrics.otel.OtelUnit;
import org.apache.solr.pkg.PackageAPI;
import org.apache.solr.pkg.SolrPackageLoader;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.request.SolrQueryRequestBase;
Expand Down Expand Up @@ -841,8 +842,7 @@ private void loadInternal() {
registerV2ApiIfEnabled(ClusterFileStore.class);

packageLoader = new SolrPackageLoader(this);
registerV2ApiIfEnabled(packageLoader.getPackageAPI().editAPI);
registerV2ApiIfEnabled(packageLoader.getPackageAPI().readAPI);
registerV2ApiIfEnabled(PackageAPI.class);
registerV2ApiIfEnabled(ZookeeperRead.class);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
import org.apache.solr.core.CoreContainer;
import org.apache.solr.core.SolrCore;
import org.apache.solr.jersey.PermissionName;
import org.apache.solr.pkg.PackageAPI;
import org.apache.solr.pkg.PackageStore;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.response.SolrQueryResponse;
import org.apache.solr.security.PermissionNameProvider;
Expand Down Expand Up @@ -86,8 +86,8 @@ public ClusterFileStore(
public UploadToFileStoreResponse uploadFile(
String filePath, List<String> sig, InputStream requestBody) {
final var response = instantiateJerseyResponse(UploadToFileStoreResponse.class);
if (!coreContainer.getPackageLoader().getPackageAPI().isEnabled()) {
throw new RuntimeException(PackageAPI.ERR_MSG);
if (!coreContainer.getPackageLoader().getPackageStore().isEnabled()) {
throw new RuntimeException(PackageStore.ERR_MSG);
}
try {
coreContainer
Expand Down Expand Up @@ -302,12 +302,12 @@ private void doDelete(String filePath, Boolean localDelete) {
@PermissionName(PermissionNameProvider.Name.FILESTORE_WRITE_PERM)
public SolrJerseyResponse deleteFile(String filePath, Boolean localDelete) {
final var response = instantiateJerseyResponse(SolrJerseyResponse.class);
if (!coreContainer.getPackageLoader().getPackageAPI().isEnabled()) {
throw new RuntimeException(PackageAPI.ERR_MSG);
if (!coreContainer.getPackageLoader().getPackageStore().isEnabled()) {
throw new RuntimeException(PackageStore.ERR_MSG);
}

validateName(filePath, true);
if (coreContainer.getPackageLoader().getPackageAPI().isJarInuse(filePath)) {
if (coreContainer.getPackageLoader().getPackageStore().isJarInuse(filePath)) {
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "jar in use, can't delete");
}
doDelete(filePath, localDelete);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,8 @@
import org.apache.solr.handler.admin.api.GetConfigAPI;
import org.apache.solr.handler.admin.api.ModifyConfigComponentAPI;
import org.apache.solr.handler.admin.api.ModifyParamSetAPI;
import org.apache.solr.pkg.PackageAPI;
import org.apache.solr.pkg.PackageListeners;
import org.apache.solr.pkg.PackageStore;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.request.SolrRequestHandler;
import org.apache.solr.response.SolrQueryResponse;
Expand Down Expand Up @@ -303,7 +303,7 @@ private void handleGET() {
List<PackageListeners.Listener> listeners =
req.getCore().getPackageListeners().getListeners();
for (PackageListeners.Listener listener : listeners) {
Map<String, PackageAPI.PkgVersion> infos = listener.packageDetails();
Map<String, PackageStore.PkgVersion> infos = listener.packageDetails();
if (infos == null || infos.isEmpty()) continue;
infos.forEach(
(s, mapWriter) -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@
import org.apache.solr.core.SolrCore;
import org.apache.solr.handler.RequestHandlerBase;
import org.apache.solr.metrics.SolrMetricsContext;
import org.apache.solr.pkg.PackageAPI;
import org.apache.solr.pkg.PackageListeners;
import org.apache.solr.pkg.PackageStore;
import org.apache.solr.pkg.SolrPackageLoader;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.response.SolrQueryResponse;
Expand Down Expand Up @@ -202,7 +202,7 @@ public String packageName() {
}

@Override
public Map<String, PackageAPI.PkgVersion> packageDetails() {
public Map<String, PackageStore.PkgVersion> packageDetails() {
return Collections.emptyMap();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,8 @@
import org.apache.solr.client.solrj.impl.SolrZkClientTimeout;
import org.apache.solr.client.solrj.request.GenericSolrRequest;
import org.apache.solr.client.solrj.request.GenericV2SolrRequest;
import org.apache.solr.client.solrj.request.V2Request;
import org.apache.solr.client.solrj.request.beans.PackagePayload;
import org.apache.solr.client.solrj.request.PackageApi;
import org.apache.solr.client.solrj.request.beans.PluginMeta;
import org.apache.solr.client.solrj.response.V2Response;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.SolrException.ErrorCode;
import org.apache.solr.common.cloud.SolrZkClient;
Expand Down Expand Up @@ -152,20 +150,9 @@ public void uninstall(String packageName, String version)
// Delete the package by calling the Package API and remove the Jar

printGreen("Executing Package API to remove this package...");
PackagePayload.DelVersion del = new PackagePayload.DelVersion();
del.version = version;
del.pkg = packageName;

V2Request req =
new V2Request.Builder(PackageUtils.PACKAGE_PATH)
.forceV2(true)
.withMethod(SolrRequest.METHOD.POST)
.withPayload(Collections.singletonMap("delete", del))
.build();

try {
V2Response resp = req.process(solrClient);
printGreen("Response: " + resp.jsonStr());
new PackageApi.DeletePackageVersion(packageName, version).process(solrClient);
printGreen("Package version deleted from Package API.");
} catch (SolrServerException | IOException e) {
throw new SolrException(ErrorCode.BAD_REQUEST, e);
}
Expand Down Expand Up @@ -467,10 +454,7 @@ private Pair<List<String>, List<String>> deployCollectionPackage(
// If updating, refresh the package version for this to take effect
if (isUpdate || pegToLatest) {
try {
SolrCLI.postJsonToSolr(
solrClient,
PackageUtils.PACKAGE_PATH,
"{\"refresh\": \"" + packageInstance.name + "\"}");
new PackageApi.RefreshPackage(packageInstance.name).process(solrClient);
} catch (Exception ex) {
throw new SolrException(ErrorCode.SERVER_ERROR, ex);
}
Expand Down Expand Up @@ -1082,8 +1066,7 @@ public void undeploy(
solrClient,
PackageUtils.getCollectionParamsPath(collection),
"{set: {PKG_VERSIONS: {" + packageName + ": null}}}");
SolrCLI.postJsonToSolr(
solrClient, PackageUtils.PACKAGE_PATH, "{\"refresh\": \"" + packageName + "\"}");
new PackageApi.RefreshPackage(packageName).process(solrClient);
} catch (Exception ex) {
throw new SolrException(ErrorCode.SERVER_ERROR, ex);
}
Expand Down
Loading
Loading