From 9bd4177b5c6845934bddd0954eef6a34cfc08d6f Mon Sep 17 00:00:00 2001 From: Jason Gerlowski Date: Fri, 3 Jan 2025 14:27:39 -0500 Subject: [PATCH 01/10] SOLR-16391: Convert create-core to JAX-RS Still need to run tests and do a final once-over of the code, but this appears to be pretty close. --- .../solr/client/api/endpoint/CoreApis.java | 34 +++ .../client/api/model/CreateCoreParams.java} | 21 +- .../client/api/model/CreateCoreResponse.java | 24 +++ .../solr/handler/admin/CoreAdminHandler.java | 37 +--- .../handler/admin/CoreAdminOperation.java | 38 +--- .../solr/handler/admin/api/CreateCore.java | 196 ++++++++++++++++++ .../solr/handler/admin/api/CreateCoreAPI.java | 79 ------- .../apache/solr/jersey/SolrJacksonMapper.java | 4 + .../handler/admin/V2CoresAPIMappingTest.java | 116 +++++------ .../solr/common/util/CollectionUtil.java | 9 + 10 files changed, 337 insertions(+), 221 deletions(-) create mode 100644 solr/api/src/java/org/apache/solr/client/api/endpoint/CoreApis.java rename solr/{solrj/src/java/org/apache/solr/client/solrj/request/beans/CreateCorePayload.java => api/src/java/org/apache/solr/client/api/model/CreateCoreParams.java} (71%) create mode 100644 solr/api/src/java/org/apache/solr/client/api/model/CreateCoreResponse.java create mode 100644 solr/core/src/java/org/apache/solr/handler/admin/api/CreateCore.java delete mode 100644 solr/core/src/java/org/apache/solr/handler/admin/api/CreateCoreAPI.java diff --git a/solr/api/src/java/org/apache/solr/client/api/endpoint/CoreApis.java b/solr/api/src/java/org/apache/solr/client/api/endpoint/CoreApis.java new file mode 100644 index 00000000000..b87879dc8f5 --- /dev/null +++ b/solr/api/src/java/org/apache/solr/client/api/endpoint/CoreApis.java @@ -0,0 +1,34 @@ +/* + * 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 jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import org.apache.solr.client.api.model.CreateCoreParams; +import org.apache.solr.client.api.model.CreateCoreResponse; + +public interface CoreApis { + + /** V2 API definition for creating a core on the receiving Solr node */ + @Path("/cores") + interface Create { + @POST + @Operation(summary = "Create a new core on the receiving Solr node.", tags = "cores") + CreateCoreResponse createCore(CreateCoreParams requestBody) throws Exception; + } +} diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/request/beans/CreateCorePayload.java b/solr/api/src/java/org/apache/solr/client/api/model/CreateCoreParams.java similarity index 71% rename from solr/solrj/src/java/org/apache/solr/client/solrj/request/beans/CreateCorePayload.java rename to solr/api/src/java/org/apache/solr/client/api/model/CreateCoreParams.java index ff4a1a6425a..49de4b7fd5a 100644 --- a/solr/solrj/src/java/org/apache/solr/client/solrj/request/beans/CreateCorePayload.java +++ b/solr/api/src/java/org/apache/solr/client/api/model/CreateCoreParams.java @@ -14,15 +14,14 @@ * 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 java.util.Map; -import org.apache.solr.common.annotation.JsonProperty; -import org.apache.solr.common.util.ReflectMapWriter; -public class CreateCorePayload implements ReflectMapWriter { +public class CreateCoreParams { @JsonProperty(required = true) public String name; @@ -40,21 +39,21 @@ public class CreateCorePayload implements ReflectMapWriter { @JsonProperty public Boolean loadOnStartup; - // If our JsonProperty clone was more feature-rich here we could specify the property be called - // 'transient', but without that support it needs to be named something else to avoid conflicting - // with the 'transient' keyword in Java - @JsonProperty public Boolean isTransient; + @Schema(name = "isTransient") + @JsonProperty("transient") + public Boolean isTransient; @JsonProperty public String shard; @JsonProperty public String collection; - // TODO - what type is 'roles' expected to be? @JsonProperty public List roles; @JsonProperty public String replicaType; - @JsonProperty public Map properties; + @JsonProperty public Map properties; + + @JsonProperty public Map collectionProperties; @JsonProperty public String coreNodeName; diff --git a/solr/api/src/java/org/apache/solr/client/api/model/CreateCoreResponse.java b/solr/api/src/java/org/apache/solr/client/api/model/CreateCoreResponse.java new file mode 100644 index 00000000000..b9c4e576de7 --- /dev/null +++ b/solr/api/src/java/org/apache/solr/client/api/model/CreateCoreResponse.java @@ -0,0 +1,24 @@ +/* + * 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; + +/** Response for {@link org.apache.solr.client.api.endpoint.CoreApis} 'create' API */ +public class CreateCoreResponse extends SolrJerseyResponse { + @JsonProperty public String core; +} 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 c35117fb088..cb0aa3c0d11 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 @@ -30,7 +30,6 @@ import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; -import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; @@ -42,25 +41,22 @@ import org.apache.solr.api.Api; import org.apache.solr.api.JerseyResource; import org.apache.solr.cloud.CloudDescriptor; -import org.apache.solr.cloud.ZkController; import org.apache.solr.common.SolrException; import org.apache.solr.common.SolrException.ErrorCode; import org.apache.solr.common.cloud.ZkStateReader; import org.apache.solr.common.params.CommonAdminParams; import org.apache.solr.common.params.CoreAdminParams; import org.apache.solr.common.params.ModifiableSolrParams; -import org.apache.solr.common.params.SolrParams; import org.apache.solr.common.util.EnvUtils; import org.apache.solr.common.util.ExecutorUtil; import org.apache.solr.common.util.NamedList; import org.apache.solr.common.util.SolrNamedThreadFactory; -import org.apache.solr.common.util.StrUtils; import org.apache.solr.core.CoreContainer; 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.CoreSnapshot; -import org.apache.solr.handler.admin.api.CreateCoreAPI; +import org.apache.solr.handler.admin.api.CreateCore; import org.apache.solr.handler.admin.api.CreateCoreBackup; import org.apache.solr.handler.admin.api.GetNodeCommandStatus; import org.apache.solr.handler.admin.api.InstallCoreData; @@ -290,35 +286,6 @@ private static Map initializeOpMap() { return opMap; } - protected static Map buildCoreParams(SolrParams params) { - - Map coreParams = new HashMap<>(); - - // standard core create parameters - for (Map.Entry entry : paramToProp.entrySet()) { - String value = params.get(entry.getKey(), null); - if (StrUtils.isNotNullOrEmpty(value)) { - coreParams.put(entry.getValue(), value); - } - } - - // extra properties - Iterator paramsIt = params.getParameterNamesIterator(); - while (paramsIt.hasNext()) { - String param = paramsIt.next(); - if (param.startsWith(CoreAdminParams.PROPERTY_PREFIX)) { - String propName = param.substring(CoreAdminParams.PROPERTY_PREFIX.length()); - String propValue = params.get(param); - coreParams.put(propName, propValue); - } - if (param.startsWith(ZkController.COLLECTION_PARAM_PREFIX)) { - coreParams.put(param, params.get(param)); - } - } - - return coreParams; - } - protected static String normalizePath(String path) { if (path == null) return null; path = path.replace('/', File.separatorChar); @@ -384,7 +351,6 @@ public Collection getApis() { final List apis = new ArrayList<>(); apis.addAll(AnnotatedApi.getApis(new AllCoresStatusAPI(this))); apis.addAll(AnnotatedApi.getApis(new SingleCoreStatusAPI(this))); - apis.addAll(AnnotatedApi.getApis(new CreateCoreAPI(this))); apis.addAll(AnnotatedApi.getApis(new RejoinLeaderElectionAPI(this))); apis.addAll(AnnotatedApi.getApis(new OverseerOperationAPI(this))); apis.addAll(AnnotatedApi.getApis(new SplitCoreAPI(this))); @@ -403,6 +369,7 @@ public Collection> getJerseyResources() { return List.of( CoreSnapshot.class, InstallCoreData.class, + CreateCore.class, CreateCoreBackup.class, RestoreCore.class, ReloadCore.class, 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 0aafba7c70b..312d244798f 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 @@ -44,14 +44,11 @@ import static org.apache.solr.common.params.CoreAdminParams.REPLICA_TYPE; import static org.apache.solr.common.params.CoreAdminParams.SHARD; import static org.apache.solr.handler.admin.CoreAdminHandler.CallInfo; -import static org.apache.solr.handler.admin.CoreAdminHandler.buildCoreParams; import static org.apache.solr.handler.admin.CoreAdminHandler.normalizePath; import java.io.IOException; import java.lang.invoke.MethodHandles; -import java.nio.file.Path; import java.util.Locale; -import java.util.Map; import org.apache.solr.client.api.endpoint.SwapCoresApi; import org.apache.solr.client.api.model.ListCoreSnapshotsResponse; import org.apache.solr.client.api.model.ReloadCoreRequestBody; @@ -65,7 +62,6 @@ import org.apache.solr.common.params.CoreAdminParams; import org.apache.solr.common.params.SolrParams; import org.apache.solr.common.util.NamedList; -import org.apache.solr.common.util.PropertiesUtil; import org.apache.solr.common.util.SimpleOrderedMap; import org.apache.solr.common.util.StrUtils; import org.apache.solr.core.CoreContainer; @@ -73,6 +69,7 @@ import org.apache.solr.core.SolrCore; import org.apache.solr.handler.admin.CoreAdminHandler.CoreAdminOp; import org.apache.solr.handler.admin.api.CoreSnapshot; +import org.apache.solr.handler.admin.api.CreateCore; import org.apache.solr.handler.admin.api.GetNodeCommandStatus; import org.apache.solr.handler.admin.api.ReloadCore; import org.apache.solr.handler.admin.api.RenameCore; @@ -83,7 +80,6 @@ import org.apache.solr.update.UpdateLog; import org.apache.solr.util.NumberUtils; import org.apache.solr.util.RefCounted; -import org.apache.solr.util.TestInjection; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -92,32 +88,12 @@ public enum CoreAdminOperation implements CoreAdminOp { CREATE_OP( CREATE, it -> { - assert TestInjection.injectRandomDelayInCoreCreation(); - - SolrParams params = it.req.getParams(); - log().info("core create command {}", params); - String coreName = params.required().get(CoreAdminParams.NAME); - Map coreParams = buildCoreParams(params); - CoreContainer coreContainer = it.handler.coreContainer; - Path instancePath; - - // TODO: Should we nuke setting odd instance paths? They break core discovery, generally - String instanceDir = it.req.getParams().get(CoreAdminParams.INSTANCE_DIR); - if (instanceDir == null) instanceDir = it.req.getParams().get("property.instanceDir"); - if (instanceDir != null) { - instanceDir = - PropertiesUtil.substituteProperty( - instanceDir, coreContainer.getContainerProperties()); - instancePath = coreContainer.getCoreRootDirectory().resolve(instanceDir).normalize(); - } else { - instancePath = coreContainer.getCoreRootDirectory().resolve(coreName); - } - - boolean newCollection = params.getBool(CoreAdminParams.NEW_COLLECTION, false); - - coreContainer.create(coreName, instancePath, coreParams, newCollection); - - it.rsp.add("core", coreName); + final var createParams = CreateCore.createRequestBodyFromV1Params(it.req.getParams()); + final var createCoreApi = + new CreateCore( + it.handler.coreContainer, it.handler.getCoreAdminAsyncTracker(), it.req, it.rsp); + final var response = createCoreApi.createCore(createParams); + V2ApiUtils.squashIntoSolrResponseWithoutHeader(it.rsp, response); }), UNLOAD_OP( UNLOAD, diff --git a/solr/core/src/java/org/apache/solr/handler/admin/api/CreateCore.java b/solr/core/src/java/org/apache/solr/handler/admin/api/CreateCore.java new file mode 100644 index 00000000000..0b82500bb8e --- /dev/null +++ b/solr/core/src/java/org/apache/solr/handler/admin/api/CreateCore.java @@ -0,0 +1,196 @@ +/* + * 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.handler.admin.api; + +import static org.apache.solr.cloud.ZkController.COLLECTION_PARAM_PREFIX; +import static org.apache.solr.common.params.CollectionAdminParams.PROPERTY_PREFIX; +import static org.apache.solr.common.params.CoreAdminParams.ACTION; +import static org.apache.solr.common.params.CoreAdminParams.ROLES; +import static org.apache.solr.handler.admin.CoreAdminHandler.paramToProp; +import static org.apache.solr.security.PermissionNameProvider.Name.CORE_EDIT_PERM; + +import com.fasterxml.jackson.core.type.TypeReference; +import jakarta.inject.Inject; +import java.lang.invoke.MethodHandles; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import org.apache.solr.client.api.endpoint.CoreApis; +import org.apache.solr.client.api.model.CreateCoreParams; +import org.apache.solr.client.api.model.CreateCoreResponse; +import org.apache.solr.cloud.ZkController; +import org.apache.solr.common.params.SolrParams; +import org.apache.solr.common.util.CollectionUtil; +import org.apache.solr.common.util.PropertiesUtil; +import org.apache.solr.common.util.StrUtils; +import org.apache.solr.common.util.Utils; +import org.apache.solr.core.CoreContainer; +import org.apache.solr.handler.admin.CoreAdminHandler; +import org.apache.solr.jersey.PermissionName; +import org.apache.solr.jersey.SolrJacksonMapper; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.response.SolrQueryResponse; +import org.apache.solr.util.TestInjection; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * V2 API for creating a new core on the receiving node. + * + *

This API (POST /api/cores {...}) is analogous to the v1 /admin/cores?action=CREATE command. + */ +public class CreateCore extends CoreAdminAPIBase implements CoreApis.Create { + private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + + @Inject + public CreateCore( + CoreContainer coreContainer, + CoreAdminHandler.CoreAdminAsyncTracker coreAdminAsyncTracker, + SolrQueryRequest solrQueryRequest, + SolrQueryResponse solrQueryResponse) { + super(coreContainer, coreAdminAsyncTracker, solrQueryRequest, solrQueryResponse); + } + + @Override + @PermissionName(CORE_EDIT_PERM) + public CreateCoreResponse createCore(CreateCoreParams requestBody) throws Exception { + final var response = instantiateJerseyResponse(CreateCoreResponse.class); + + assert TestInjection.injectRandomDelayInCoreCreation(); + ensureRequiredRequestBodyProvided(requestBody); + ensureRequiredParameterProvided("name", requestBody.name); + + return handlePotentiallyAsynchronousTask( + response, + requestBody.name, + requestBody.async, + "create", + () -> { + return doCreate(requestBody, response); + }); + } + + private CreateCoreResponse doCreate(CreateCoreParams requestBody, CreateCoreResponse response) { + assert TestInjection.injectRandomDelayInCoreCreation(); + + if (log.isInfoEnabled()) { + log.info("core create command {}", Utils.toJSONString(requestBody)); + } + Map coreParams = buildCoreParams(requestBody); + final var instancePath = determineInstancePath(requestBody); + + boolean newCollection = Boolean.TRUE.equals(requestBody.newCollection); + + coreContainer.create(requestBody.name, instancePath, coreParams, newCollection); + + response.core = requestBody.name; + return response; + } + + // TODO: Should we nuke setting odd instance paths? They break core discovery, generally + private Path determineInstancePath(CreateCoreParams createParams) { + String instanceDir = createParams.instanceDir; + if (instanceDir == null && CollectionUtil.isNotEmpty(createParams.properties)) + instanceDir = createParams.properties.get("instanceDir"); + + if (instanceDir != null) { + instanceDir = + PropertiesUtil.substituteProperty(instanceDir, coreContainer.getContainerProperties()); + return coreContainer.getCoreRootDirectory().resolve(instanceDir).normalize(); + } else { + return coreContainer.getCoreRootDirectory().resolve(createParams.name); + } + } + + // TODO create-core API accepts a strongly-typed object, but CoreContainer, CoreDescriptor, et al. + // still consume a Map of properties, persist this to disk as a properties file. + // We should think through how far to push the strong-typing down into things, if/how it impacts + // on-disk representations (e.g. core.properties), etc. + private Map buildCoreParams(CreateCoreParams createParams) { + final var createParamMap = + SolrJacksonMapper.getObjectMapper() + .convertValue(createParams, new TypeReference>() {}); + final var coreParams = new HashMap(); + + // Copy standard core create parameters from paramToProp allowlist + for (Map.Entry entry : paramToProp.entrySet()) { + if (createParamMap.containsKey(entry.getKey())) { + Object value = createParamMap.get(entry.getKey()); + if (value != null) { + if (entry.getKey().equals(ROLES) && value instanceof List) { + @SuppressWarnings("unchecked") + final var values = (List) value; + value = StrUtils.join(values, ','); + } + coreParams.put(entry.getValue(), value.toString()); + } + } + } + + // User-properties are added without any prefix + if (CollectionUtil.isNotEmpty(createParams.properties)) { + coreParams.putAll(createParams.properties); + } + // "Collection"-specific properties are given a "collection." prefix in the flattened map + if (CollectionUtil.isNotEmpty(createParams.collectionProperties)) { + createParams.collectionProperties.entrySet().stream() + .forEach( + entry -> { + coreParams.put( + ZkController.COLLECTION_PARAM_PREFIX + entry.getKey(), entry.getValue()); + }); + } + + return coreParams; + } + + public static CreateCoreParams createRequestBodyFromV1Params(SolrParams solrParams) { + final var v1ParamMap = solrParams.toMap(new HashMap<>()); + v1ParamMap.remove(ACTION); + + final var coreProperties = new HashMap(); + final var collectionProperties = new HashMap(); + + Iterator paramIterator = solrParams.getParameterNamesIterator(); + while (paramIterator.hasNext()) { + final String key = paramIterator.next(); + final Object value = v1ParamMap.get(key); + if (key.startsWith(PROPERTY_PREFIX)) { + v1ParamMap.remove(key); + coreProperties.put(key.substring(PROPERTY_PREFIX.length()), value.toString()); + } + if (key.startsWith(COLLECTION_PARAM_PREFIX)) { + v1ParamMap.remove(key); + collectionProperties.put(key.substring(COLLECTION_PARAM_PREFIX.length()), value.toString()); + } + if (key.equals(ROLES)) { + v1ParamMap.put(ROLES, value.toString().split(",")); + } + } + if (CollectionUtil.isNotEmpty(coreProperties)) { + v1ParamMap.put("properties", coreProperties); + } + if (CollectionUtil.isNotEmpty(collectionProperties)) { + v1ParamMap.put("collectionProperties", collectionProperties); + } + + return SolrJacksonMapper.getObjectMapper().convertValue(v1ParamMap, CreateCoreParams.class); + } +} diff --git a/solr/core/src/java/org/apache/solr/handler/admin/api/CreateCoreAPI.java b/solr/core/src/java/org/apache/solr/handler/admin/api/CreateCoreAPI.java deleted file mode 100644 index d797c88e0be..00000000000 --- a/solr/core/src/java/org/apache/solr/handler/admin/api/CreateCoreAPI.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.solr.handler.admin.api; - -import static org.apache.solr.client.solrj.SolrRequest.METHOD.POST; -import static org.apache.solr.common.params.CollectionAdminParams.PROPERTY_PREFIX; -import static org.apache.solr.common.params.CoreAdminParams.ACTION; -import static org.apache.solr.common.params.CoreAdminParams.CoreAdminAction.CREATE; -import static org.apache.solr.handler.ClusterAPI.wrapParams; -import static org.apache.solr.handler.api.V2ApiUtils.flattenMapWithPrefix; -import static org.apache.solr.handler.api.V2ApiUtils.flattenToCommaDelimitedString; -import static org.apache.solr.security.PermissionNameProvider.Name.CORE_EDIT_PERM; - -import java.util.HashMap; -import java.util.Locale; -import java.util.Map; -import org.apache.solr.api.Command; -import org.apache.solr.api.EndPoint; -import org.apache.solr.api.PayloadObj; -import org.apache.solr.client.solrj.request.beans.CreateCorePayload; -import org.apache.solr.handler.admin.CoreAdminHandler; - -/** - * V2 API for creating a new core on the receiving node. - * - *

This API (POST /v2/cores {'create': {...}}) is analogous to the v1 /admin/cores?action=CREATE - * command. - * - * @see CreateCorePayload - */ -@EndPoint( - path = {"/cores"}, - method = POST, - permission = CORE_EDIT_PERM) -public class CreateCoreAPI { - public static final String V2_CREATE_CORE_CMD = "create"; - - private final CoreAdminHandler coreAdminHandler; - - public CreateCoreAPI(CoreAdminHandler coreAdminHandler) { - this.coreAdminHandler = coreAdminHandler; - } - - @Command(name = V2_CREATE_CORE_CMD) - public void createCore(PayloadObj obj) throws Exception { - final CreateCorePayload v2Body = obj.get(); - final Map v1Params = v2Body.toMap(new HashMap<>()); - v1Params.put(ACTION, CREATE.name().toLowerCase(Locale.ROOT)); - - if (v2Body.isTransient != null) { - v1Params.put("transient", v1Params.remove("isTransient")); - } - if (v2Body.properties != null) { - v1Params.remove("properties"); - flattenMapWithPrefix(v2Body.properties, v1Params, PROPERTY_PREFIX); - } - if (v2Body.roles != null && !v2Body.roles.isEmpty()) { - v1Params.remove("roles"); // Not strictly needed, since the method below would overwrite it. - flattenToCommaDelimitedString(v1Params, v2Body.roles, "roles"); - } - - coreAdminHandler.handleRequestBody(wrapParams(obj.getRequest(), v1Params), obj.getResponse()); - } -} diff --git a/solr/core/src/java/org/apache/solr/jersey/SolrJacksonMapper.java b/solr/core/src/java/org/apache/solr/jersey/SolrJacksonMapper.java index b3444070701..7f57715993c 100644 --- a/solr/core/src/java/org/apache/solr/jersey/SolrJacksonMapper.java +++ b/solr/core/src/java/org/apache/solr/jersey/SolrJacksonMapper.java @@ -19,6 +19,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.module.SimpleModule; @@ -49,6 +50,9 @@ private static ObjectMapper createObjectMapper() { customTypeModule.addSerializer(new NamedListSerializer(NamedList.class)); return new ObjectMapper() + // TODO Should failOnUnknown=false be made available on a "permissive" object mapper instead + // of the "main" one? + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) .setSerializationInclusion(JsonInclude.Include.NON_NULL) .registerModule(customTypeModule); } diff --git a/solr/core/src/test/org/apache/solr/handler/admin/V2CoresAPIMappingTest.java b/solr/core/src/test/org/apache/solr/handler/admin/V2CoresAPIMappingTest.java index 6c692880ec8..9d0ba0635c8 100644 --- a/solr/core/src/test/org/apache/solr/handler/admin/V2CoresAPIMappingTest.java +++ b/solr/core/src/test/org/apache/solr/handler/admin/V2CoresAPIMappingTest.java @@ -17,34 +17,17 @@ package org.apache.solr.handler.admin; -import static org.apache.solr.common.params.CollectionAdminParams.NUM_SHARDS; -import static org.apache.solr.common.params.CommonAdminParams.ASYNC; import static org.apache.solr.common.params.CommonParams.ACTION; -import static org.apache.solr.common.params.CoreAdminParams.COLLECTION; -import static org.apache.solr.common.params.CoreAdminParams.CONFIG; -import static org.apache.solr.common.params.CoreAdminParams.CONFIGSET; import static org.apache.solr.common.params.CoreAdminParams.CORE; -import static org.apache.solr.common.params.CoreAdminParams.CORE_NODE_NAME; -import static org.apache.solr.common.params.CoreAdminParams.CoreAdminAction.CREATE; import static org.apache.solr.common.params.CoreAdminParams.CoreAdminAction.STATUS; -import static org.apache.solr.common.params.CoreAdminParams.DATA_DIR; import static org.apache.solr.common.params.CoreAdminParams.INDEX_INFO; -import static org.apache.solr.common.params.CoreAdminParams.INSTANCE_DIR; -import static org.apache.solr.common.params.CoreAdminParams.LOAD_ON_STARTUP; -import static org.apache.solr.common.params.CoreAdminParams.NAME; -import static org.apache.solr.common.params.CoreAdminParams.NEW_COLLECTION; -import static org.apache.solr.common.params.CoreAdminParams.REPLICA_TYPE; -import static org.apache.solr.common.params.CoreAdminParams.ROLES; -import static org.apache.solr.common.params.CoreAdminParams.SCHEMA; -import static org.apache.solr.common.params.CoreAdminParams.SHARD; -import static org.apache.solr.common.params.CoreAdminParams.TRANSIENT; -import static org.apache.solr.common.params.CoreAdminParams.ULOG_DIR; import java.util.Locale; import java.util.Map; +import org.apache.solr.common.params.ModifiableSolrParams; import org.apache.solr.common.params.SolrParams; import org.apache.solr.handler.admin.api.AllCoresStatusAPI; -import org.apache.solr.handler.admin.api.CreateCoreAPI; +import org.apache.solr.handler.admin.api.CreateCore; import org.apache.solr.handler.admin.api.SingleCoreStatusAPI; import org.junit.Test; @@ -61,7 +44,6 @@ public class V2CoresAPIMappingTest extends V2ApiMappingTest { @Override public void populateApiBag() { final CoreAdminHandler handler = getRequestHandler(); - apiBag.registerObject(new CreateCoreAPI(handler)); apiBag.registerObject(new SingleCoreStatusAPI(handler)); apiBag.registerObject(new AllCoresStatusAPI(handler)); } @@ -77,52 +59,56 @@ public boolean isCoreSpecific() { } @Test - public void testCreateCoreAllProperties() throws Exception { - final SolrParams v1Params = - captureConvertedV1Params( - "/cores", - "POST", - "{'create': {" - + "'name': 'someCoreName', " - + "'instanceDir': 'someInstanceDir', " - + "'dataDir': 'someDataDir', " - + "'ulogDir': 'someUpdateLogDirectory', " - + "'schema': 'some-schema-file-name', " - + "'config': 'some-config-file-name', " - + "'configSet': 'someConfigSetName', " - + "'loadOnStartup': true, " - + "'isTransient': true, " - + "'shard': 'someShardName', " - + "'collection': 'someCollectionName', " - + "'replicaType': 'TLOG', " - + "'coreNodeName': 'someNodeName', " - + "'numShards': 123, " - + "'roles': ['role1', 'role2'], " - + "'properties': {'prop1': 'val1', 'prop2': 'val2'}, " - + "'newCollection': true, " - + "'async': 'requestTrackingId' " - + "}}"); + public void testCreateCoreRequestBodyMappingAllParams() throws Exception { + final ModifiableSolrParams v1Params = new ModifiableSolrParams(); + v1Params.add("action", "create"); + v1Params.add("name", "someCoreName"); + v1Params.add("instanceDir", "some/instance/dir"); + v1Params.add("config", "some-config.xml"); + v1Params.add("schema", "some-schema.xml"); + v1Params.add("dataDir", "some/data/dir"); + v1Params.add("ulogDir", "some/ulog/dir"); + v1Params.add("configSet", "someConfigset"); + v1Params.add("shard", "shard1"); + v1Params.add("loadOnStartup", "true"); + v1Params.add("transient", "true"); + v1Params.add("coreNodeName", "someNodeName"); + v1Params.add("newCollection", "true"); + v1Params.add("async", "someAsyncId"); + v1Params.add("roles", "role1,role2"); + v1Params.add("collection", "someCollection"); + v1Params.add("property.foo", "fooVal"); + v1Params.add("property.bar", "barVal"); + v1Params.add("collection.abc", "abcVal"); + v1Params.add("collection.xyz", "xyzVal"); + + final var createRequestBody = CreateCore.createRequestBodyFromV1Params(v1Params); - assertEquals(CREATE.name().toLowerCase(Locale.ROOT), v1Params.get(ACTION)); - assertEquals("someCoreName", v1Params.get(NAME)); - assertEquals("someInstanceDir", v1Params.get(INSTANCE_DIR)); - assertEquals("someDataDir", v1Params.get(DATA_DIR)); - assertEquals("someUpdateLogDirectory", v1Params.get(ULOG_DIR)); - assertEquals("some-schema-file-name", v1Params.get(SCHEMA)); - assertEquals("some-config-file-name", v1Params.get(CONFIG)); - assertEquals("someConfigSetName", v1Params.get(CONFIGSET)); - assertTrue(v1Params.getPrimitiveBool(LOAD_ON_STARTUP)); - assertTrue(v1Params.getPrimitiveBool(TRANSIENT)); - assertEquals("someShardName", v1Params.get(SHARD)); - assertEquals("someCollectionName", v1Params.get(COLLECTION)); - assertEquals("TLOG", v1Params.get(REPLICA_TYPE)); - assertEquals("someNodeName", v1Params.get(CORE_NODE_NAME)); - assertEquals(123, v1Params.getPrimitiveInt(NUM_SHARDS)); - assertEquals("role1,role2", v1Params.get(ROLES)); - assertEquals("val1", v1Params.get("property.prop1")); - assertEquals("val2", v1Params.get("property.prop2")); - assertTrue(v1Params.getPrimitiveBool(NEW_COLLECTION)); - assertEquals("requestTrackingId", v1Params.get(ASYNC)); + assertEquals("someCoreName", createRequestBody.name); + assertEquals("some/instance/dir", createRequestBody.instanceDir); + assertEquals("some-config.xml", createRequestBody.config); + assertEquals("some-schema.xml", createRequestBody.schema); + assertEquals("some/data/dir", createRequestBody.dataDir); + assertEquals("some/ulog/dir", createRequestBody.ulogDir); + assertEquals("someConfigset", createRequestBody.configSet); + assertEquals("someCollection", createRequestBody.collection); + assertEquals("shard1", createRequestBody.shard); + assertEquals(Boolean.TRUE, createRequestBody.loadOnStartup); + assertEquals(Boolean.TRUE, createRequestBody.isTransient); + assertEquals(Boolean.TRUE, createRequestBody.newCollection); + assertEquals("someNodeName", createRequestBody.coreNodeName); + assertEquals("someAsyncId", createRequestBody.async); + assertEquals(2, createRequestBody.roles.size()); + assertEquals("role1", createRequestBody.roles.get(0)); + assertEquals("role2", createRequestBody.roles.get(1)); + assertNotNull(createRequestBody.properties); + assertEquals(2, createRequestBody.properties.size()); + assertEquals("fooVal", createRequestBody.properties.get("foo")); + assertEquals("barVal", createRequestBody.properties.get("bar")); + assertNotNull(createRequestBody.collectionProperties); + assertEquals(2, createRequestBody.collectionProperties.size()); + assertEquals("abcVal", createRequestBody.collectionProperties.get("abc")); + assertEquals("xyzVal", createRequestBody.collectionProperties.get("xyz")); } @Test diff --git a/solr/solrj/src/java/org/apache/solr/common/util/CollectionUtil.java b/solr/solrj/src/java/org/apache/solr/common/util/CollectionUtil.java index 92282e1ccfc..3b40f14bf5c 100644 --- a/solr/solrj/src/java/org/apache/solr/common/util/CollectionUtil.java +++ b/solr/solrj/src/java/org/apache/solr/common/util/CollectionUtil.java @@ -20,6 +20,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; +import java.util.Map; /** * Methods for creating collections with exact sizes, and other convenience methods @@ -69,4 +70,12 @@ public static boolean isEmpty(Collection collection) { public static boolean isNotEmpty(Collection collection) { return !isEmpty(collection); } + + public static boolean isEmpty(Map map) { + return map == null || map.isEmpty(); + } + + public static boolean isNotEmpty(Map map) { + return !isEmpty(map); + } } From f21a7ffe2ef98409c44ab8d52d01d60680c3c59b Mon Sep 17 00:00:00 2001 From: Jason Gerlowski Date: Wed, 15 Jan 2025 13:47:54 -0500 Subject: [PATCH 02/10] Fix tests, 1 --- .../java/org/apache/solr/handler/admin/api/CreateCore.java | 2 ++ .../org/apache/solr/handler/admin/V2CoresAPIMappingTest.java | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/solr/core/src/java/org/apache/solr/handler/admin/api/CreateCore.java b/solr/core/src/java/org/apache/solr/handler/admin/api/CreateCore.java index 0b82500bb8e..bc45bf5049c 100644 --- a/solr/core/src/java/org/apache/solr/handler/admin/api/CreateCore.java +++ b/solr/core/src/java/org/apache/solr/handler/admin/api/CreateCore.java @@ -19,6 +19,7 @@ import static org.apache.solr.cloud.ZkController.COLLECTION_PARAM_PREFIX; import static org.apache.solr.common.params.CollectionAdminParams.PROPERTY_PREFIX; +import static org.apache.solr.common.params.CommonAdminParams.ASYNC; import static org.apache.solr.common.params.CoreAdminParams.ACTION; import static org.apache.solr.common.params.CoreAdminParams.ROLES; import static org.apache.solr.handler.admin.CoreAdminHandler.paramToProp; @@ -164,6 +165,7 @@ private Map buildCoreParams(CreateCoreParams createParams) { public static CreateCoreParams createRequestBodyFromV1Params(SolrParams solrParams) { final var v1ParamMap = solrParams.toMap(new HashMap<>()); v1ParamMap.remove(ACTION); + v1ParamMap.remove(ASYNC); final var coreProperties = new HashMap(); final var collectionProperties = new HashMap(); diff --git a/solr/core/src/test/org/apache/solr/handler/admin/V2CoresAPIMappingTest.java b/solr/core/src/test/org/apache/solr/handler/admin/V2CoresAPIMappingTest.java index 9d0ba0635c8..42fb31beee3 100644 --- a/solr/core/src/test/org/apache/solr/handler/admin/V2CoresAPIMappingTest.java +++ b/solr/core/src/test/org/apache/solr/handler/admin/V2CoresAPIMappingTest.java @@ -97,7 +97,6 @@ public void testCreateCoreRequestBodyMappingAllParams() throws Exception { assertEquals(Boolean.TRUE, createRequestBody.isTransient); assertEquals(Boolean.TRUE, createRequestBody.newCollection); assertEquals("someNodeName", createRequestBody.coreNodeName); - assertEquals("someAsyncId", createRequestBody.async); assertEquals(2, createRequestBody.roles.size()); assertEquals("role1", createRequestBody.roles.get(0)); assertEquals("role2", createRequestBody.roles.get(1)); @@ -109,6 +108,9 @@ public void testCreateCoreRequestBodyMappingAllParams() throws Exception { assertEquals(2, createRequestBody.collectionProperties.size()); assertEquals("abcVal", createRequestBody.collectionProperties.get("abc")); assertEquals("xyzVal", createRequestBody.collectionProperties.get("xyz")); + + // V1 codepath handles the async/taskId differently, and it's not passed down the v2 code + assertEquals(null, createRequestBody.async); } @Test From 72d5e2a907107414aac6613983128a461e741259 Mon Sep 17 00:00:00 2001 From: Jason Gerlowski Date: Wed, 15 Jan 2025 15:03:44 -0500 Subject: [PATCH 03/10] WIP: single and multi-core status JAX-RS conversion This is not working, or even compiling, but I wanted to checkpoint my work to share it between machines. When resuming, look for the TODO comment in StatusOp.java. --- .../solr/client/api/endpoint/CoreApis.java | 17 +++++ .../client/api/model/CoreStatusResponse.java | 66 +++++++++++++++++++ .../solr/handler/admin/CoreAdminHandler.java | 4 +- .../apache/solr/handler/admin/StatusOp.java | 48 ++++++++++---- .../handler/admin/api/AllCoresStatusAPI.java | 2 +- ...eCoreStatusAPI.java => CoreStatusAPI.java} | 33 ++++++---- .../handler/admin/V2CoresAPIMappingTest.java | 5 +- 7 files changed, 148 insertions(+), 27 deletions(-) create mode 100644 solr/api/src/java/org/apache/solr/client/api/model/CoreStatusResponse.java rename solr/core/src/java/org/apache/solr/handler/admin/api/{SingleCoreStatusAPI.java => CoreStatusAPI.java} (64%) diff --git a/solr/api/src/java/org/apache/solr/client/api/endpoint/CoreApis.java b/solr/api/src/java/org/apache/solr/client/api/endpoint/CoreApis.java index b87879dc8f5..7c6ccc4d3e6 100644 --- a/solr/api/src/java/org/apache/solr/client/api/endpoint/CoreApis.java +++ b/solr/api/src/java/org/apache/solr/client/api/endpoint/CoreApis.java @@ -17,10 +17,14 @@ package org.apache.solr.client.api.endpoint; import io.swagger.v3.oas.annotations.Operation; +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.CreateCoreParams; import org.apache.solr.client.api.model.CreateCoreResponse; +import org.apache.solr.client.api.model.SolrJerseyResponse; public interface CoreApis { @@ -31,4 +35,17 @@ interface Create { @Operation(summary = "Create a new core on the receiving Solr node.", tags = "cores") CreateCoreResponse createCore(CreateCoreParams requestBody) throws Exception; } + + @Path("/cores") + interface GetStatus { + + @GET + @Operation(summary = "Fetch status info for all cores hosted on this node.", tags = "cores") + SolrJerseyResponse getAllCoreStatus(@QueryParam("indexInfo") Boolean indexInfo); + + @GET + @Operation(summary = "Fetch status info for the core hosted on this node with the specified name.", tags = "cores") + @Path("/{coreName}") + SolrJerseyResponse getCoreStatus(@PathParam("coreName") String coreName, @QueryParam("indexInfo") Boolean indexInfo); + } } diff --git a/solr/api/src/java/org/apache/solr/client/api/model/CoreStatusResponse.java b/solr/api/src/java/org/apache/solr/client/api/model/CoreStatusResponse.java new file mode 100644 index 00000000000..eca69b3465a --- /dev/null +++ b/solr/api/src/java/org/apache/solr/client/api/model/CoreStatusResponse.java @@ -0,0 +1,66 @@ +/* + * 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 java.util.Map; + +public class CoreStatusResponse extends SolrJerseyResponse { + + // In the v1 code this is a Map of Exception instances by core name. How are exceptions serialized out by things though, that's what I'd have to mirror on the v2 side. + @JsonProperty public Map initFailures; + + @JsonProperty public Map status; + + public static class SingleCoreData { + @JsonProperty String name; + @JsonProperty String instanceDir; + @JsonProperty String dataDir; + @JsonProperty String config; + @JsonProperty String schema; + @JsonProperty String startTime; // TODO NOCOMMIT Date? Instant? + @JsonProperty Long uptime; + @JsonProperty String lastPublished; + @JsonProperty Integer configVersion; + @JsonProperty CloudDetails cloud; + @JsonProperty IndexDetails index; + } + + public static class CloudDetails { + @JsonProperty public String collection; + @JsonProperty public String shard; + @JsonProperty public String replica; + @JsonProperty public String replicaType; // TODO enum? + } + + public static class IndexDetails { + @JsonProperty public Integer numDocs; + @JsonProperty public Integer maxDoc; + @JsonProperty public Integer deletedDocs; + @JsonProperty public Integer version; + @JsonProperty public Integer segmentCount; + @JsonProperty public Boolean current; + @JsonProperty public Boolean hasDeletions; + @JsonProperty public String directory; + @JsonProperty public String segmentsFile; + @JsonProperty public Integer segmentsFileSizeInBytes; + @JsonProperty public Map userData; + @JsonProperty public Integer sizeInBytes; + @JsonProperty public String size; // Human readable representation of 'sizeInBytes' + } +} 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 cb0aa3c0d11..42b6fd64267 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 @@ -71,7 +71,7 @@ import org.apache.solr.handler.admin.api.RequestCoreRecoveryAPI; import org.apache.solr.handler.admin.api.RequestSyncShardAPI; import org.apache.solr.handler.admin.api.RestoreCore; -import org.apache.solr.handler.admin.api.SingleCoreStatusAPI; +import org.apache.solr.handler.admin.api.CoreStatusAPI; import org.apache.solr.handler.admin.api.SplitCoreAPI; import org.apache.solr.handler.admin.api.SwapCores; import org.apache.solr.handler.admin.api.UnloadCore; @@ -350,7 +350,6 @@ void call() throws Exception { public Collection getApis() { final List apis = new ArrayList<>(); apis.addAll(AnnotatedApi.getApis(new AllCoresStatusAPI(this))); - apis.addAll(AnnotatedApi.getApis(new SingleCoreStatusAPI(this))); apis.addAll(AnnotatedApi.getApis(new RejoinLeaderElectionAPI(this))); apis.addAll(AnnotatedApi.getApis(new OverseerOperationAPI(this))); apis.addAll(AnnotatedApi.getApis(new SplitCoreAPI(this))); @@ -368,6 +367,7 @@ public Collection getApis() { public Collection> getJerseyResources() { return List.of( CoreSnapshot.class, + CoreStatusAPI.class, InstallCoreData.class, CreateCore.class, CreateCoreBackup.class, diff --git a/solr/core/src/java/org/apache/solr/handler/admin/StatusOp.java b/solr/core/src/java/org/apache/solr/handler/admin/StatusOp.java index c42e7c43c2d..cb3c4c5ceb0 100644 --- a/solr/core/src/java/org/apache/solr/handler/admin/StatusOp.java +++ b/solr/core/src/java/org/apache/solr/handler/admin/StatusOp.java @@ -17,10 +17,13 @@ package org.apache.solr.handler.admin; +import java.io.IOException; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; + +import org.apache.solr.client.api.model.CoreStatusResponse; import org.apache.solr.common.params.CoreAdminParams; import org.apache.solr.common.params.SolrParams; import org.apache.solr.common.util.NamedList; @@ -28,6 +31,39 @@ import org.apache.solr.core.CoreContainer; class StatusOp implements CoreAdminHandler.CoreAdminOp { + + public static CoreStatusResponse fetchStatusInfo(CoreContainer coreContainer, String coreName, Boolean indexInfo) throws IOException { + final var response = new CoreStatusResponse(); + response.initFailures = new HashMap<>(); + + for (Map.Entry failure : coreContainer.getCoreInitFailures().entrySet()) { + + // Skip irrelevant initFailures if we're only interested in a single core + if (coreName != null && !failure.getKey().equals(coreName)) + continue; + + response.initFailures.put(failure.getKey(), failure.getValue().exception); + } + + // Populate status for each core + final var coreNameList = (coreName != null) ? List.of(coreName) : coreContainer.getAllCoreNames(); + coreNameList.sort(null); + response.status = new HashMap<>(); + for (String toPopulate : coreNameList) { + // TODO This is where I left off at 1/15. Next TODO items are: + // 1. port CoreAdminOperation.getCoreStatus over to return a POJO + // 2. modify execute() below to call this method (fetchStatusInfo), and then squash the strong-type into a NL + // 3. Move this method (fetchStatusInfo) into the v2 API class, and use it to implement both single and all-core fetching + // 4. Trace through the v1 and v2 codepaths to make sure registration, plumbing, etc. are all in place + // 5. Look into how v1 code currently serializes Exceptions (are we just toString-ing them) + // 6. Look into NOCOMMIT and other todo comments + // 7. Look into the failing tests from create-core side + response.status.put(toPopulate, CoreAdminOperation.getCoreStatus(coreContainer, toPopulate, indexInfo)); + } + + return response; + } + @Override public void execute(CoreAdminHandler.CallInfo it) throws Exception { SolrParams params = it.req.getParams(); @@ -36,11 +72,7 @@ public void execute(CoreAdminHandler.CallInfo it) throws Exception { String indexInfo = params.get(CoreAdminParams.INDEX_INFO); boolean isIndexInfoNeeded = Boolean.parseBoolean(null == indexInfo ? "true" : indexInfo); NamedList status = new SimpleOrderedMap<>(); - Map failures = new HashMap<>(); - for (Map.Entry failure : - it.handler.coreContainer.getCoreInitFailures().entrySet()) { - failures.put(failure.getKey(), failure.getValue().exception); - } + if (cname == null) { List nameList = it.handler.coreContainer.getAllCoreNames(); nameList.sort(null); @@ -49,13 +81,7 @@ public void execute(CoreAdminHandler.CallInfo it) throws Exception { name, CoreAdminOperation.getCoreStatus(it.handler.coreContainer, name, isIndexInfoNeeded)); } - it.rsp.add("initFailures", failures); } else { - failures = - failures.containsKey(cname) - ? Collections.singletonMap(cname, failures.get(cname)) - : Collections.emptyMap(); - it.rsp.add("initFailures", failures); status.add( cname, CoreAdminOperation.getCoreStatus(it.handler.coreContainer, cname, isIndexInfoNeeded)); diff --git a/solr/core/src/java/org/apache/solr/handler/admin/api/AllCoresStatusAPI.java b/solr/core/src/java/org/apache/solr/handler/admin/api/AllCoresStatusAPI.java index 87f7c9bffa9..a1c2b75bc1d 100644 --- a/solr/core/src/java/org/apache/solr/handler/admin/api/AllCoresStatusAPI.java +++ b/solr/core/src/java/org/apache/solr/handler/admin/api/AllCoresStatusAPI.java @@ -33,7 +33,7 @@ * *

This API (GET /v2/cores is analogous to the v1 /admin/cores?action=status command. * - * @see SingleCoreStatusAPI + * @see CoreStatusAPI */ public class AllCoresStatusAPI { diff --git a/solr/core/src/java/org/apache/solr/handler/admin/api/SingleCoreStatusAPI.java b/solr/core/src/java/org/apache/solr/handler/admin/api/CoreStatusAPI.java similarity index 64% rename from solr/core/src/java/org/apache/solr/handler/admin/api/SingleCoreStatusAPI.java rename to solr/core/src/java/org/apache/solr/handler/admin/api/CoreStatusAPI.java index 7253bd35b69..17a3578da02 100644 --- a/solr/core/src/java/org/apache/solr/handler/admin/api/SingleCoreStatusAPI.java +++ b/solr/core/src/java/org/apache/solr/handler/admin/api/CoreStatusAPI.java @@ -24,8 +24,13 @@ import static org.apache.solr.handler.ClusterAPI.wrapParams; import static org.apache.solr.security.PermissionNameProvider.Name.CORE_READ_PERM; +import jakarta.inject.Inject; import org.apache.solr.api.EndPoint; +import org.apache.solr.client.api.endpoint.CoreApis; +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.PermissionName; import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.response.SolrQueryResponse; @@ -37,20 +42,26 @@ * * @see AllCoresStatusAPI */ -public class SingleCoreStatusAPI { +public class CoreStatusAPI extends CoreAdminAPIBase implements CoreApis.GetStatus { - private final CoreAdminHandler coreAdminHandler; + @Inject + public CoreStatusAPI( + CoreContainer coreContainer, + CoreAdminHandler.CoreAdminAsyncTracker coreAdminAsyncTracker, + SolrQueryRequest solrQueryRequest, + SolrQueryResponse solrQueryResponse) { + super(coreContainer, coreAdminAsyncTracker, solrQueryRequest, solrQueryResponse); + } - public SingleCoreStatusAPI(CoreAdminHandler coreAdminHandler) { - this.coreAdminHandler = coreAdminHandler; + @Override + @PermissionName(CORE_READ_PERM) + public SolrJerseyResponse getAllCoreStatus(Boolean indexInfo) { + return null; } - @EndPoint( - path = {"/cores/{core}"}, - method = GET, - permission = CORE_READ_PERM) - public void getStatusOfSingleCore(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception { - req = wrapParams(req, ACTION, STATUS, CORE, req.getPathTemplateValues().get(CORE)); - coreAdminHandler.handleRequestBody(req, rsp); + @Override + @PermissionName(CORE_READ_PERM) + public SolrJerseyResponse getCoreStatus(String coreName, Boolean indexInfo) { + return null; } } diff --git a/solr/core/src/test/org/apache/solr/handler/admin/V2CoresAPIMappingTest.java b/solr/core/src/test/org/apache/solr/handler/admin/V2CoresAPIMappingTest.java index 42fb31beee3..d79658efdd3 100644 --- a/solr/core/src/test/org/apache/solr/handler/admin/V2CoresAPIMappingTest.java +++ b/solr/core/src/test/org/apache/solr/handler/admin/V2CoresAPIMappingTest.java @@ -28,7 +28,7 @@ import org.apache.solr.common.params.SolrParams; import org.apache.solr.handler.admin.api.AllCoresStatusAPI; import org.apache.solr.handler.admin.api.CreateCore; -import org.apache.solr.handler.admin.api.SingleCoreStatusAPI; +import org.apache.solr.handler.admin.api.CoreStatusAPI; import org.junit.Test; /** @@ -44,7 +44,6 @@ public class V2CoresAPIMappingTest extends V2ApiMappingTest { @Override public void populateApiBag() { final CoreAdminHandler handler = getRequestHandler(); - apiBag.registerObject(new SingleCoreStatusAPI(handler)); apiBag.registerObject(new AllCoresStatusAPI(handler)); } @@ -58,6 +57,7 @@ public boolean isCoreSpecific() { return false; } + @Test public void testCreateCoreRequestBodyMappingAllParams() throws Exception { final ModifiableSolrParams v1Params = new ModifiableSolrParams(); @@ -113,6 +113,7 @@ public void testCreateCoreRequestBodyMappingAllParams() throws Exception { assertEquals(null, createRequestBody.async); } + // TODO NOCOMMIT - following 2 tests will need attended to if they're still relevant @Test public void testSpecificCoreStatusApiAllParams() throws Exception { final SolrParams v1Params = From 4b3f561410ca03721ae61d5bd0982c533be5e0c6 Mon Sep 17 00:00:00 2001 From: Jason Gerlowski Date: Thu, 16 Jan 2025 07:34:47 -0500 Subject: [PATCH 04/10] Finish off core-status conversion, mostly Builds on the previous commit's broken state and brings things to a point where they should at least compile so that I can run tests and start catching bugs. TODO/NOCOMMITs throughout the changes still, and there are a few tests I know fail from the earlier create-core change, but we can start to catch things. --- .../solr/client/api/endpoint/CoreApis.java | 11 ++- .../client/api/model/CoreStatusResponse.java | 82 ++++++++-------- .../solr/handler/admin/CoreAdminHandler.java | 6 +- .../handler/admin/CoreAdminOperation.java | 82 +++++++--------- .../handler/admin/LukeRequestHandler.java | 32 ++++++- .../apache/solr/handler/admin/StatusOp.java | 64 ++----------- .../handler/admin/api/AllCoresStatusAPI.java | 54 ----------- .../solr/handler/admin/api/CoreStatus.java | 94 +++++++++++++++++++ .../solr/handler/admin/api/CoreStatusAPI.java | 67 ------------- .../handler/admin/V2CoresAPIMappingTest.java | 34 ------- .../apache/solr/embedded/JettySolrRunner.java | 7 +- 11 files changed, 225 insertions(+), 308 deletions(-) delete mode 100644 solr/core/src/java/org/apache/solr/handler/admin/api/AllCoresStatusAPI.java create mode 100644 solr/core/src/java/org/apache/solr/handler/admin/api/CoreStatus.java delete mode 100644 solr/core/src/java/org/apache/solr/handler/admin/api/CoreStatusAPI.java diff --git a/solr/api/src/java/org/apache/solr/client/api/endpoint/CoreApis.java b/solr/api/src/java/org/apache/solr/client/api/endpoint/CoreApis.java index 7c6ccc4d3e6..43716529bab 100644 --- a/solr/api/src/java/org/apache/solr/client/api/endpoint/CoreApis.java +++ b/solr/api/src/java/org/apache/solr/client/api/endpoint/CoreApis.java @@ -41,11 +41,16 @@ interface GetStatus { @GET @Operation(summary = "Fetch status info for all cores hosted on this node.", tags = "cores") - SolrJerseyResponse getAllCoreStatus(@QueryParam("indexInfo") Boolean indexInfo); + SolrJerseyResponse getAllCoreStatus(@QueryParam("indexInfo") Boolean indexInfo) + throws Exception; @GET - @Operation(summary = "Fetch status info for the core hosted on this node with the specified name.", tags = "cores") + @Operation( + summary = "Fetch status info for the core hosted on this node with the specified name.", + tags = "cores") @Path("/{coreName}") - SolrJerseyResponse getCoreStatus(@PathParam("coreName") String coreName, @QueryParam("indexInfo") Boolean indexInfo); + SolrJerseyResponse getCoreStatus( + @PathParam("coreName") String coreName, @QueryParam("indexInfo") Boolean indexInfo) + throws Exception; } } diff --git a/solr/api/src/java/org/apache/solr/client/api/model/CoreStatusResponse.java b/solr/api/src/java/org/apache/solr/client/api/model/CoreStatusResponse.java index eca69b3465a..e9361972aa2 100644 --- a/solr/api/src/java/org/apache/solr/client/api/model/CoreStatusResponse.java +++ b/solr/api/src/java/org/apache/solr/client/api/model/CoreStatusResponse.java @@ -17,50 +17,56 @@ package org.apache.solr.client.api.model; import com.fasterxml.jackson.annotation.JsonProperty; - +import java.util.Date; import java.util.Map; public class CoreStatusResponse extends SolrJerseyResponse { - // In the v1 code this is a Map of Exception instances by core name. How are exceptions serialized out by things though, that's what I'd have to mirror on the v2 side. - @JsonProperty public Map initFailures; + // In the v1 code this is a Map of Exception instances by core name. How are exceptions + // serialized out by things though, that's what I'd have to mirror on the v2 side. + @JsonProperty public Map initFailures; + + @JsonProperty public Map status; + + public static class SingleCoreData { + @JsonProperty public String name; - @JsonProperty public Map status; + @JsonProperty public Boolean isLoaded; + @JsonProperty public Boolean isLoading; - public static class SingleCoreData { - @JsonProperty String name; - @JsonProperty String instanceDir; - @JsonProperty String dataDir; - @JsonProperty String config; - @JsonProperty String schema; - @JsonProperty String startTime; // TODO NOCOMMIT Date? Instant? - @JsonProperty Long uptime; - @JsonProperty String lastPublished; - @JsonProperty Integer configVersion; - @JsonProperty CloudDetails cloud; - @JsonProperty IndexDetails index; - } + @JsonProperty public String instanceDir; + @JsonProperty public String dataDir; + @JsonProperty public String config; + @JsonProperty public String schema; + @JsonProperty public Date startTime; + @JsonProperty public Long uptime; + @JsonProperty public String lastPublished; + @JsonProperty public Integer configVersion; + @JsonProperty public CloudDetails cloud; + @JsonProperty public IndexDetails index; + } - public static class CloudDetails { - @JsonProperty public String collection; - @JsonProperty public String shard; - @JsonProperty public String replica; - @JsonProperty public String replicaType; // TODO enum? - } + public static class CloudDetails { + @JsonProperty public String collection; + @JsonProperty public String shard; + @JsonProperty public String replica; + @JsonProperty public String replicaType; // TODO enum? + } - public static class IndexDetails { - @JsonProperty public Integer numDocs; - @JsonProperty public Integer maxDoc; - @JsonProperty public Integer deletedDocs; - @JsonProperty public Integer version; - @JsonProperty public Integer segmentCount; - @JsonProperty public Boolean current; - @JsonProperty public Boolean hasDeletions; - @JsonProperty public String directory; - @JsonProperty public String segmentsFile; - @JsonProperty public Integer segmentsFileSizeInBytes; - @JsonProperty public Map userData; - @JsonProperty public Integer sizeInBytes; - @JsonProperty public String size; // Human readable representation of 'sizeInBytes' - } + public static class IndexDetails { + @JsonProperty public Integer numDocs; + @JsonProperty public Integer maxDoc; + @JsonProperty public Integer deletedDocs; + @JsonProperty public Long version; + @JsonProperty public Integer segmentCount; + @JsonProperty public Boolean current; + @JsonProperty public Boolean hasDeletions; + @JsonProperty public String directory; + @JsonProperty public String segmentsFile; + @JsonProperty public Long segmentsFileSizeInBytes; + @JsonProperty public Map userData; + @JsonProperty public Date lastModified; + @JsonProperty public Long sizeInBytes; + @JsonProperty public String size; // Human readable representation of 'sizeInBytes' + } } 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 42b6fd64267..c69a30d1039 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 @@ -54,8 +54,8 @@ import org.apache.solr.core.CoreContainer; 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.CoreSnapshot; +import org.apache.solr.handler.admin.api.CoreStatus; import org.apache.solr.handler.admin.api.CreateCore; import org.apache.solr.handler.admin.api.CreateCoreBackup; import org.apache.solr.handler.admin.api.GetNodeCommandStatus; @@ -71,7 +71,6 @@ import org.apache.solr.handler.admin.api.RequestCoreRecoveryAPI; import org.apache.solr.handler.admin.api.RequestSyncShardAPI; import org.apache.solr.handler.admin.api.RestoreCore; -import org.apache.solr.handler.admin.api.CoreStatusAPI; import org.apache.solr.handler.admin.api.SplitCoreAPI; import org.apache.solr.handler.admin.api.SwapCores; import org.apache.solr.handler.admin.api.UnloadCore; @@ -349,7 +348,6 @@ void call() throws Exception { @Override public Collection getApis() { final List apis = new ArrayList<>(); - apis.addAll(AnnotatedApi.getApis(new AllCoresStatusAPI(this))); apis.addAll(AnnotatedApi.getApis(new RejoinLeaderElectionAPI(this))); apis.addAll(AnnotatedApi.getApis(new OverseerOperationAPI(this))); apis.addAll(AnnotatedApi.getApis(new SplitCoreAPI(this))); @@ -367,7 +365,7 @@ public Collection getApis() { public Collection> getJerseyResources() { return List.of( CoreSnapshot.class, - CoreStatusAPI.class, + CoreStatus.class, InstallCoreData.class, CreateCore.class, CreateCoreBackup.class, 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 312d244798f..36b2cf5a6f5 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 @@ -16,8 +16,6 @@ */ package org.apache.solr.handler.admin; -import static org.apache.solr.common.params.CommonParams.NAME; -import static org.apache.solr.common.params.CoreAdminParams.COLLECTION; import static org.apache.solr.common.params.CoreAdminParams.CoreAdminAction.BACKUPCORE; import static org.apache.solr.common.params.CoreAdminParams.CoreAdminAction.CREATE; import static org.apache.solr.common.params.CoreAdminParams.CoreAdminAction.CREATESNAPSHOT; @@ -40,9 +38,6 @@ import static org.apache.solr.common.params.CoreAdminParams.CoreAdminAction.STATUS; import static org.apache.solr.common.params.CoreAdminParams.CoreAdminAction.SWAP; import static org.apache.solr.common.params.CoreAdminParams.CoreAdminAction.UNLOAD; -import static org.apache.solr.common.params.CoreAdminParams.REPLICA; -import static org.apache.solr.common.params.CoreAdminParams.REPLICA_TYPE; -import static org.apache.solr.common.params.CoreAdminParams.SHARD; import static org.apache.solr.handler.admin.CoreAdminHandler.CallInfo; import static org.apache.solr.handler.admin.CoreAdminHandler.normalizePath; @@ -50,6 +45,7 @@ import java.lang.invoke.MethodHandles; import java.util.Locale; import org.apache.solr.client.api.endpoint.SwapCoresApi; +import org.apache.solr.client.api.model.CoreStatusResponse; import org.apache.solr.client.api.model.ListCoreSnapshotsResponse; import org.apache.solr.client.api.model.ReloadCoreRequestBody; import org.apache.solr.client.api.model.RenameCoreRequestBody; @@ -61,8 +57,6 @@ import org.apache.solr.common.SolrException.ErrorCode; import org.apache.solr.common.params.CoreAdminParams; import org.apache.solr.common.params.SolrParams; -import org.apache.solr.common.util.NamedList; -import org.apache.solr.common.util.SimpleOrderedMap; import org.apache.solr.common.util.StrUtils; import org.apache.solr.core.CoreContainer; import org.apache.solr.core.CoreDescriptor; @@ -293,6 +287,7 @@ public boolean isExpensive() { return fun.isExpensive(); } + // TODO NOCOMMIT - this method should be moved elsewhere, probably to CoreStatusAPI /** * Returns the core status for a particular core. * @@ -303,70 +298,65 @@ public boolean isExpensive() { * @return - a named list of key/value pairs from the core. * @throws IOException - LukeRequestHandler can throw an I/O exception */ - @SuppressWarnings({"unchecked", "rawtypes"}) - public static NamedList getCoreStatus( + public static CoreStatusResponse.SingleCoreData getCoreStatus( CoreContainer cores, String cname, boolean isIndexInfoNeeded) throws IOException { - NamedList info = new SimpleOrderedMap<>(); - + final var info = new CoreStatusResponse.SingleCoreData(); if (cores.isCoreLoading(cname)) { - info.add(NAME, cname); - info.add("isLoaded", "false"); - info.add("isLoading", "true"); + info.name = cname; + info.isLoaded = false; + info.isLoading = true; } else { if (!cores.isLoaded(cname)) { // Lazily-loaded core, fill in what we can. // It would be a real mistake to load the cores just to get the status CoreDescriptor desc = cores.getCoreDescriptor(cname); if (desc != null) { - info.add(NAME, desc.getName()); - info.add("instanceDir", desc.getInstanceDir()); + info.name = desc.getName(); + info.instanceDir = desc.getInstanceDir().toString(); // None of the following are guaranteed to be present in a not-yet-loaded core. String tmp = desc.getDataDir(); - if (StrUtils.isNotBlank(tmp)) info.add("dataDir", tmp); + if (StrUtils.isNotBlank(tmp)) info.dataDir = tmp; tmp = desc.getConfigName(); - if (StrUtils.isNotBlank(tmp)) info.add("config", tmp); + if (StrUtils.isNotBlank(tmp)) info.config = tmp; tmp = desc.getSchemaName(); - if (StrUtils.isNotBlank(tmp)) info.add("schema", tmp); - info.add("isLoaded", "false"); + if (StrUtils.isNotBlank(tmp)) info.schema = tmp; + info.isLoaded = false; } } else { try (SolrCore core = cores.getCore(cname)) { if (core != null) { - info.add(NAME, core.getName()); - info.add("instanceDir", core.getInstancePath().toString()); - info.add("dataDir", normalizePath(core.getDataDir())); - info.add("config", core.getConfigResource()); - info.add("schema", core.getSchemaResource()); - info.add("startTime", core.getStartTimeStamp()); - info.add("uptime", core.getUptimeMs()); + info.name = core.getName(); + info.instanceDir = core.getInstancePath().toString(); + info.dataDir = normalizePath(core.getDataDir()); + info.config = core.getConfigResource(); + info.schema = core.getSchemaResource(); + info.startTime = core.getStartTimeStamp(); + info.uptime = core.getUptimeMs(); if (cores.isZooKeeperAware()) { - info.add( - "lastPublished", + info.lastPublished = core.getCoreDescriptor() .getCloudDescriptor() .getLastPublished() .toString() - .toLowerCase(Locale.ROOT)); - info.add("configVersion", core.getSolrConfig().getZnodeVersion()); - SimpleOrderedMap cloudInfo = new SimpleOrderedMap<>(); - cloudInfo.add( - COLLECTION, core.getCoreDescriptor().getCloudDescriptor().getCollectionName()); - cloudInfo.add(SHARD, core.getCoreDescriptor().getCloudDescriptor().getShardId()); - cloudInfo.add( - REPLICA, core.getCoreDescriptor().getCloudDescriptor().getCoreNodeName()); - cloudInfo.add( - REPLICA_TYPE, - core.getCoreDescriptor().getCloudDescriptor().getReplicaType().name()); - info.add("cloud", cloudInfo); + .toLowerCase(Locale.ROOT); + info.configVersion = core.getSolrConfig().getZnodeVersion(); + final var cloudInfo = new CoreStatusResponse.CloudDetails(); + cloudInfo.collection = + core.getCoreDescriptor().getCloudDescriptor().getCollectionName(); + cloudInfo.shard = core.getCoreDescriptor().getCloudDescriptor().getShardId(); + cloudInfo.replica = core.getCoreDescriptor().getCloudDescriptor().getCoreNodeName(); + cloudInfo.replicaType = + core.getCoreDescriptor().getCloudDescriptor().getReplicaType().name(); + info.cloud = cloudInfo; } if (isIndexInfoNeeded) { RefCounted searcher = core.getSearcher(); try { - SimpleOrderedMap indexInfo = - LukeRequestHandler.getIndexInfo(searcher.get().getIndexReader()); + final var indexInfo = + LukeRequestHandler.getIndexInfoTyped(searcher.get().getIndexReader()); long size = core.getIndexSize(); - indexInfo.add("sizeInBytes", size); - indexInfo.add("size", NumberUtils.readableSize(size)); - info.add("index", indexInfo); + indexInfo.sizeInBytes = size; + indexInfo.size = NumberUtils.readableSize(size); + info.index = indexInfo; } finally { searcher.decref(); } diff --git a/solr/core/src/java/org/apache/solr/handler/admin/LukeRequestHandler.java b/solr/core/src/java/org/apache/solr/handler/admin/LukeRequestHandler.java index f06ec4369b1..8e09d09baab 100644 --- a/solr/core/src/java/org/apache/solr/handler/admin/LukeRequestHandler.java +++ b/solr/core/src/java/org/apache/solr/handler/admin/LukeRequestHandler.java @@ -66,6 +66,7 @@ import org.apache.lucene.util.CharsRefBuilder; import org.apache.lucene.util.PriorityQueue; import org.apache.solr.analysis.TokenizerChain; +import org.apache.solr.client.api.model.CoreStatusResponse; import org.apache.solr.common.SolrException; import org.apache.solr.common.SolrException.ErrorCode; import org.apache.solr.common.luke.FieldFlag; @@ -622,6 +623,35 @@ private static void populateFieldInfo( // This method just gets the top-most level of information. This was conflated with getting // detailed info for *all* the fields, called from CoreAdminHandler etc. + public static CoreStatusResponse.IndexDetails getIndexInfoTyped(DirectoryReader reader) + throws IOException { + Directory dir = reader.directory(); + final var indexInfo = new CoreStatusResponse.IndexDetails(); + + indexInfo.numDocs = reader.numDocs(); + indexInfo.maxDoc = reader.maxDoc(); + indexInfo.deletedDocs = reader.maxDoc() - reader.numDocs(); + // TODO? Is this different then: IndexReader.getCurrentVersion( dir )? + indexInfo.version = reader.getVersion(); + indexInfo.segmentCount = reader.leaves().size(); + indexInfo.current = closeSafe(reader::isCurrent); + indexInfo.hasDeletions = reader.hasDeletions(); + indexInfo.directory = dir.toString(); + IndexCommit indexCommit = reader.getIndexCommit(); + String segmentsFileName = indexCommit.getSegmentsFileName(); + indexInfo.segmentsFile = segmentsFileName; + indexInfo.segmentsFileSizeInBytes = getSegmentsFileLength(indexCommit); + Map userData = indexCommit.getUserData(); + indexInfo.userData = userData; + String s = userData.get(SolrIndexWriter.COMMIT_TIME_MSEC_KEY); + if (s != null) { + indexInfo.lastModified = new Date(Long.parseLong(s)); + } + return indexInfo; + } + + // TODO NOCOMMIT Nuke this method and its usages, in favor of the typed version above that + // produces CoreStatusResponse.IndexInfo public static SimpleOrderedMap getIndexInfo(DirectoryReader reader) throws IOException { Directory dir = reader.directory(); SimpleOrderedMap indexInfo = new SimpleOrderedMap<>(); @@ -653,7 +683,7 @@ interface IOSupplier { boolean get() throws IOException; } - private static Object closeSafe(IOSupplier isCurrent) { + private static boolean closeSafe(IOSupplier isCurrent) { try { return isCurrent.get(); } catch (AlreadyClosedException | IOException exception) { diff --git a/solr/core/src/java/org/apache/solr/handler/admin/StatusOp.java b/solr/core/src/java/org/apache/solr/handler/admin/StatusOp.java index cb3c4c5ceb0..95af7758c79 100644 --- a/solr/core/src/java/org/apache/solr/handler/admin/StatusOp.java +++ b/solr/core/src/java/org/apache/solr/handler/admin/StatusOp.java @@ -17,75 +17,23 @@ package org.apache.solr.handler.admin; -import java.io.IOException; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.apache.solr.client.api.model.CoreStatusResponse; import org.apache.solr.common.params.CoreAdminParams; import org.apache.solr.common.params.SolrParams; -import org.apache.solr.common.util.NamedList; -import org.apache.solr.common.util.SimpleOrderedMap; -import org.apache.solr.core.CoreContainer; +import org.apache.solr.handler.admin.api.CoreStatus; +import org.apache.solr.handler.api.V2ApiUtils; class StatusOp implements CoreAdminHandler.CoreAdminOp { - public static CoreStatusResponse fetchStatusInfo(CoreContainer coreContainer, String coreName, Boolean indexInfo) throws IOException { - final var response = new CoreStatusResponse(); - response.initFailures = new HashMap<>(); - - for (Map.Entry failure : coreContainer.getCoreInitFailures().entrySet()) { - - // Skip irrelevant initFailures if we're only interested in a single core - if (coreName != null && !failure.getKey().equals(coreName)) - continue; - - response.initFailures.put(failure.getKey(), failure.getValue().exception); - } - - // Populate status for each core - final var coreNameList = (coreName != null) ? List.of(coreName) : coreContainer.getAllCoreNames(); - coreNameList.sort(null); - response.status = new HashMap<>(); - for (String toPopulate : coreNameList) { - // TODO This is where I left off at 1/15. Next TODO items are: - // 1. port CoreAdminOperation.getCoreStatus over to return a POJO - // 2. modify execute() below to call this method (fetchStatusInfo), and then squash the strong-type into a NL - // 3. Move this method (fetchStatusInfo) into the v2 API class, and use it to implement both single and all-core fetching - // 4. Trace through the v1 and v2 codepaths to make sure registration, plumbing, etc. are all in place - // 5. Look into how v1 code currently serializes Exceptions (are we just toString-ing them) - // 6. Look into NOCOMMIT and other todo comments - // 7. Look into the failing tests from create-core side - response.status.put(toPopulate, CoreAdminOperation.getCoreStatus(coreContainer, toPopulate, indexInfo)); - } - - return response; - } - @Override public void execute(CoreAdminHandler.CallInfo it) throws Exception { SolrParams params = it.req.getParams(); - String cname = params.get(CoreAdminParams.CORE); String indexInfo = params.get(CoreAdminParams.INDEX_INFO); boolean isIndexInfoNeeded = Boolean.parseBoolean(null == indexInfo ? "true" : indexInfo); - NamedList status = new SimpleOrderedMap<>(); - if (cname == null) { - List nameList = it.handler.coreContainer.getAllCoreNames(); - nameList.sort(null); - for (String name : nameList) { - status.add( - name, - CoreAdminOperation.getCoreStatus(it.handler.coreContainer, name, isIndexInfoNeeded)); - } - } else { - status.add( - cname, - CoreAdminOperation.getCoreStatus(it.handler.coreContainer, cname, isIndexInfoNeeded)); - } - it.rsp.add("status", status); + final var response = + CoreStatus.fetchStatusInfo(it.handler.coreContainer, cname, isIndexInfoNeeded); + + V2ApiUtils.squashIntoSolrResponseWithoutHeader(it.rsp, response); } } diff --git a/solr/core/src/java/org/apache/solr/handler/admin/api/AllCoresStatusAPI.java b/solr/core/src/java/org/apache/solr/handler/admin/api/AllCoresStatusAPI.java deleted file mode 100644 index a1c2b75bc1d..00000000000 --- a/solr/core/src/java/org/apache/solr/handler/admin/api/AllCoresStatusAPI.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.solr.handler.admin.api; - -import static org.apache.solr.client.solrj.SolrRequest.METHOD.GET; -import static org.apache.solr.common.params.CommonParams.ACTION; -import static org.apache.solr.common.params.CommonParams.STATUS; -import static org.apache.solr.handler.ClusterAPI.wrapParams; -import static org.apache.solr.security.PermissionNameProvider.Name.CORE_READ_PERM; - -import org.apache.solr.api.EndPoint; -import org.apache.solr.handler.admin.CoreAdminHandler; -import org.apache.solr.request.SolrQueryRequest; -import org.apache.solr.response.SolrQueryResponse; - -/** - * V2 API for retrieving status information for all cores on the receiving node. - * - *

This API (GET /v2/cores is analogous to the v1 /admin/cores?action=status command. - * - * @see CoreStatusAPI - */ -public class AllCoresStatusAPI { - - private final CoreAdminHandler coreAdminHandler; - - public AllCoresStatusAPI(CoreAdminHandler coreAdminHandler) { - this.coreAdminHandler = coreAdminHandler; - } - - @EndPoint( - path = {"/cores"}, - method = GET, - permission = CORE_READ_PERM) - public void getStatusOfAllCores(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception { - req = wrapParams(req, ACTION, STATUS); - coreAdminHandler.handleRequestBody(req, rsp); - } -} diff --git a/solr/core/src/java/org/apache/solr/handler/admin/api/CoreStatus.java b/solr/core/src/java/org/apache/solr/handler/admin/api/CoreStatus.java new file mode 100644 index 00000000000..d7bb44cca48 --- /dev/null +++ b/solr/core/src/java/org/apache/solr/handler/admin/api/CoreStatus.java @@ -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.handler.admin.api; + +import static org.apache.solr.security.PermissionNameProvider.Name.CORE_READ_PERM; + +import jakarta.inject.Inject; +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.apache.solr.client.api.endpoint.CoreApis; +import org.apache.solr.client.api.model.CoreStatusResponse; +import org.apache.solr.core.CoreContainer; +import org.apache.solr.handler.admin.CoreAdminHandler; +import org.apache.solr.handler.admin.CoreAdminOperation; +import org.apache.solr.jersey.PermissionName; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.response.SolrQueryResponse; + +/** + * V2 APIs for getting the status of one or all cores. + * + *

This API (GET /v2/cores/coreName is analogous to the v1 /admin/cores?action=status command. + */ +public class CoreStatus extends CoreAdminAPIBase implements CoreApis.GetStatus { + + @Inject + public CoreStatus( + CoreContainer coreContainer, + CoreAdminHandler.CoreAdminAsyncTracker coreAdminAsyncTracker, + SolrQueryRequest solrQueryRequest, + SolrQueryResponse solrQueryResponse) { + super(coreContainer, coreAdminAsyncTracker, solrQueryRequest, solrQueryResponse); + } + + @Override + @PermissionName(CORE_READ_PERM) + public CoreStatusResponse getAllCoreStatus(Boolean indexInfo) throws IOException { + return fetchStatusInfo(coreContainer, null, indexInfo); + } + + @Override + @PermissionName(CORE_READ_PERM) + public CoreStatusResponse getCoreStatus(String coreName, Boolean indexInfo) throws IOException { + return fetchStatusInfo(coreContainer, coreName, indexInfo); + } + + public static CoreStatusResponse fetchStatusInfo( + CoreContainer coreContainer, String coreName, Boolean indexInfo) throws IOException { + final var response = new CoreStatusResponse(); + response.initFailures = new HashMap<>(); + + for (Map.Entry failure : + coreContainer.getCoreInitFailures().entrySet()) { + + // Skip irrelevant initFailures if we're only interested in a single core + if (coreName != null && !failure.getKey().equals(coreName)) continue; + + response.initFailures.put(failure.getKey(), failure.getValue().exception); + } + + // Populate status for each core + final var coreNameList = + (coreName != null) ? List.of(coreName) : coreContainer.getAllCoreNames(); + coreNameList.sort(null); + response.status = new HashMap<>(); + for (String toPopulate : coreNameList) { + // TODO This is where I left off at 1/15. Next TODO items are: + // 5. Look into how v1 code currently serializes Exceptions (are we just toString-ing them) + // 6. Look into NOCOMMIT and other todo comments + // 7. Look into the failing tests from create-core side + response.status.put( + toPopulate, CoreAdminOperation.getCoreStatus(coreContainer, toPopulate, indexInfo)); + } + + return response; + } +} diff --git a/solr/core/src/java/org/apache/solr/handler/admin/api/CoreStatusAPI.java b/solr/core/src/java/org/apache/solr/handler/admin/api/CoreStatusAPI.java deleted file mode 100644 index 17a3578da02..00000000000 --- a/solr/core/src/java/org/apache/solr/handler/admin/api/CoreStatusAPI.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.solr.handler.admin.api; - -import static org.apache.solr.client.solrj.SolrRequest.METHOD.GET; -import static org.apache.solr.common.params.CommonParams.ACTION; -import static org.apache.solr.common.params.CommonParams.STATUS; -import static org.apache.solr.common.params.CoreAdminParams.CORE; -import static org.apache.solr.handler.ClusterAPI.wrapParams; -import static org.apache.solr.security.PermissionNameProvider.Name.CORE_READ_PERM; - -import jakarta.inject.Inject; -import org.apache.solr.api.EndPoint; -import org.apache.solr.client.api.endpoint.CoreApis; -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.PermissionName; -import org.apache.solr.request.SolrQueryRequest; -import org.apache.solr.response.SolrQueryResponse; - -/** - * V2 API for checking the status of a specific core. - * - *

This API (GET /v2/cores/coreName is analogous to the v1 - * /admin/cores?action=status&core=coreName command. - * - * @see AllCoresStatusAPI - */ -public class CoreStatusAPI extends CoreAdminAPIBase implements CoreApis.GetStatus { - - @Inject - public CoreStatusAPI( - CoreContainer coreContainer, - CoreAdminHandler.CoreAdminAsyncTracker coreAdminAsyncTracker, - SolrQueryRequest solrQueryRequest, - SolrQueryResponse solrQueryResponse) { - super(coreContainer, coreAdminAsyncTracker, solrQueryRequest, solrQueryResponse); - } - - @Override - @PermissionName(CORE_READ_PERM) - public SolrJerseyResponse getAllCoreStatus(Boolean indexInfo) { - return null; - } - - @Override - @PermissionName(CORE_READ_PERM) - public SolrJerseyResponse getCoreStatus(String coreName, Boolean indexInfo) { - return null; - } -} diff --git a/solr/core/src/test/org/apache/solr/handler/admin/V2CoresAPIMappingTest.java b/solr/core/src/test/org/apache/solr/handler/admin/V2CoresAPIMappingTest.java index d79658efdd3..b67cb982197 100644 --- a/solr/core/src/test/org/apache/solr/handler/admin/V2CoresAPIMappingTest.java +++ b/solr/core/src/test/org/apache/solr/handler/admin/V2CoresAPIMappingTest.java @@ -17,18 +17,8 @@ package org.apache.solr.handler.admin; -import static org.apache.solr.common.params.CommonParams.ACTION; -import static org.apache.solr.common.params.CoreAdminParams.CORE; -import static org.apache.solr.common.params.CoreAdminParams.CoreAdminAction.STATUS; -import static org.apache.solr.common.params.CoreAdminParams.INDEX_INFO; - -import java.util.Locale; -import java.util.Map; import org.apache.solr.common.params.ModifiableSolrParams; -import org.apache.solr.common.params.SolrParams; -import org.apache.solr.handler.admin.api.AllCoresStatusAPI; import org.apache.solr.handler.admin.api.CreateCore; -import org.apache.solr.handler.admin.api.CoreStatusAPI; import org.junit.Test; /** @@ -44,7 +34,6 @@ public class V2CoresAPIMappingTest extends V2ApiMappingTest { @Override public void populateApiBag() { final CoreAdminHandler handler = getRequestHandler(); - apiBag.registerObject(new AllCoresStatusAPI(handler)); } @Override @@ -57,7 +46,6 @@ public boolean isCoreSpecific() { return false; } - @Test public void testCreateCoreRequestBodyMappingAllParams() throws Exception { final ModifiableSolrParams v1Params = new ModifiableSolrParams(); @@ -112,26 +100,4 @@ public void testCreateCoreRequestBodyMappingAllParams() throws Exception { // V1 codepath handles the async/taskId differently, and it's not passed down the v2 code assertEquals(null, createRequestBody.async); } - - // TODO NOCOMMIT - following 2 tests will need attended to if they're still relevant - @Test - public void testSpecificCoreStatusApiAllParams() throws Exception { - final SolrParams v1Params = - captureConvertedV1Params( - "/cores/someCore", "GET", Map.of(INDEX_INFO, new String[] {"true"})); - - assertEquals(STATUS.name().toLowerCase(Locale.ROOT), v1Params.get(ACTION)); - assertEquals("someCore", v1Params.get(CORE)); - assertTrue(v1Params.getPrimitiveBool(INDEX_INFO)); - } - - @Test - public void testAllCoreStatusApiAllParams() throws Exception { - final SolrParams v1Params = - captureConvertedV1Params("/cores", "GET", Map.of(INDEX_INFO, new String[] {"true"})); - - assertEquals(STATUS.name().toLowerCase(Locale.ROOT), v1Params.get(ACTION)); - assertNull("Expected 'core' parameter to be null", v1Params.get(CORE)); - assertTrue(v1Params.getPrimitiveBool(INDEX_INFO)); - } } diff --git a/solr/test-framework/src/java/org/apache/solr/embedded/JettySolrRunner.java b/solr/test-framework/src/java/org/apache/solr/embedded/JettySolrRunner.java index 9c033827241..e0a19ed50ef 100644 --- a/solr/test-framework/src/java/org/apache/solr/embedded/JettySolrRunner.java +++ b/solr/test-framework/src/java/org/apache/solr/embedded/JettySolrRunner.java @@ -61,9 +61,9 @@ import org.apache.solr.client.solrj.cloud.SocketProxy; import org.apache.solr.client.solrj.embedded.SSLConfig; import org.apache.solr.client.solrj.impl.HttpSolrClient; -import org.apache.solr.common.util.NamedList; import org.apache.solr.common.util.SimpleOrderedMap; import org.apache.solr.common.util.TimeSource; +import org.apache.solr.common.util.Utils; import org.apache.solr.core.CoreContainer; import org.apache.solr.core.SolrCore; import org.apache.solr.handler.admin.CoreAdminOperation; @@ -716,13 +716,14 @@ public void dumpCoresInfo(PrintStream pw) throws IOException { if (getCoreContainer() != null) { List cores = getCoreContainer().getCores(); for (SolrCore core : cores) { - NamedList coreStatus = + final var coreStatus = CoreAdminOperation.getCoreStatus(getCoreContainer(), core.getName(), false); core.withSearcher( solrIndexSearcher -> { SimpleOrderedMap lukeIndexInfo = LukeRequestHandler.getIndexInfo(solrIndexSearcher.getIndexReader()); - Map indexInfoMap = coreStatus.toMap(new LinkedHashMap<>()); + // TODO NOCOMMIT Following line is a little suspect IMO, but we'll see if tests pass. + Map indexInfoMap = Utils.reflectToMap(coreStatus); indexInfoMap.putAll(lukeIndexInfo.toMap(new LinkedHashMap<>())); pw.println(JSONUtil.toJSON(indexInfoMap, 2)); From efc080aadff21b93855cf4252c1724b1c932daa2 Mon Sep 17 00:00:00 2001 From: Jason Gerlowski Date: Thu, 16 Jan 2025 12:18:40 -0500 Subject: [PATCH 05/10] Fix tests, partial --- .../org/apache/solr/handler/admin/api/CoreStatus.java | 2 +- .../solr/client/solrj/response/CoreAdminResponse.java | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/solr/core/src/java/org/apache/solr/handler/admin/api/CoreStatus.java b/solr/core/src/java/org/apache/solr/handler/admin/api/CoreStatus.java index d7bb44cca48..11caf463ba3 100644 --- a/solr/core/src/java/org/apache/solr/handler/admin/api/CoreStatus.java +++ b/solr/core/src/java/org/apache/solr/handler/admin/api/CoreStatus.java @@ -78,7 +78,7 @@ public static CoreStatusResponse fetchStatusInfo( // Populate status for each core final var coreNameList = (coreName != null) ? List.of(coreName) : coreContainer.getAllCoreNames(); - coreNameList.sort(null); + if (coreNameList.size() > 1) coreNameList.sort(null); response.status = new HashMap<>(); for (String toPopulate : coreNameList) { // TODO This is where I left off at 1/15. Next TODO items are: diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/response/CoreAdminResponse.java b/solr/solrj/src/java/org/apache/solr/client/solrj/response/CoreAdminResponse.java index 0226593891f..fb7baaec458 100644 --- a/solr/solrj/src/java/org/apache/solr/client/solrj/response/CoreAdminResponse.java +++ b/solr/solrj/src/java/org/apache/solr/client/solrj/response/CoreAdminResponse.java @@ -23,6 +23,17 @@ * @since solr 1.3 */ public class CoreAdminResponse extends SolrResponseBase { + + // TODO NOCOMMIT This is a cause of several test failures, and needs resolved somehow for both + // 10.0 and branch_9x + // In javabin at least, we end up as a LinkedHashMap and not a NamedList. This comes up + // periodically in v2 API transitions, but idk if I've seen a case yet where NamedList is declared + // in a method signature itself, which introduces some backcompat concerns. The ideal way to fix + // this I think would be to (1) introduce a new method returning the POJO instead of a NL, (2) + // switch usage over to the new method, (3) deprecate the old method on branch_9x, and (4) remove + // the old method on main. In the interim though (at least on branch_9x) we'll need to + // reimplement this method to swap out maps for NamedLists, so that we can obey the method + // signature. @SuppressWarnings("unchecked") public NamedList> getCoreStatus() { return (NamedList>) getResponse().get("status"); From faaf8693f0dc0f0b9e796259948797884b8394cf Mon Sep 17 00:00:00 2001 From: Jason Gerlowski Date: Tue, 21 Jan 2025 10:00:57 -0500 Subject: [PATCH 06/10] Replace 'CoreStatus' with 'SingleCoreData' (main only) --- .../solr/cloud/CollectionsAPISolrJTest.java | 13 +++-- .../apache/solr/cloud/DeleteReplicaTest.java | 21 +++------ .../apache/solr/cloud/DeleteShardTest.java | 19 +++----- .../cloud/SystemCollectionCompatTest.java | 4 +- .../api/collections/CollectionReloadTest.java | 4 +- .../handler/admin/CoreAdminHandlerTest.java | 5 +- .../solrj/request/CoreAdminRequest.java | 8 ++-- .../solr/client/solrj/request/CoreStatus.java | 47 ------------------- .../solrj/response/CoreAdminResponse.java | 32 +++++-------- .../client/solrj/request/TestCoreAdmin.java | 2 +- .../cloud/AbstractMoveReplicaTestBase.java | 10 ++-- .../apache/solr/cloud/SolrCloudTestCase.java | 7 +-- ...ctCollectionsAPIDistributedZkTestBase.java | 11 ++--- 13 files changed, 59 insertions(+), 124 deletions(-) delete mode 100644 solr/solrj/src/java/org/apache/solr/client/solrj/request/CoreStatus.java diff --git a/solr/core/src/test/org/apache/solr/cloud/CollectionsAPISolrJTest.java b/solr/core/src/test/org/apache/solr/cloud/CollectionsAPISolrJTest.java index 5239deeaeac..05808c3b31a 100644 --- a/solr/core/src/test/org/apache/solr/cloud/CollectionsAPISolrJTest.java +++ b/solr/core/src/test/org/apache/solr/cloud/CollectionsAPISolrJTest.java @@ -52,7 +52,6 @@ import org.apache.solr.client.solrj.request.CollectionAdminRequest; import org.apache.solr.client.solrj.request.CollectionsApi; import org.apache.solr.client.solrj.request.CoreAdminRequest; -import org.apache.solr.client.solrj.request.CoreStatus; import org.apache.solr.client.solrj.request.V2Request; import org.apache.solr.client.solrj.response.CollectionAdminResponse; import org.apache.solr.client.solrj.response.CoreAdminResponse; @@ -454,9 +453,9 @@ public void testCreateCollectionWithPropertyParam() throws Exception { DocCollection testCollection = getCollectionState(collectionName); Replica replica1 = testCollection.getReplicas().iterator().next(); - CoreStatus coreStatus = getCoreStatus(replica1); + final var coreStatus = getCoreStatus(replica1); - assertEquals(Paths.get(coreStatus.getDataDirectory()).toString(), dataDir.toString()); + assertEquals(Paths.get(coreStatus.dataDir).toString(), dataDir.toString()); } @Test @@ -887,7 +886,7 @@ public void testReadOnlyCollection() throws Exception { ZkStateReader.from(solrClient).getLeaderRetry(collectionName, "shard1", DEFAULT_TIMEOUT); final AtomicReference coreStartTime = - new AtomicReference<>(getCoreStatus(leader).getCoreStartTime().getTime()); + new AtomicReference<>(getCoreStatus(leader).startTime.getTime()); // Check for value change CollectionAdminRequest.modifyCollection( @@ -908,7 +907,7 @@ public void testReadOnlyCollection() throws Exception { () -> { long restartTime = 0; try { - restartTime = getCoreStatus(leader).getCoreStartTime().getTime(); + restartTime = getCoreStatus(leader).startTime.getTime(); } catch (Exception e) { log.warn("Exception getting core start time: ", e); return false; @@ -916,7 +915,7 @@ public void testReadOnlyCollection() throws Exception { return restartTime > coreStartTime.get(); }); - coreStartTime.set(getCoreStatus(leader).getCoreStartTime().getTime()); + coreStartTime.set(getCoreStatus(leader).startTime.getTime()); // check for docs - reloading should have committed the new docs // this also verifies that searching works in read-only mode @@ -979,7 +978,7 @@ public void testReadOnlyCollection() throws Exception { () -> { long restartTime = 0; try { - restartTime = getCoreStatus(leader).getCoreStartTime().getTime(); + restartTime = getCoreStatus(leader).startTime.getTime(); } catch (Exception e) { log.warn("Exception getting core start time: ", e); return false; diff --git a/solr/core/src/test/org/apache/solr/cloud/DeleteReplicaTest.java b/solr/core/src/test/org/apache/solr/cloud/DeleteReplicaTest.java index 16242bcc5eb..d13bb8e0eb9 100644 --- a/solr/core/src/test/org/apache/solr/cloud/DeleteReplicaTest.java +++ b/solr/core/src/test/org/apache/solr/cloud/DeleteReplicaTest.java @@ -31,7 +31,6 @@ import java.util.concurrent.atomic.AtomicInteger; import org.apache.solr.client.solrj.request.CollectionAdminRequest; import org.apache.solr.client.solrj.request.CollectionAdminRequest.Create; -import org.apache.solr.client.solrj.request.CoreStatus; import org.apache.solr.cloud.overseer.OverseerAction; import org.apache.solr.common.SolrException; import org.apache.solr.common.SolrInputDocument; @@ -97,8 +96,8 @@ public void deleteLiveReplicaTest() throws Exception { getRandomReplica( shard, (r) -> (r.getState() == Replica.State.ACTIVE && !r.equals(shard.getLeader()))); - CoreStatus coreStatus = getCoreStatus(replica); - Path dataDir = Paths.get(coreStatus.getDataDirectory()); + final var coreStatus = getCoreStatus(replica); + Path dataDir = Paths.get(coreStatus.dataDir); Exception e = expectThrows( @@ -164,12 +163,9 @@ public void deleteReplicaAndVerifyDirectoryCleanup() throws Exception { Replica leader = cluster.getZkStateReader().getLeaderRetry(collectionName, "shard1"); // Confirm that the instance and data directory exist - CoreStatus coreStatus = getCoreStatus(leader); - assertTrue( - "Instance directory doesn't exist", - Files.exists(Paths.get(coreStatus.getInstanceDirectory()))); - assertTrue( - "DataDirectory doesn't exist", Files.exists(Paths.get(coreStatus.getDataDirectory()))); + final var coreStatus = getCoreStatus(leader); + assertTrue("Instance directory doesn't exist", Files.exists(Paths.get(coreStatus.instanceDir))); + assertTrue("DataDirectory doesn't exist", Files.exists(Paths.get(coreStatus.dataDir))); CollectionAdminRequest.deleteReplica(collectionName, "shard1", leader.getName()) .process(cluster.getSolrClient()); @@ -179,11 +175,8 @@ public void deleteReplicaAndVerifyDirectoryCleanup() throws Exception { assertNotEquals(leader, newLeader); // Confirm that the instance and data directory were deleted by default - assertFalse( - "Instance directory still exists", - Files.exists(Paths.get(coreStatus.getInstanceDirectory()))); - assertFalse( - "DataDirectory still exists", Files.exists(Paths.get(coreStatus.getDataDirectory()))); + assertFalse("Instance directory still exists", Files.exists(Paths.get(coreStatus.instanceDir))); + assertFalse("DataDirectory still exists", Files.exists(Paths.get(coreStatus.dataDir))); } @Test diff --git a/solr/core/src/test/org/apache/solr/cloud/DeleteShardTest.java b/solr/core/src/test/org/apache/solr/cloud/DeleteShardTest.java index f363bb58068..21ce66d279d 100644 --- a/solr/core/src/test/org/apache/solr/cloud/DeleteShardTest.java +++ b/solr/core/src/test/org/apache/solr/cloud/DeleteShardTest.java @@ -19,7 +19,6 @@ import java.io.IOException; import org.apache.solr.client.solrj.SolrServerException; import org.apache.solr.client.solrj.request.CollectionAdminRequest; -import org.apache.solr.client.solrj.request.CoreStatus; import org.apache.solr.common.cloud.DocCollection; import org.apache.solr.common.cloud.Replica; import org.apache.solr.common.cloud.Slice; @@ -89,11 +88,9 @@ public void testDirectoryCleanupAfterDeleteShard() throws IOException, SolrServe // Get replica details Replica leader = getCollectionState(collection).getLeader("a"); - CoreStatus coreStatus = getCoreStatus(leader); - assertTrue( - "Instance directory doesn't exist", - FileUtils.fileExists(coreStatus.getInstanceDirectory())); - assertTrue("Data directory doesn't exist", FileUtils.fileExists(coreStatus.getDataDirectory())); + var coreStatus = getCoreStatus(leader); + assertTrue("Instance directory doesn't exist", FileUtils.fileExists(coreStatus.instanceDir)); + assertTrue("Data directory doesn't exist", FileUtils.fileExists(coreStatus.dataDir)); assertEquals(3, getCollectionState(collection).getActiveSlices().size()); @@ -103,9 +100,8 @@ public void testDirectoryCleanupAfterDeleteShard() throws IOException, SolrServe waitForState("Expected 'a' to be removed", collection, (n, c) -> c.getSlice("a") == null); assertEquals(2, getCollectionState(collection).getActiveSlices().size()); - assertFalse( - "Instance directory still exists", FileUtils.fileExists(coreStatus.getInstanceDirectory())); - assertFalse("Data directory still exists", FileUtils.fileExists(coreStatus.getDataDirectory())); + assertFalse("Instance directory still exists", FileUtils.fileExists(coreStatus.instanceDir)); + assertFalse("Data directory still exists", FileUtils.fileExists(coreStatus.dataDir)); leader = getCollectionState(collection).getLeader("b"); coreStatus = getCoreStatus(leader); @@ -119,9 +115,8 @@ public void testDirectoryCleanupAfterDeleteShard() throws IOException, SolrServe waitForState("Expected 'b' to be removed", collection, (n, c) -> c.getSlice("b") == null); assertEquals(1, getCollectionState(collection).getActiveSlices().size()); - assertTrue( - "Instance directory still exists", FileUtils.fileExists(coreStatus.getInstanceDirectory())); - assertTrue("Data directory still exists", FileUtils.fileExists(coreStatus.getDataDirectory())); + assertTrue("Instance directory still exists", FileUtils.fileExists(coreStatus.instanceDir)); + assertTrue("Data directory still exists", FileUtils.fileExists(coreStatus.dataDir)); } private void setSliceState(String collectionName, String shardId, Slice.State state) diff --git a/solr/core/src/test/org/apache/solr/cloud/SystemCollectionCompatTest.java b/solr/core/src/test/org/apache/solr/cloud/SystemCollectionCompatTest.java index af7c2d1cd54..a67094098b0 100644 --- a/solr/core/src/test/org/apache/solr/cloud/SystemCollectionCompatTest.java +++ b/solr/core/src/test/org/apache/solr/cloud/SystemCollectionCompatTest.java @@ -101,7 +101,7 @@ public void setupSystemCollection() throws Exception { DocCollection coll = cloudManager.getClusterStateProvider().getCollection(CollectionAdminParams.SYSTEM_COLL); for (Replica r : coll.getReplicas()) { - coreStartTimes.put(r.getName(), getCoreStatus(r).getCoreStartTime().getTime()); + coreStartTimes.put(r.getName(), getCoreStatus(r).startTime.getTime()); } // trigger compat report by changing the schema SchemaRequest req = new SchemaRequest(); @@ -130,7 +130,7 @@ public void setupSystemCollection() throws Exception { for (Replica r : coll.getReplicas()) { long previousTime = coreStartTimes.get(r.getName()); try { - long currentTime = getCoreStatus(r).getCoreStartTime().getTime(); + long currentTime = getCoreStatus(r).startTime.getTime(); allReloaded = allReloaded && (previousTime < currentTime); } catch (Exception e) { log.warn("Error retrieving replica status of {}", Utils.toJSONString(r), e); diff --git a/solr/core/src/test/org/apache/solr/cloud/api/collections/CollectionReloadTest.java b/solr/core/src/test/org/apache/solr/cloud/api/collections/CollectionReloadTest.java index a6c961f5820..9b97b2f1772 100644 --- a/solr/core/src/test/org/apache/solr/cloud/api/collections/CollectionReloadTest.java +++ b/solr/core/src/test/org/apache/solr/cloud/api/collections/CollectionReloadTest.java @@ -61,7 +61,7 @@ public void testReloadedLeaderStateAfterZkSessionLoss() throws Exception { Replica leader = cluster.getZkStateReader().getLeaderRetry(testCollectionName, "shard1", DEFAULT_TIMEOUT); - long coreStartTime = getCoreStatus(leader).getCoreStartTime().getTime(); + long coreStartTime = getCoreStatus(leader).startTime.getTime(); CollectionAdminRequest.reloadCollection(testCollectionName).process(cluster.getSolrClient()); RetryUtil.retryUntil( @@ -72,7 +72,7 @@ public void testReloadedLeaderStateAfterZkSessionLoss() throws Exception { () -> { long restartTime; try { - restartTime = getCoreStatus(leader).getCoreStartTime().getTime(); + restartTime = getCoreStatus(leader).startTime.getTime(); } catch (Exception e) { log.warn("Exception getting core start time: ", e); return false; diff --git a/solr/core/src/test/org/apache/solr/handler/admin/CoreAdminHandlerTest.java b/solr/core/src/test/org/apache/solr/handler/admin/CoreAdminHandlerTest.java index 3f7ec3080d5..33a890ecc84 100644 --- a/solr/core/src/test/org/apache/solr/handler/admin/CoreAdminHandlerTest.java +++ b/solr/core/src/test/org/apache/solr/handler/admin/CoreAdminHandlerTest.java @@ -36,7 +36,6 @@ import org.apache.solr.client.solrj.SolrQuery; import org.apache.solr.client.solrj.impl.HttpSolrClient; import org.apache.solr.client.solrj.request.CoreAdminRequest; -import org.apache.solr.client.solrj.request.CoreStatus; import org.apache.solr.client.solrj.response.QueryResponse; import org.apache.solr.common.SolrException; import org.apache.solr.common.SolrInputDocument; @@ -464,8 +463,8 @@ public void testDeleteInstanceDirAfterCreateFailure() throws Exception { Path dataDir = null; try (SolrClient client = getHttpSolrClient(runner.getBaseUrl().toString())) { - CoreStatus status = CoreAdminRequest.getCoreStatus("corex", true, client); - String dataDirectory = status.getDataDirectory(); + final var status = CoreAdminRequest.getCoreStatus("corex", true, client); + String dataDirectory = status.dataDir; dataDir = Paths.get(dataDirectory); assertTrue(Files.exists(dataDir)); } diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/request/CoreAdminRequest.java b/solr/solrj/src/java/org/apache/solr/client/solrj/request/CoreAdminRequest.java index d31b5651fbe..db078ca2ebc 100644 --- a/solr/solrj/src/java/org/apache/solr/client/solrj/request/CoreAdminRequest.java +++ b/solr/solrj/src/java/org/apache/solr/client/solrj/request/CoreAdminRequest.java @@ -19,6 +19,7 @@ import java.io.IOException; import java.util.Arrays; import java.util.List; +import org.apache.solr.client.api.model.CoreStatusResponse; import org.apache.solr.client.solrj.SolrClient; import org.apache.solr.client.solrj.SolrRequest; import org.apache.solr.client.solrj.SolrServerException; @@ -708,17 +709,18 @@ public static CoreAdminResponse swapCore(String core1, String core2, SolrClient return req.process(client); } - public static CoreStatus getCoreStatus(String coreName, SolrClient client) + public static CoreStatusResponse.SingleCoreData getCoreStatus(String coreName, SolrClient client) throws SolrServerException, IOException { return getCoreStatus(coreName, true, client); } - public static CoreStatus getCoreStatus(String coreName, boolean getIndexInfo, SolrClient client) + public static CoreStatusResponse.SingleCoreData getCoreStatus( + String coreName, boolean getIndexInfo, SolrClient client) throws SolrServerException, IOException { CoreAdminRequest req = new CoreAdminRequest(); req.setAction(CoreAdminAction.STATUS); req.setIndexInfoNeeded(getIndexInfo); - return new CoreStatus(req.process(client).getCoreStatus(coreName)); + return req.process(client).getCoreStatus(coreName); } public static CoreAdminResponse getStatus(String name, SolrClient client) diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/request/CoreStatus.java b/solr/solrj/src/java/org/apache/solr/client/solrj/request/CoreStatus.java deleted file mode 100644 index ac720640c01..00000000000 --- a/solr/solrj/src/java/org/apache/solr/client/solrj/request/CoreStatus.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.solr.client.solrj.request; - -import java.util.Date; -import org.apache.solr.common.util.NamedList; - -public class CoreStatus { - - private final NamedList response; - - public CoreStatus(NamedList response) { - this.response = response; - } - - public String getDataDirectory() { - return (String) response.get("dataDir"); - } - - public String getInstanceDirectory() { - return (String) response.findRecursive("instanceDir"); - } - - @Override - public String toString() { - return response.toString(); - } - - public Date getCoreStartTime() { - return (Date) response.get("startTime"); - } -} diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/response/CoreAdminResponse.java b/solr/solrj/src/java/org/apache/solr/client/solrj/response/CoreAdminResponse.java index fb7baaec458..d7c4899a898 100644 --- a/solr/solrj/src/java/org/apache/solr/client/solrj/response/CoreAdminResponse.java +++ b/solr/solrj/src/java/org/apache/solr/client/solrj/response/CoreAdminResponse.java @@ -16,46 +16,40 @@ */ package org.apache.solr.client.solrj.response; +import com.fasterxml.jackson.core.type.TypeReference; import java.util.Date; -import org.apache.solr.common.util.NamedList; +import java.util.Map; +import org.apache.solr.client.api.model.CoreStatusResponse; +import org.apache.solr.client.solrj.JacksonContentWriter; /** * @since solr 1.3 */ public class CoreAdminResponse extends SolrResponseBase { - // TODO NOCOMMIT This is a cause of several test failures, and needs resolved somehow for both - // 10.0 and branch_9x - // In javabin at least, we end up as a LinkedHashMap and not a NamedList. This comes up - // periodically in v2 API transitions, but idk if I've seen a case yet where NamedList is declared - // in a method signature itself, which introduces some backcompat concerns. The ideal way to fix - // this I think would be to (1) introduce a new method returning the POJO instead of a NL, (2) - // switch usage over to the new method, (3) deprecate the old method on branch_9x, and (4) remove - // the old method on main. In the interim though (at least on branch_9x) we'll need to - // reimplement this method to swap out maps for NamedLists, so that we can obey the method - // signature. - @SuppressWarnings("unchecked") - public NamedList> getCoreStatus() { - return (NamedList>) getResponse().get("status"); + public Map getCoreStatus() { + final var allCoreStatus = getResponse().get("status"); + return JacksonContentWriter.DEFAULT_MAPPER.convertValue( + allCoreStatus, new TypeReference>() {}); } - public NamedList getCoreStatus(String core) { + public CoreStatusResponse.SingleCoreData getCoreStatus(String core) { return getCoreStatus().get(core); } public Date getStartTime(String core) { - NamedList v = getCoreStatus(core); + final var v = getCoreStatus(core); if (v == null) { return null; } - return (Date) v.get("startTime"); + return v.startTime; } public Long getUptime(String core) { - NamedList v = getCoreStatus(core); + final var v = getCoreStatus(core); if (v == null) { return null; } - return (Long) v.get("uptime"); + return v.uptime; } } diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/request/TestCoreAdmin.java b/solr/solrj/src/test/org/apache/solr/client/solrj/request/TestCoreAdmin.java index d8630d016ff..ddcd76d9a5d 100644 --- a/solr/solrj/src/test/org/apache/solr/client/solrj/request/TestCoreAdmin.java +++ b/solr/solrj/src/test/org/apache/solr/client/solrj/request/TestCoreAdmin.java @@ -264,7 +264,7 @@ public void testReloadCoreAfterFailure() throws Exception { try { cores = CoreContainer.createAndLoad(SOLR_HOME); - String ddir = CoreAdminRequest.getCoreStatus("core0", getSolrCore0()).getDataDirectory(); + String ddir = CoreAdminRequest.getCoreStatus("core0", getSolrCore0()).dataDir; Path data = Paths.get(ddir, "index"); assumeTrue("test can't handle relative data directory paths (yet?)", data.isAbsolute()); diff --git a/solr/test-framework/src/java/org/apache/solr/cloud/AbstractMoveReplicaTestBase.java b/solr/test-framework/src/java/org/apache/solr/cloud/AbstractMoveReplicaTestBase.java index 73baf2b8c84..2ca97dbbbe4 100644 --- a/solr/test-framework/src/java/org/apache/solr/cloud/AbstractMoveReplicaTestBase.java +++ b/solr/test-framework/src/java/org/apache/solr/cloud/AbstractMoveReplicaTestBase.java @@ -25,6 +25,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import org.apache.solr.client.api.model.CoreStatusResponse; import org.apache.solr.client.solrj.SolrClient; import org.apache.solr.client.solrj.SolrQuery; import org.apache.solr.client.solrj.SolrServerException; @@ -376,17 +377,16 @@ private int getNumOfCores( } // filter size by collection name int size = 0; - for (Map.Entry> stringNamedListEntry : status.getCoreStatus()) { + for (Map.Entry coreStatusEntry : + status.getCoreStatus().entrySet()) { if (collectionName != null) { - String coll = - (String) stringNamedListEntry.getValue().findRecursive("cloud", "collection"); + String coll = coreStatusEntry.getValue().cloud.collection; if (!collectionName.equals(coll)) { continue; } } if (replicaType != null) { - String type = - (String) stringNamedListEntry.getValue().findRecursive("cloud", "replicaType"); + String type = coreStatusEntry.getValue().cloud.replicaType; if (!replicaType.equals(type)) { continue; } diff --git a/solr/test-framework/src/java/org/apache/solr/cloud/SolrCloudTestCase.java b/solr/test-framework/src/java/org/apache/solr/cloud/SolrCloudTestCase.java index d410c3abeb4..c592e8d4cb5 100644 --- a/solr/test-framework/src/java/org/apache/solr/cloud/SolrCloudTestCase.java +++ b/solr/test-framework/src/java/org/apache/solr/cloud/SolrCloudTestCase.java @@ -39,13 +39,13 @@ import java.util.function.Predicate; import org.apache.lucene.tests.util.LuceneTestCase; import org.apache.solr.SolrTestCaseJ4; +import org.apache.solr.client.api.model.CoreStatusResponse; import org.apache.solr.client.solrj.SolrClient; import org.apache.solr.client.solrj.SolrRequest; import org.apache.solr.client.solrj.SolrServerException; import org.apache.solr.client.solrj.impl.CloudLegacySolrClient; import org.apache.solr.client.solrj.impl.HttpSolrClient; import org.apache.solr.client.solrj.request.CoreAdminRequest; -import org.apache.solr.client.solrj.request.CoreStatus; import org.apache.solr.common.cloud.CollectionStatePredicate; import org.apache.solr.common.cloud.DocCollection; import org.apache.solr.common.cloud.LiveNodesPredicate; @@ -325,11 +325,12 @@ protected static Replica getRandomReplica(Slice slice, Predicate matchP } /** - * Get the {@link CoreStatus} data for a {@link Replica} + * Get the {@link org.apache.solr.client.api.model.CoreStatusResponse.SingleCoreData} data for a + * {@link Replica} * *

This assumes that the replica is hosted on a live node. */ - protected static CoreStatus getCoreStatus(Replica replica) + protected static CoreStatusResponse.SingleCoreData getCoreStatus(Replica replica) throws IOException, SolrServerException { JettySolrRunner jetty = cluster.getReplicaJetty(replica); try (SolrClient client = diff --git a/solr/test-framework/src/java/org/apache/solr/cloud/api/collections/AbstractCollectionsAPIDistributedZkTestBase.java b/solr/test-framework/src/java/org/apache/solr/cloud/api/collections/AbstractCollectionsAPIDistributedZkTestBase.java index d0e250e59c7..2b1973a09ec 100644 --- a/solr/test-framework/src/java/org/apache/solr/cloud/api/collections/AbstractCollectionsAPIDistributedZkTestBase.java +++ b/solr/test-framework/src/java/org/apache/solr/cloud/api/collections/AbstractCollectionsAPIDistributedZkTestBase.java @@ -40,12 +40,12 @@ import javax.management.MBeanServerFactory; import javax.management.ObjectName; import org.apache.lucene.tests.util.TestUtil; +import org.apache.solr.client.api.model.CoreStatusResponse; import org.apache.solr.client.solrj.SolrClient; import org.apache.solr.client.solrj.SolrQuery; import org.apache.solr.client.solrj.SolrServerException; import org.apache.solr.client.solrj.request.CollectionAdminRequest; import org.apache.solr.client.solrj.request.CoreAdminRequest; -import org.apache.solr.client.solrj.request.CoreStatus; import org.apache.solr.client.solrj.request.QueryRequest; import org.apache.solr.client.solrj.request.UpdateRequest; import org.apache.solr.client.solrj.response.CollectionAdminResponse; @@ -60,7 +60,6 @@ import org.apache.solr.common.params.CollectionParams.CollectionAction; import org.apache.solr.common.params.CoreAdminParams; import org.apache.solr.common.params.ModifiableSolrParams; -import org.apache.solr.common.util.NamedList; import org.apache.solr.common.util.TimeSource; import org.apache.solr.core.CoreContainer; import org.apache.solr.core.SolrCore; @@ -568,11 +567,11 @@ private void collectStartTimes(String collectionName, Map urlToTim for (Slice shard : collectionState) { for (Replica replica : shard) { ZkCoreNodeProps coreProps = new ZkCoreNodeProps(replica); - CoreStatus coreStatus; + CoreStatusResponse.SingleCoreData coreStatus; try (SolrClient server = getHttpSolrClient(coreProps.getBaseUrl())) { coreStatus = CoreAdminRequest.getCoreStatus(coreProps.getCoreName(), false, server); } - long before = coreStatus.getCoreStartTime().getTime(); + long before = coreStatus.startTime.getTime(); urlToTime.put(coreProps.getCoreUrl(), before); } } @@ -660,8 +659,8 @@ public void addReplicaTest() throws Exception { try (SolrClient coreclient = getHttpSolrClient(newReplica.getBaseUrl())) { CoreAdminResponse status = CoreAdminRequest.getStatus(newReplica.getStr("core"), coreclient); - NamedList coreStatus = status.getCoreStatus(newReplica.getStr("core")); - String instanceDirStr = (String) coreStatus.get("instanceDir"); + final var coreStatus = status.getCoreStatus(newReplica.getStr("core")); + String instanceDirStr = coreStatus.instanceDir; assertEquals(instanceDirStr, instancePath.toString()); } From d13f62f385a67b24e22037dd7abba9f512edff2f Mon Sep 17 00:00:00 2001 From: Jason Gerlowski Date: Wed, 22 Jan 2025 07:23:51 -0500 Subject: [PATCH 07/10] Remove unnecessary TestCoreAdminApis --- .../solr/client/api/endpoint/CoreApis.java | 6 +- .../solr/handler/admin/TestApiFramework.java | 45 ++++++++- .../solr/handler/admin/TestCoreAdminApis.java | 97 ------------------- 3 files changed, 47 insertions(+), 101 deletions(-) delete mode 100644 solr/core/src/test/org/apache/solr/handler/admin/TestCoreAdminApis.java diff --git a/solr/api/src/java/org/apache/solr/client/api/endpoint/CoreApis.java b/solr/api/src/java/org/apache/solr/client/api/endpoint/CoreApis.java index 43716529bab..306e9136797 100644 --- a/solr/api/src/java/org/apache/solr/client/api/endpoint/CoreApis.java +++ b/solr/api/src/java/org/apache/solr/client/api/endpoint/CoreApis.java @@ -22,9 +22,9 @@ import jakarta.ws.rs.Path; import jakarta.ws.rs.PathParam; import jakarta.ws.rs.QueryParam; +import org.apache.solr.client.api.model.CoreStatusResponse; import org.apache.solr.client.api.model.CreateCoreParams; import org.apache.solr.client.api.model.CreateCoreResponse; -import org.apache.solr.client.api.model.SolrJerseyResponse; public interface CoreApis { @@ -41,7 +41,7 @@ interface GetStatus { @GET @Operation(summary = "Fetch status info for all cores hosted on this node.", tags = "cores") - SolrJerseyResponse getAllCoreStatus(@QueryParam("indexInfo") Boolean indexInfo) + CoreStatusResponse getAllCoreStatus(@QueryParam("indexInfo") Boolean indexInfo) throws Exception; @GET @@ -49,7 +49,7 @@ SolrJerseyResponse getAllCoreStatus(@QueryParam("indexInfo") Boolean indexInfo) summary = "Fetch status info for the core hosted on this node with the specified name.", tags = "cores") @Path("/{coreName}") - SolrJerseyResponse getCoreStatus( + CoreStatusResponse getCoreStatus( @PathParam("coreName") String coreName, @QueryParam("indexInfo") Boolean indexInfo) throws Exception; } diff --git a/solr/core/src/test/org/apache/solr/handler/admin/TestApiFramework.java b/solr/core/src/test/org/apache/solr/handler/admin/TestApiFramework.java index 8600ed8236b..fa9fd0b9c41 100644 --- a/solr/core/src/test/org/apache/solr/handler/admin/TestApiFramework.java +++ b/solr/core/src/test/org/apache/solr/handler/admin/TestApiFramework.java @@ -24,14 +24,22 @@ import static org.apache.solr.common.params.CommonParams.CONFIGSETS_HANDLER_PATH; import static org.apache.solr.common.params.CommonParams.CORES_HANDLER_PATH; import static org.apache.solr.common.util.ValidatingJsonMap.NOT_NULL; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import java.io.ByteArrayInputStream; import java.io.InputStream; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Properties; import java.util.Set; import org.apache.solr.SolrTestCaseJ4; import org.apache.solr.api.Api; @@ -72,7 +80,7 @@ public class TestApiFramework extends SolrTestCaseJ4 { public void testFramework() { Map calls = new HashMap<>(); Map out = new HashMap<>(); - CoreContainer mockCC = TestCoreAdminApis.getCoreContainerMock(calls, out); + CoreContainer mockCC = getCoreContainerMock(calls, out); PluginBag containerHandlers = new PluginBag<>(SolrRequestHandler.class, null, false); TestCollectionAPIs.MockCollectionsHandler collectionsHandler = @@ -364,4 +372,39 @@ public static void assertConditions(Map root, Map conditio } } } + + public static CoreContainer getCoreContainerMock( + final Map in, Map out) { + assumeWorkingMockito(); + + CoreContainer mockCC = mock(CoreContainer.class); + when(mockCC.create(any(String.class), any(Path.class), any(Map.class), anyBoolean())) + .thenAnswer( + invocationOnMock -> { + in.put("create", invocationOnMock.getArguments()); + return null; + }); + + doAnswer( + invocationOnMock -> { + in.put("rename", invocationOnMock.getArguments()); + return null; + }) + .when(mockCC) + .rename(any(String.class), any(String.class)); + + doAnswer( + invocationOnMock -> { + in.put("unload", invocationOnMock.getArguments()); + return null; + }) + .when(mockCC) + .unload(any(String.class), anyBoolean(), anyBoolean(), anyBoolean()); + + when(mockCC.getCoreRootDirectory()).thenReturn(Paths.get("coreroot")); + when(mockCC.getContainerProperties()).thenReturn(new Properties()); + + when(mockCC.getRequestHandlers()).thenAnswer(invocationOnMock -> out.get("getRequestHandlers")); + return mockCC; + } } diff --git a/solr/core/src/test/org/apache/solr/handler/admin/TestCoreAdminApis.java b/solr/core/src/test/org/apache/solr/handler/admin/TestCoreAdminApis.java deleted file mode 100644 index 8a8ac0652bf..00000000000 --- a/solr/core/src/test/org/apache/solr/handler/admin/TestCoreAdminApis.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.solr.handler.admin; - -import static org.apache.solr.common.util.Utils.fromJSONString; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.anyBoolean; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.Properties; -import org.apache.solr.SolrTestCaseJ4; -import org.apache.solr.api.Api; -import org.apache.solr.api.ApiBag; -import org.apache.solr.client.solrj.SolrRequest; -import org.apache.solr.core.CoreContainer; - -public class TestCoreAdminApis extends SolrTestCaseJ4 { - - public void testCalls() throws Exception { - Map calls = new HashMap<>(); - CoreContainer mockCC = getCoreContainerMock(calls, new HashMap<>()); - - ApiBag apiBag; - try (CoreAdminHandler coreAdminHandler = new CoreAdminHandler(mockCC)) { - apiBag = new ApiBag(false); - for (Api api : coreAdminHandler.getApis()) { - apiBag.register(api, Collections.emptyMap()); - } - } - TestCollectionAPIs.makeCall( - apiBag, - "/cores", - SolrRequest.METHOD.POST, - "{create:{name: hello, instanceDir : someDir, schema: 'schema.xml'}}"); - Object[] params = calls.get("create"); - assertEquals("hello", params[0]); - assertEquals(fromJSONString("{schema : schema.xml}"), params[2]); - } - - @SuppressWarnings({"unchecked"}) - public static CoreContainer getCoreContainerMock( - final Map in, Map out) { - assumeWorkingMockito(); - - CoreContainer mockCC = mock(CoreContainer.class); - when(mockCC.create(any(String.class), any(Path.class), any(Map.class), anyBoolean())) - .thenAnswer( - invocationOnMock -> { - in.put("create", invocationOnMock.getArguments()); - return null; - }); - - doAnswer( - invocationOnMock -> { - in.put("rename", invocationOnMock.getArguments()); - return null; - }) - .when(mockCC) - .rename(any(String.class), any(String.class)); - - doAnswer( - invocationOnMock -> { - in.put("unload", invocationOnMock.getArguments()); - return null; - }) - .when(mockCC) - .unload(any(String.class), anyBoolean(), anyBoolean(), anyBoolean()); - - when(mockCC.getCoreRootDirectory()).thenReturn(Paths.get("coreroot")); - when(mockCC.getContainerProperties()).thenReturn(new Properties()); - - when(mockCC.getRequestHandlers()).thenAnswer(invocationOnMock -> out.get("getRequestHandlers")); - return mockCC; - } -} From 420a752dd9bf4d919631723ef584f8f61fd37b0d Mon Sep 17 00:00:00 2001 From: Jason Gerlowski Date: Wed, 22 Jan 2025 08:17:14 -0500 Subject: [PATCH 08/10] Generated v2 req/rsp usage --- .../client/api/model/CoreStatusResponse.java | 4 ++-- .../java/org/apache/solr/cli/CLIUtils.java | 22 +++++++++---------- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/solr/api/src/java/org/apache/solr/client/api/model/CoreStatusResponse.java b/solr/api/src/java/org/apache/solr/client/api/model/CoreStatusResponse.java index e9361972aa2..ca83bb2b29b 100644 --- a/solr/api/src/java/org/apache/solr/client/api/model/CoreStatusResponse.java +++ b/solr/api/src/java/org/apache/solr/client/api/model/CoreStatusResponse.java @@ -22,9 +22,9 @@ public class CoreStatusResponse extends SolrJerseyResponse { - // In the v1 code this is a Map of Exception instances by core name. How are exceptions + // NOCOMMIT In the v1 code this is a Map of Exception instances by core name. How are exceptions // serialized out by things though, that's what I'd have to mirror on the v2 side. - @JsonProperty public Map initFailures; + @JsonProperty public Map initFailures; @JsonProperty public Map status; diff --git a/solr/core/src/java/org/apache/solr/cli/CLIUtils.java b/solr/core/src/java/org/apache/solr/cli/CLIUtils.java index b3336eb2361..696061922cb 100644 --- a/solr/core/src/java/org/apache/solr/cli/CLIUtils.java +++ b/solr/core/src/java/org/apache/solr/cli/CLIUtils.java @@ -19,7 +19,6 @@ import static org.apache.solr.common.SolrException.ErrorCode.FORBIDDEN; import static org.apache.solr.common.SolrException.ErrorCode.UNAUTHORIZED; -import static org.apache.solr.common.params.CommonParams.NAME; import static org.apache.solr.common.params.CommonParams.SYSTEM_INFO_PATH; import java.io.IOException; @@ -45,7 +44,7 @@ import org.apache.solr.client.solrj.impl.Http2SolrClient; import org.apache.solr.client.solrj.impl.SolrZkClientTimeout; import org.apache.solr.client.solrj.request.CollectionAdminRequest; -import org.apache.solr.client.solrj.request.CoreAdminRequest; +import org.apache.solr.client.solrj.request.CoresApi; import org.apache.solr.client.solrj.request.GenericSolrRequest; import org.apache.solr.common.SolrException; import org.apache.solr.common.cloud.SolrZkClient; @@ -321,16 +320,15 @@ public static boolean safeCheckCoreExists(String solrUrl, String coreName, Strin final int clamPeriodForStatusPollMs = 1000; Thread.sleep(clamPeriodForStatusPollMs); } - NamedList existsCheckResult = - CoreAdminRequest.getStatus(coreName, solrClient).getResponse(); - NamedList status = (NamedList) existsCheckResult.get("status"); - NamedList coreStatus = (NamedList) status.get(coreName); - Map failureStatus = - (Map) existsCheckResult.get("initFailures"); - String errorMsg = (String) failureStatus.get(coreName); - final boolean hasName = coreStatus != null && coreStatus.get(NAME) != null; - exists = hasName || errorMsg != null; - wait = hasName && errorMsg == null && "true".equals(coreStatus.get("isLoading")); + final var coreStatusReq = new CoresApi.GetCoreStatus(coreName); + final var coreStatusRsp = coreStatusReq.process(solrClient).getParsed(); + final var coreStatusByName = coreStatusRsp.status; + final var coreStatus = coreStatusByName.get(coreName); + final var failureStatus = coreStatusRsp.initFailures; + final var initFailureForCore = failureStatus.get(coreName); + final boolean hasName = coreStatus != null && coreStatus.name != null; + exists = hasName || initFailureForCore != null; + wait = hasName && initFailureForCore == null && "true".equals(coreStatus.isLoading); } while (wait && System.nanoTime() - startWaitAt < MAX_WAIT_FOR_CORE_LOAD_NANOS); } catch (Exception exc) { // just ignore it since we're only interested in a positive result here From 1f92fb88a77d16f82233756aab73af6d5029335d Mon Sep 17 00:00:00 2001 From: Jason Gerlowski Date: Wed, 22 Jan 2025 08:53:01 -0500 Subject: [PATCH 09/10] Fix lingering test failures Thought this code was already on the branch prior to creating the PR, but neglected to commit/push I guess. Tests should be passing with this commit. --- .../solr/handler/admin/api/CreateCore.java | 3 +++ .../admin/CoreAdminCreateDiscoverTest.java | 18 ++++++++++++------ .../handler/admin/CoreAdminHandlerTest.java | 19 ++++++++++++------- .../handler/admin/CoreAdminOperationTest.java | 4 +++- .../solr/schema/TestCloudManagedSchema.java | 15 +++++++++++---- .../apache/solr/schema/TestManagedSchema.java | 12 ++++++++---- ...estSimplePropagatorDistributedTracing.java | 4 ++-- 7 files changed, 51 insertions(+), 24 deletions(-) diff --git a/solr/core/src/java/org/apache/solr/handler/admin/api/CreateCore.java b/solr/core/src/java/org/apache/solr/handler/admin/api/CreateCore.java index bc45bf5049c..14807e3a36a 100644 --- a/solr/core/src/java/org/apache/solr/handler/admin/api/CreateCore.java +++ b/solr/core/src/java/org/apache/solr/handler/admin/api/CreateCore.java @@ -74,6 +74,9 @@ public CreateCore( public CreateCoreResponse createCore(CreateCoreParams requestBody) throws Exception { final var response = instantiateJerseyResponse(CreateCoreResponse.class); + ensureRequiredRequestBodyProvided(requestBody); + ensureRequiredParameterProvided("name", requestBody.name); + assert TestInjection.injectRandomDelayInCoreCreation(); ensureRequiredRequestBodyProvided(requestBody); ensureRequiredParameterProvided("name", requestBody.name); diff --git a/solr/core/src/test/org/apache/solr/handler/admin/CoreAdminCreateDiscoverTest.java b/solr/core/src/test/org/apache/solr/handler/admin/CoreAdminCreateDiscoverTest.java index 81a16ab858f..ae50c308f90 100644 --- a/solr/core/src/test/org/apache/solr/handler/admin/CoreAdminCreateDiscoverTest.java +++ b/solr/core/src/test/org/apache/solr/handler/admin/CoreAdminCreateDiscoverTest.java @@ -16,17 +16,20 @@ */ package org.apache.solr.handler.admin; +import com.fasterxml.jackson.core.type.TypeReference; import java.io.File; import java.io.IOException; import java.io.Reader; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; +import java.util.Map; import java.util.Properties; import org.apache.solr.SolrTestCaseJ4; +import org.apache.solr.client.api.model.CoreStatusResponse; +import org.apache.solr.client.solrj.JacksonContentWriter; import org.apache.solr.common.SolrException; import org.apache.solr.common.params.CoreAdminParams; -import org.apache.solr.common.util.NamedList; import org.apache.solr.core.CorePropertiesLocator; import org.apache.solr.response.SolrQueryResponse; import org.junit.AfterClass; @@ -247,11 +250,14 @@ public void testInstanceDirAsPropertyParam() throws Exception { CoreAdminParams.CORE, "testInstanceDirAsPropertyParam"), resp); - NamedList status = (NamedList) resp.getValues().get("status"); - assertNotNull(status); - NamedList coreProps = (NamedList) status.get("testInstanceDirAsPropertyParam"); - assertNotNull(status); - String instanceDir = (String) coreProps.get("instanceDir"); + final var statusByCore = + JacksonContentWriter.DEFAULT_MAPPER.convertValue( + resp.getValues().get("status"), + new TypeReference>() {}); + assertNotNull(statusByCore); + final var coreProps = statusByCore.get("testInstanceDirAsPropertyParam"); + assertNotNull(coreProps); + String instanceDir = coreProps.instanceDir; assertNotNull(instanceDir); assertEquals( "Instance dir does not match param given in property.instanceDir syntax", diff --git a/solr/core/src/test/org/apache/solr/handler/admin/CoreAdminHandlerTest.java b/solr/core/src/test/org/apache/solr/handler/admin/CoreAdminHandlerTest.java index 33a890ecc84..3e17d115a93 100644 --- a/solr/core/src/test/org/apache/solr/handler/admin/CoreAdminHandlerTest.java +++ b/solr/core/src/test/org/apache/solr/handler/admin/CoreAdminHandlerTest.java @@ -18,6 +18,7 @@ import static org.apache.solr.handler.admin.CoreAdminHandler.CoreAdminAsyncTracker.COMPLETED; +import com.fasterxml.jackson.core.type.TypeReference; import java.io.Reader; import java.nio.charset.StandardCharsets; import java.nio.file.Files; @@ -32,6 +33,8 @@ import org.apache.commons.io.file.PathUtils; import org.apache.lucene.util.Constants; import org.apache.solr.SolrTestCaseJ4; +import org.apache.solr.client.api.model.CoreStatusResponse; +import org.apache.solr.client.solrj.JacksonContentWriter; import org.apache.solr.client.solrj.SolrClient; import org.apache.solr.client.solrj.SolrQuery; import org.apache.solr.client.solrj.impl.HttpSolrClient; @@ -40,7 +43,6 @@ import org.apache.solr.common.SolrException; import org.apache.solr.common.SolrInputDocument; import org.apache.solr.common.params.CoreAdminParams; -import org.apache.solr.common.util.NamedList; import org.apache.solr.core.CoreContainer; import org.apache.solr.core.CoreDescriptor; import org.apache.solr.core.SolrCore; @@ -228,8 +230,11 @@ public void testCoreAdminHandler() throws Exception { Map failures = (Map) resp.getValues().get("initFailures"); assertNotNull("core failures is null", failures); - NamedList status = (NamedList) resp.getValues().get("status"); - assertNotNull("core status is null", status); + final var statusByCore = + JacksonContentWriter.DEFAULT_MAPPER.convertValue( + resp.getValues().get("status"), + new TypeReference>() {}); + assertNotNull("core status is null", statusByCore); assertEquals("wrong number of core failures", 1, failures.size()); Exception fail = failures.get("bogus_dir_core"); @@ -238,10 +243,10 @@ public void testCoreAdminHandler() throws Exception { "init failure doesn't mention problem: " + fail.getCause().getMessage(), 0 < fail.getCause().getMessage().indexOf("dir_does_not_exist")); - assertEquals( - "bogus_dir_core status isn't empty", - 0, - ((NamedList) status.get("bogus_dir_core")).size()); + assertTrue("bogus_dir_core status isn't empty", statusByCore.containsKey("bogus_dir_core")); + final var bogusDirCoreStatus = statusByCore.get("bogus_dir_core"); + assertNull(bogusDirCoreStatus.name); + assertNull(bogusDirCoreStatus.config); // Try renaming the core, we should fail // First assert that the props core exists diff --git a/solr/core/src/test/org/apache/solr/handler/admin/CoreAdminOperationTest.java b/solr/core/src/test/org/apache/solr/handler/admin/CoreAdminOperationTest.java index 928e2e94862..97f40bdecc9 100644 --- a/solr/core/src/test/org/apache/solr/handler/admin/CoreAdminOperationTest.java +++ b/solr/core/src/test/org/apache/solr/handler/admin/CoreAdminOperationTest.java @@ -35,6 +35,7 @@ public class CoreAdminOperationTest extends SolrTestCaseJ4 { private CoreAdminHandler.CallInfo callInfo; private SolrQueryRequest mockRequest; + private CoreAdminHandler mockHandler; @BeforeClass public static void setUpClass() { @@ -47,7 +48,8 @@ public void setUp() throws Exception { super.setUp(); mockRequest = mock(SolrQueryRequest.class); - callInfo = new CoreAdminHandler.CallInfo(null, mockRequest, null, null); + mockHandler = mock(CoreAdminHandler.class); + callInfo = new CoreAdminHandler.CallInfo(mockHandler, mockRequest, null, null); } @Override diff --git a/solr/core/src/test/org/apache/solr/schema/TestCloudManagedSchema.java b/solr/core/src/test/org/apache/solr/schema/TestCloudManagedSchema.java index 5c0f4f49d22..e758b7326f1 100644 --- a/solr/core/src/test/org/apache/solr/schema/TestCloudManagedSchema.java +++ b/solr/core/src/test/org/apache/solr/schema/TestCloudManagedSchema.java @@ -16,9 +16,13 @@ */ package org.apache.solr.schema; +import com.fasterxml.jackson.core.type.TypeReference; import java.nio.charset.StandardCharsets; import java.util.List; +import java.util.Map; import java.util.concurrent.TimeUnit; +import org.apache.solr.client.api.model.CoreStatusResponse; +import org.apache.solr.client.solrj.JacksonContentWriter; import org.apache.solr.client.solrj.SolrClient; import org.apache.solr.client.solrj.impl.HttpSolrClient; import org.apache.solr.client.solrj.request.QueryRequest; @@ -60,14 +64,17 @@ public void test() throws Exception { try (SolrClient rootClient = new HttpSolrClient.Builder(buildUrl(jettys.get(which).getLocalPort())).build()) { NamedList namedListResponse = rootClient.request(request); - NamedList status = (NamedList) namedListResponse.get("status"); - NamedList collectionStatus = (NamedList) status.getVal(0); - String collectionSchema = (String) collectionStatus.get(CoreAdminParams.SCHEMA); + final var statusByCore = + JacksonContentWriter.DEFAULT_MAPPER.convertValue( + namedListResponse.get("status"), + new TypeReference>() {}); + final String coreName = statusByCore.keySet().stream().findFirst().get(); + final var collectionStatus = statusByCore.get(coreName); // Make sure the upgrade to managed schema happened assertEquals( "Schema resource name differs from expected name", "managed-schema.xml", - collectionSchema); + collectionStatus.schema); } try (SolrZkClient zkClient = diff --git a/solr/core/src/test/org/apache/solr/schema/TestManagedSchema.java b/solr/core/src/test/org/apache/solr/schema/TestManagedSchema.java index ec5d288bc7f..7efbdf09aa9 100644 --- a/solr/core/src/test/org/apache/solr/schema/TestManagedSchema.java +++ b/solr/core/src/test/org/apache/solr/schema/TestManagedSchema.java @@ -16,6 +16,7 @@ */ package org.apache.solr.schema; +import com.fasterxml.jackson.core.type.TypeReference; import java.io.File; import java.io.InputStream; import java.lang.invoke.MethodHandles; @@ -26,6 +27,8 @@ import java.util.Map; import java.util.regex.Pattern; import org.apache.commons.io.FileUtils; +import org.apache.solr.client.api.model.CoreStatusResponse; +import org.apache.solr.client.solrj.JacksonContentWriter; import org.apache.solr.common.SolrException; import org.apache.solr.common.params.CoreAdminParams; import org.apache.solr.common.util.NamedList; @@ -158,13 +161,14 @@ private void assertSchemaResource(String collection, String expectedSchemaResour admin.handleRequestBody(request, response); assertNull("Exception on create", response.getException()); NamedList responseValues = response.getValues(); - NamedList status = (NamedList) responseValues.get("status"); - NamedList collectionStatus = (NamedList) status.get(collection); - String collectionSchema = (String) collectionStatus.get(CoreAdminParams.SCHEMA); + final var statusByCore = + JacksonContentWriter.DEFAULT_MAPPER.convertValue( + responseValues.get("status"), + new TypeReference>() {}); assertEquals( "Schema resource name differs from expected name", expectedSchemaResource, - collectionSchema); + statusByCore.get(collection).schema); } public void testAddFieldWhenNotMutable() throws Exception { diff --git a/solr/core/src/test/org/apache/solr/util/tracing/TestSimplePropagatorDistributedTracing.java b/solr/core/src/test/org/apache/solr/util/tracing/TestSimplePropagatorDistributedTracing.java index 36a4788d93e..8b1af81c59e 100644 --- a/solr/core/src/test/org/apache/solr/util/tracing/TestSimplePropagatorDistributedTracing.java +++ b/solr/core/src/test/org/apache/solr/util/tracing/TestSimplePropagatorDistributedTracing.java @@ -35,7 +35,7 @@ import org.apache.solr.cloud.SolrCloudTestCase; import org.apache.solr.common.util.SuppressForbidden; import org.apache.solr.core.SolrCore; -import org.apache.solr.handler.admin.CoreAdminOperation; +import org.apache.solr.handler.admin.api.CreateCore; import org.apache.solr.logging.MDCLoggingContext; import org.apache.solr.update.processor.LogUpdateProcessorFactory; import org.apache.solr.util.LogListener; @@ -191,7 +191,7 @@ public void testInternalCollectionApiCommands() throws Exception { } private void verifyCollectionCreation(String collection) throws Exception { - try (LogListener reqLog = LogListener.info(CoreAdminOperation.class.getName())) { + try (LogListener reqLog = LogListener.info(CreateCore.class.getName())) { var a1 = CollectionAdminRequest.createCollection(collection, 2, 2); CollectionAdminResponse r1 = a1.process(cluster.getSolrClient()); assertEquals(0, r1.getStatus()); From ba14d857d8ea8590a0a5c9188f71a9854a6fe814 Mon Sep 17 00:00:00 2001 From: Jason Gerlowski Date: Wed, 5 Feb 2025 13:32:53 -0800 Subject: [PATCH 10/10] Remove unused method --- .../org/apache/solr/handler/admin/CoreAdminHandler.java | 9 --------- .../apache/solr/handler/admin/CoreAdminOperation.java | 1 - 2 files changed, 10 deletions(-) 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 284c95e7a7a..b073bc1bf5f 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 @@ -25,8 +25,6 @@ import com.github.benmanes.caffeine.cache.Caffeine; import com.github.benmanes.caffeine.cache.Expiry; import com.github.benmanes.caffeine.cache.Ticker; - -import java.io.File; import java.lang.invoke.MethodHandles; import java.util.ArrayList; import java.util.Collection; @@ -286,13 +284,6 @@ private static Map initializeOpMap() { return opMap; } - protected static String normalizePath(String path) { - if (path == null) return null; - path = path.replace('/', File.separatorChar); - path = path.replace('\\', File.separatorChar); - return path; - } - public static ModifiableSolrParams params(String... params) { ModifiableSolrParams msp = new ModifiableSolrParams(); for (int i = 0; i < params.length; i += 2) { 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 f53f95a7871..6d1af7af61f 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 @@ -39,7 +39,6 @@ import static org.apache.solr.common.params.CoreAdminParams.CoreAdminAction.SWAP; import static org.apache.solr.common.params.CoreAdminParams.CoreAdminAction.UNLOAD; import static org.apache.solr.handler.admin.CoreAdminHandler.CallInfo; -import static org.apache.solr.handler.admin.CoreAdminHandler.normalizePath; import static org.apache.solr.util.FileUtils.normalizeToOsPathSeparator; import java.io.IOException;