Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SOLR-17044: Expose api "version" on SolrRequest objects #2456

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
import org.apache.solr.client.solrj.SolrServerException;
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.beans.PluginMeta;
Expand Down Expand Up @@ -237,7 +238,7 @@ public Map<String, SolrPackageInstance> getPackagesDeployed(String collection) {
try {
NamedList<Object> result =
solrClient.request(
new GenericSolrRequest(
new GenericV2SolrRequest(
SolrRequest.METHOD.GET,
PackageUtils.getCollectionParamsPath(collection) + "/PKG_VERSIONS")
.setRequiresCollection(
Expand Down Expand Up @@ -278,7 +279,7 @@ public Map<String, SolrPackageInstance> getPackagesDeployedAsClusterLevelPlugins
try {
NamedList<Object> response =
solrClient.request(
new GenericSolrRequest(SolrRequest.METHOD.GET, PackageUtils.CLUSTERPROPS_PATH));
new GenericV2SolrRequest(SolrRequest.METHOD.GET, PackageUtils.CLUSTERPROPS_PATH));
Integer statusCode = (Integer) response.findRecursive("responseHeader", "status");
if (statusCode == null || statusCode == ErrorCode.NOT_FOUND.code) {
// Cluster props doesn't exist, that means there are no cluster level plugins installed.
Expand Down Expand Up @@ -421,7 +422,7 @@ private Pair<List<String>, List<String>> deployCollectionPackage(
boolean packageParamsExist =
solrClient
.request(
new GenericSolrRequest(
new GenericV2SolrRequest(
SolrRequest.METHOD.GET,
PackageUtils.getCollectionParamsPath(collection) + "/packages")
.setRequiresCollection(
Expand Down Expand Up @@ -723,7 +724,7 @@ Map<String, String> getPackageParams(String packageName, String collection) {
try {
NamedList<Object> response =
solrClient.request(
new GenericSolrRequest(
new GenericV2SolrRequest(
SolrRequest.METHOD.GET,
PackageUtils.getCollectionParamsPath(collection) + "/packages")
.setRequiresCollection(
Expand Down
52 changes: 15 additions & 37 deletions solr/core/src/java/org/apache/solr/packagemanager/PackageUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,7 @@
import com.jayway.jsonpath.spi.mapper.JacksonMappingProvider;
import com.jayway.jsonpath.spi.mapper.MappingProvider;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.List;
Expand All @@ -40,11 +38,11 @@
import org.apache.solr.client.solrj.SolrRequest;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.impl.JsonMapResponseParser;
import org.apache.solr.client.solrj.request.FileStoreApi;
import org.apache.solr.client.solrj.request.GenericSolrRequest;
import org.apache.solr.client.solrj.request.RequestWriter;
import org.apache.solr.client.solrj.request.GenericV2SolrRequest;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.SolrException.ErrorCode;
import org.apache.solr.common.params.CommonParams;
import org.apache.solr.common.params.ModifiableSolrParams;
import org.apache.solr.common.params.SolrParams;
import org.apache.solr.common.util.NamedList;
Expand All @@ -54,6 +52,7 @@
import org.apache.solr.filestore.FileStoreAPI;
import org.apache.solr.packagemanager.SolrPackage.Manifest;
import org.apache.solr.util.SolrJacksonAnnotationInspector;
import org.apache.zookeeper.server.ByteBufferInputStream;

public class PackageUtils {

Expand Down Expand Up @@ -91,39 +90,18 @@ public static ObjectMapper getMapper() {
*/
public static void postFile(SolrClient client, ByteBuffer buffer, String name, String sig)
throws SolrServerException, IOException {
String resource = "/api/cluster/files" + name;
ModifiableSolrParams params = new ModifiableSolrParams();
if (sig != null) {
params.add("sig", sig);
}
GenericSolrRequest request =
new GenericSolrRequest(SolrRequest.METHOD.PUT, resource, params) {
@Override
public RequestWriter.ContentWriter getContentWriter(String expectedType) {
return new RequestWriter.ContentWriter() {
public final ByteBuffer payload = buffer;

@Override
public void write(OutputStream os) throws IOException {
if (payload == null) return;
Channels.newChannel(os).write(payload);
}
try (final var stream = new ByteBufferInputStream(buffer)) {
final var uploadReq = new FileStoreApi.UploadFile(name, stream);
if (sig != null) {
uploadReq.setSig(List.of(sig));
}

@Override
public String getContentType() {
return "application/octet-stream";
}
};
}
};
NamedList<Object> rsp = client.request(request);
if (!name.equals(rsp.get(CommonParams.FILE))) {
throw new SolrException(
ErrorCode.BAD_REQUEST,
"Mismatch in file uploaded. Uploaded: "
+ rsp.get(CommonParams.FILE)
+ ", Original: "
+ name);
final var uploadRsp = uploadReq.process(client).getParsed();
if (!name.equals(uploadRsp.file)) {
throw new SolrException(
ErrorCode.BAD_REQUEST,
"Mismatch in file uploaded. Uploaded: " + uploadRsp.file + ", Original: " + name);
}
}
}

Expand Down Expand Up @@ -205,7 +183,7 @@ public static Manifest fetchManifest(
SolrClient solrClient, String manifestFilePath, String expectedSHA512)
throws IOException, SolrServerException {
GenericSolrRequest request =
new GenericSolrRequest(SolrRequest.METHOD.GET, "/api/node/files" + manifestFilePath);
new GenericV2SolrRequest(SolrRequest.METHOD.GET, "/api/node/files" + manifestFilePath);
request.setResponseParser(new JsonMapResponseParser());
NamedList<Object> response = solrClient.request(request);
String manifestJson = (String) response.get("response");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
import org.apache.solr.client.solrj.SolrRequest;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.request.GenericSolrRequest;
import org.apache.solr.client.solrj.request.GenericV2SolrRequest;
import org.apache.solr.client.solrj.request.RequestWriter;
import org.apache.solr.client.solrj.request.beans.PackagePayload;
import org.apache.solr.common.SolrException;
Expand Down Expand Up @@ -237,7 +238,7 @@ private boolean installPackage(String packageName, String version) throws SolrEx
add.manifestSHA512 = manifestSHA512;

GenericSolrRequest request =
new GenericSolrRequest(SolrRequest.METHOD.POST, PackageUtils.PACKAGE_PATH) {
new GenericV2SolrRequest(SolrRequest.METHOD.POST, PackageUtils.PACKAGE_PATH) {
@Override
public RequestWriter.ContentWriter getContentWriter(String expectedType) {
return new RequestWriter.StringPayloadContentWriter(
Expand Down
24 changes: 24 additions & 0 deletions solr/solrj/src/java/org/apache/solr/client/solrj/SolrRequest.java
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,21 @@ public enum METHOD {
DELETE
};

public enum ApiVersion {
V1("/solr"),
V2("/api");

private final String apiPrefix;

ApiVersion(String apiPrefix) {
this.apiPrefix = apiPrefix;
}

public String getApiPrefix() {
return apiPrefix;
}
}

public enum SolrRequestType {
QUERY,
UPDATE,
Expand Down Expand Up @@ -188,6 +203,15 @@ public boolean requiresCollection() {
return false;
}

/**
* Indicates which API version this request will make
*
* <p>Defaults implementation returns 'V1'.
*/
public ApiVersion getApiVersion() {
return ApiVersion.V1;
}

/**
* @deprecated Please use {@link SolrRequest#getContentWriter(String)} instead.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -852,7 +852,8 @@ protected NamedList<Object> requestWithRetryOnStaleState(
// or request is v2 api and its method is not GET
if (inputCollections.isEmpty()
|| isAdmin
|| (request instanceof V2Request && request.getMethod() != SolrRequest.METHOD.GET)) {
|| (request.getApiVersion() == SolrRequest.ApiVersion.V2
&& request.getMethod() != SolrRequest.METHOD.GET)) {
if (exc instanceof SolrServerException) {
throw (SolrServerException) exc;
} else if (exc instanceof IOException) {
Expand Down Expand Up @@ -1026,7 +1027,7 @@ protected NamedList<Object> sendRequest(SolrRequest<?> request, List<String> inp
final List<LBSolrClient.Endpoint> requestEndpoints =
new ArrayList<>(); // we populate this as follows...

if (request instanceof V2Request) {
if (request.getApiVersion() == SolrRequest.ApiVersion.V2) {
if (!liveNodes.isEmpty()) {
List<String> liveNodesList = new ArrayList<>(liveNodes);
Collections.shuffle(liveNodesList, rand);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,6 @@
import org.apache.solr.client.solrj.SolrRequest;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.request.RequestWriter;
import org.apache.solr.client.solrj.request.V2Request;
import org.apache.solr.client.solrj.util.ClientUtils;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.params.CommonParams;
Expand Down Expand Up @@ -256,7 +255,7 @@ public NamedList<Object> request(
}

private boolean isV2ApiRequest(final SolrRequest<?> request) {
return request instanceof V2Request || request.getPath().contains("/____v2");
return request.getApiVersion() == SolrRequest.ApiVersion.V2;
}

private void setBasicAuthHeader(SolrRequest<?> request, HttpRequestBase method)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@
import org.apache.solr.client.solrj.SolrRequest;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.request.RequestWriter;
import org.apache.solr.client.solrj.request.V2Request;
import org.apache.solr.client.solrj.util.ClientUtils;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.params.CommonParams;
Expand Down Expand Up @@ -388,7 +387,7 @@ public CompletableFuture<NamedList<Object>> requestAsync(final SolrRequest<?> re
}

public boolean isV2ApiRequest(final SolrRequest<?> request) {
return request instanceof V2Request || request.getPath().contains("/____v2");
return request.getApiVersion() == SolrRequest.ApiVersion.V2;
}

public String getBaseURL() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.solr.client.solrj.request;

import org.apache.solr.common.params.SolrParams;

/** A {@link GenericSolrRequest} implementation intended for v2 APIs */
public class GenericV2SolrRequest extends GenericSolrRequest {

/**
* @param m the HTTP method to use for this request
* @param path the HTTP path to use for this request. Path may include the v2 API root path (i.e.
* "/api"), but does not need to. If users are making a collection-aware request (i.e. {@link
* #setRequiresCollection(boolean)} is called with 'true'), only the section of the API path
* following the collection or core should be provided here.
*/
public GenericV2SolrRequest(METHOD m, String path) {
super(m, removeLeadingApiRoot(path));
}

/**
* @param m the HTTP method to use for this request
* @param path the HTTP path to use for this request. If users are making a collection-aware
* request (i.e. {@link #setRequiresCollection(boolean)} is called with 'true'), only the
* section of the API path following the collection or core should be provided here.
* @param params query parameter names and values for making this request.
*/
public GenericV2SolrRequest(METHOD m, String path, SolrParams params) {
super(m, path);
this.params = params;
}

@Override
public ApiVersion getApiVersion() {
return ApiVersion.V2;
}

private static String removeLeadingApiRoot(String path) {
if (path.startsWith("/api")) {
return path.replaceFirst("/api", "");
}
return path;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,11 @@ public String getCollection() {
return collection;
}

@Override
public ApiVersion getApiVersion() {
return ApiVersion.V2;
}

@Override
protected V2Response createResponse(SolrClient client) {
return new V2Response();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,11 @@ public static String buildRequestUrl(
throws MalformedURLException {
String basePath = solrRequest.getBasePath() == null ? serverRootUrl : solrRequest.getBasePath();

if (solrRequest instanceof V2Request) {
if (System.getProperty("solr.v2RealPath") == null) {
basePath = changeV2RequestEndpoint(basePath);
} else {
if (solrRequest.getApiVersion() == SolrRequest.ApiVersion.V2) {
if (solrRequest instanceof V2Request && System.getProperty("solr.v2RealPath") != null) {
basePath = serverRootUrl + "/____v2";
} else {
basePath = addNormalV2ApiRoot(basePath);
}
}

Expand All @@ -102,10 +102,18 @@ public static String buildRequestUrl(
return basePath + path;
}

private static String changeV2RequestEndpoint(String basePath) throws MalformedURLException {
URI oldURI = URI.create(basePath);
String newPath = oldURI.getPath().replaceFirst("/solr", "/api");
return oldURI.resolve(newPath).toString();
private static String addNormalV2ApiRoot(String basePath) throws MalformedURLException {
final var oldURI = URI.create(basePath);
final var revisedPath = buildReplacementV2Path(oldURI.getPath());
return oldURI.resolve(revisedPath).toString();
}

private static String buildReplacementV2Path(String existingPath) {
if (existingPath.contains("/solr")) {
return existingPath.replaceFirst("/solr", "/api");
} else {
return existingPath + "/api";
}
}

// ------------------------------------------------------------------------
Expand Down
5 changes: 5 additions & 0 deletions solr/solrj/src/resources/java-template/api.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,11 @@ public class {{classname}} {
return SolrRequestType.ADMIN.toString();
}

@Override
public ApiVersion getApiVersion() {
return ApiVersion.V2;
}

@Override
public SolrParams getParams() {
final ModifiableSolrParams params = new ModifiableSolrParams();
Expand Down
Loading