From 80e258204cd256cf1d7f1471e1be563640dcc3fe Mon Sep 17 00:00:00 2001 From: Isabelle Giguere Date: Fri, 12 Dec 2025 11:17:26 -0500 Subject: [PATCH 01/15] SOLR-17136 initial commit: classes SystemInfoRequest and SystemInfoResponse --- .../solrj/request/SystemInfoRequest.java | 35 ++++ .../solrj/response/SystemInfoResponse.java | 160 ++++++++++++++++++ 2 files changed, 195 insertions(+) create mode 100644 solr/solrj/src/java/org/apache/solr/client/solrj/request/SystemInfoRequest.java create mode 100644 solr/solrj/src/java/org/apache/solr/client/solrj/response/SystemInfoResponse.java diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/request/SystemInfoRequest.java b/solr/solrj/src/java/org/apache/solr/client/solrj/request/SystemInfoRequest.java new file mode 100644 index 00000000000..617b15637c3 --- /dev/null +++ b/solr/solrj/src/java/org/apache/solr/client/solrj/request/SystemInfoRequest.java @@ -0,0 +1,35 @@ +package org.apache.solr.client.solrj.request; + + +import org.apache.solr.client.solrj.SolrRequest; +import org.apache.solr.client.solrj.response.SystemInfoResponse; +import org.apache.solr.common.params.CommonParams; +import org.apache.solr.common.params.SolrParams; +import org.apache.solr.common.util.NamedList; + +/** + * Class to get a "/admin/info/system" response. + */ +public class SystemInfoRequest extends SolrRequest { + + private static final long serialVersionUID = 1L; + + public SystemInfoRequest() { + super(METHOD.GET, CommonParams.SYSTEM_INFO_PATH, SolrRequestType.ADMIN); + } + + public SystemInfoRequest(String path) { + super(METHOD.GET, path, SolrRequestType.ADMIN); + } + + @Override + public SolrParams getParams() { + return null; // no params to return + } + + @Override + protected SystemInfoResponse createResponse(NamedList namedList) { + return (SystemInfoResponse) namedList.get("response"); + } + +} diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/response/SystemInfoResponse.java b/solr/solrj/src/java/org/apache/solr/client/solrj/response/SystemInfoResponse.java new file mode 100644 index 00000000000..58f0ac33cce --- /dev/null +++ b/solr/solrj/src/java/org/apache/solr/client/solrj/response/SystemInfoResponse.java @@ -0,0 +1,160 @@ +/* + * 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.response; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.solr.client.api.model.SolrJerseyResponse; +import org.apache.solr.common.util.NamedList; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.OptBoolean; + +/** + * This class represents a response from "/admin/info/system" + */ +public class SystemInfoResponse extends SolrJerseyResponse { + + private static final long serialVersionUID = 1L; + + @JsonProperty("mode") + public String mode; + + @JsonProperty("zkHost") + public String zkHost; + + @JsonProperty("solr_home") + public String solrHome; + + @JsonProperty("core_root") + public String coreRoot; + + @JsonProperty("node") + public String node; + + @JsonProperty("lucene") + public Lucene lucene; + + @JsonProperty("jvm") + public JVM jvm; + + @JsonProperty("security") + public Security security; + + private Map system = new HashMap<>();; + + @JsonAnyGetter + public Map getSystem() { + return system; + } + + @JsonAnySetter + public void setSystemProperty(String key, String value) { + this.system.put(key, value); + } + + /** /admin/info/system/security */ + public class Security { + @JsonProperty("tls") + public boolean tls; + } + + /** /admin/info/system/lucene */ + public class Lucene { + @JsonProperty("solr-spec-version") + public String solrSpecVersion; + + @JsonProperty("solr-impl-version") + public String solrImplVersion; + + @JsonProperty("lucene-spec-version") + public String luceneSpecVersion; + + @JsonProperty("lucene-impl-version") + public String luceneImplVersion; + + } + + /** /admin/info/system/jvm */ + public class JVM extends Vendor { + @JsonProperty("processors") + public int processors; + + @JsonProperty("jre") + public Vendor jre; + + @JsonProperty("spec") + public Vendor spec; + + @JsonProperty("vm") + public Vendor vm; + } + + public class JvmMemory { + + @JsonProperty("free") + public String free; + @JsonProperty("total") + public String total; + @JsonProperty("max") + public String max; + @JsonProperty("used") + public String used; + + @JsonProperty("raw") + public JvmMemoryRaw raw; + } + + public class JvmMemoryRaw { + @JsonProperty("free") + public long free; + @JsonProperty("total") + public long total; + @JsonProperty("max") + public long max; + @JsonProperty("used") + public long used; + @JsonProperty("used%") + public double usedPercent; + } + + + public class Vendor { + @JsonProperty(value="name", isRequired=OptBoolean.FALSE) + public String name; + @JsonProperty(value="vendor", isRequired=OptBoolean.FALSE) + public String vendor; + @JsonProperty(value="version") + public String version; + } + + public class JvmJmx { + @JsonProperty(value="classpath") + public String classpath; + @JsonProperty(value="startTime") + public String startTime; + @JsonProperty(value="upTimeMS") + public long upTimeMS; + + @JsonProperty(value="commandLineArgs") + public List commandLineArgs; + } +} From 226448bb6fe05d6a402f7a36717b7356ba9eb02a Mon Sep 17 00:00:00 2001 From: Isabelle Giguere Date: Sun, 14 Dec 2025 13:37:44 -0500 Subject: [PATCH 02/15] SOLR-17136: integrate SystemInfoRequest/Response SystemInfoRequest/Response in CLI and package manager --- .../java/org/apache/solr/cli/CLIUtils.java | 19 ++++---- .../java/org/apache/solr/cli/CreateTool.java | 15 ++++--- .../org/apache/solr/cli/HealthcheckTool.java | 21 +++++---- .../java/org/apache/solr/cli/StatusTool.java | 43 ++++++++++++++----- .../packagemanager/RepositoryManager.java | 15 ++++--- .../packagemanager/TestRepositoryManager.java | 35 +++++++++++++++ .../solrj/response/SystemInfoResponse.java | 10 ++++- 7 files changed, 119 insertions(+), 39 deletions(-) create mode 100644 solr/core/src/test/org/apache/solr/packagemanager/TestRepositoryManager.java 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 0520fab4e51..7ea03579bb8 100644 --- a/solr/core/src/java/org/apache/solr/cli/CLIUtils.java +++ b/solr/core/src/java/org/apache/solr/cli/CLIUtils.java @@ -46,6 +46,8 @@ import org.apache.solr.client.solrj.request.CollectionAdminRequest; import org.apache.solr.client.solrj.request.CoresApi; import org.apache.solr.client.solrj.request.GenericSolrRequest; +import org.apache.solr.client.solrj.request.SystemInfoRequest; +import org.apache.solr.client.solrj.response.SystemInfoResponse; import org.apache.solr.common.SolrException; import org.apache.solr.common.cloud.SolrZkClient; import org.apache.solr.common.cloud.ZkStateReader; @@ -248,13 +250,13 @@ public static String getZkHost(CommandLine cli) throws Exception { } try (SolrClient solrClient = getSolrClient(cli)) { - // hit Solr to get system info - NamedList systemInfo = - solrClient.request( - new GenericSolrRequest(SolrRequest.METHOD.GET, CommonParams.SYSTEM_INFO_PATH)); +// // hit Solr to get system info +// NamedList systemInfo = +// solrClient.request( +// new GenericSolrRequest(SolrRequest.METHOD.GET, CommonParams.SYSTEM_INFO_PATH)); // convert raw JSON into user-friendly output - Map status = StatusTool.reportStatus(systemInfo, solrClient); + Map status = StatusTool.reportStatus(solrClient); @SuppressWarnings("unchecked") Map cloud = (Map) status.get("cloud"); if (cloud != null) { @@ -357,9 +359,10 @@ public static boolean safeCheckCoreExists(String solrUrl, String coreName, Strin } public static boolean isCloudMode(SolrClient solrClient) throws SolrServerException, IOException { - NamedList systemInfo = - solrClient.request(new GenericSolrRequest(SolrRequest.METHOD.GET, SYSTEM_INFO_PATH)); - return "solrcloud".equals(systemInfo.get("mode")); +// NamedList systemInfo = +// solrClient.request(new GenericSolrRequest(SolrRequest.METHOD.GET, SYSTEM_INFO_PATH)); + SystemInfoResponse sysInfoResponse = (new SystemInfoRequest()).process(solrClient); + return "solrcloud".equals(sysInfoResponse.mode); } public static Path getConfigSetsDir(Path solrInstallDir) { diff --git a/solr/core/src/java/org/apache/solr/cli/CreateTool.java b/solr/core/src/java/org/apache/solr/cli/CreateTool.java index b6790b4a4fa..05bff9b0949 100644 --- a/solr/core/src/java/org/apache/solr/cli/CreateTool.java +++ b/solr/core/src/java/org/apache/solr/cli/CreateTool.java @@ -36,7 +36,9 @@ import org.apache.solr.client.solrj.request.CollectionAdminRequest; import org.apache.solr.client.solrj.request.CoreAdminRequest; import org.apache.solr.client.solrj.request.GenericSolrRequest; +import org.apache.solr.client.solrj.request.SystemInfoRequest; import org.apache.solr.client.solrj.response.CoreAdminResponse; +import org.apache.solr.client.solrj.response.SystemInfoResponse; import org.apache.solr.client.solrj.response.json.JsonMapResponseParser; import org.apache.solr.cloud.ZkConfigSetService; import org.apache.solr.common.cloud.ZkStateReader; @@ -157,14 +159,17 @@ protected void createCore(CommandLine cli, SolrClient solrClient) throws Excepti } printDefaultConfigsetWarningIfNecessary(cli); - String coreRootDirectory; // usually same as solr home, but not always + //String coreRootDirectory; // usually same as solr home, but not always - NamedList systemInfo = - solrClient.request( - new GenericSolrRequest(SolrRequest.METHOD.GET, CommonParams.SYSTEM_INFO_PATH)); +// NamedList systemInfo = +// solrClient.request( +// new GenericSolrRequest(SolrRequest.METHOD.GET, CommonParams.SYSTEM_INFO_PATH)); + SystemInfoResponse sysInfoResponse = (new SystemInfoRequest()).process(solrClient); // convert raw JSON into user-friendly output - coreRootDirectory = (String) systemInfo.get("core_root"); + //String coreRootDirectory = (String) systemInfo.get("core_root"); + // usually same as solr home, but not always + String coreRootDirectory = sysInfoResponse.coreRoot != null ? sysInfoResponse.coreRoot : sysInfoResponse.solrHome; if (CLIUtils.safeCheckCoreExists( solrUrl, coreName, cli.getOptionValue(CommonCLIOptions.CREDENTIALS_OPTION))) { diff --git a/solr/core/src/java/org/apache/solr/cli/HealthcheckTool.java b/solr/core/src/java/org/apache/solr/cli/HealthcheckTool.java index ef0f61bebda..216aad96369 100644 --- a/solr/core/src/java/org/apache/solr/cli/HealthcheckTool.java +++ b/solr/core/src/java/org/apache/solr/cli/HealthcheckTool.java @@ -34,7 +34,9 @@ import org.apache.solr.client.solrj.impl.CloudSolrClient; import org.apache.solr.client.solrj.request.GenericSolrRequest; import org.apache.solr.client.solrj.request.SolrQuery; +import org.apache.solr.client.solrj.request.SystemInfoRequest; import org.apache.solr.client.solrj.response.QueryResponse; +import org.apache.solr.client.solrj.response.SystemInfoResponse; import org.apache.solr.common.cloud.ClusterState; import org.apache.solr.common.cloud.DocCollection; import org.apache.solr.common.cloud.Replica; @@ -168,15 +170,16 @@ protected void runCloudTool(CloudSolrClient cloudSolrClient, CommandLine cli) th try (var solrClient = CLIUtils.getSolrClient( r.getBaseUrl(), cli.getOptionValue(CommonCLIOptions.CREDENTIALS_OPTION))) { - NamedList systemInfo = - solrClient.request( - new GenericSolrRequest( - SolrRequest.METHOD.GET, CommonParams.SYSTEM_INFO_PATH)); - uptime = - SolrCLI.uptime((Long) systemInfo._get(List.of("jvm", "jmx", "upTimeMS"), null)); - String usedMemory = systemInfo._getStr(List.of("jvm", "memory", "used"), null); - String totalMemory = systemInfo._getStr(List.of("jvm", "memory", "total"), null); - memory = usedMemory + " of " + totalMemory; + SystemInfoResponse sysInfoResponse = (new SystemInfoRequest()).process(solrClient); +// NamedList systemInfo = +// solrClient.request( +// new GenericSolrRequest( +// SolrRequest.METHOD.GET, CommonParams.SYSTEM_INFO_PATH)); + uptime = SolrCLI.uptime(sysInfoResponse.jvm != null && sysInfoResponse.jvm.jmx != null ? sysInfoResponse.jvm.jmx.upTimeMS : -1L); + // SolrCLI.uptime((Long) systemInfo._get(List.of("jvm", "jmx", "upTimeMS"), null)); +// String usedMemory = systemInfo._getStr(List.of("jvm", "memory", "used"), null); +// String totalMemory = systemInfo._getStr(List.of("jvm", "memory", "total"), null); + memory = sysInfoResponse.jvm.memory.used + " of " + sysInfoResponse.jvm.memory.total; } // if we get here, we can trust the state diff --git a/solr/core/src/java/org/apache/solr/cli/StatusTool.java b/solr/core/src/java/org/apache/solr/cli/StatusTool.java index 2f7422c054f..28be3cd2956 100644 --- a/solr/core/src/java/org/apache/solr/cli/StatusTool.java +++ b/solr/core/src/java/org/apache/solr/cli/StatusTool.java @@ -34,6 +34,8 @@ import org.apache.solr.client.solrj.SolrRequest; import org.apache.solr.client.solrj.request.CollectionAdminRequest; import org.apache.solr.client.solrj.request.GenericSolrRequest; +import org.apache.solr.client.solrj.request.SystemInfoRequest; +import org.apache.solr.client.solrj.response.SystemInfoResponse; import org.apache.solr.common.params.CommonParams; import org.apache.solr.common.util.NamedList; import org.apache.solr.common.util.URLUtil; @@ -292,19 +294,40 @@ public Map waitToSeeSolrUp( public Map getStatus(String solrUrl, String credentials) throws Exception { try (var solrClient = CLIUtils.getSolrClient(solrUrl, credentials)) { - return getStatus(solrClient); + return reportStatus(solrClient); } } - public Map getStatus(SolrClient solrClient) throws Exception { - Map status; - - NamedList systemInfo = - solrClient.request( - new GenericSolrRequest(SolrRequest.METHOD.GET, CommonParams.SYSTEM_INFO_PATH)); - // convert raw JSON into user-friendly output - status = reportStatus(systemInfo, solrClient); - +// public Map getStatus(SolrClient solrClient) throws Exception { +// Map status; +// +// NamedList systemInfo = +// solrClient.request( +// new GenericSolrRequest(SolrRequest.METHOD.GET, CommonParams.SYSTEM_INFO_PATH)); +// // convert raw JSON into user-friendly output +// status = reportStatus(systemInfo, solrClient); +// +// return status; +// } + + public static Map reportStatus(SolrClient solrClient) + throws Exception { + Map status = new LinkedHashMap<>(); + SystemInfoResponse sysInfoResponse = (new SystemInfoRequest()).process(solrClient); + status.put("solr_home", sysInfoResponse.solrHome); + status.put("version", sysInfoResponse.lucene != null ? sysInfoResponse.lucene.solrImplVersion : null); + status.put("startTime", sysInfoResponse.jvm != null && sysInfoResponse.jvm.jmx != null ? sysInfoResponse.jvm.jmx.startTime: null); + status.put("uptime", sysInfoResponse.jvm != null && sysInfoResponse.jvm.jmx != null ? sysInfoResponse.jvm.jmx.upTimeMS: null); + + String memoryUsed = sysInfoResponse.jvm != null && sysInfoResponse.jvm.memory != null ? sysInfoResponse.jvm.memory.used : null; + String memoryTtl = sysInfoResponse.jvm != null && sysInfoResponse.jvm.memory != null ? sysInfoResponse.jvm.memory.total : null; + status.put("memory", memoryUsed + " of " + memoryTtl); + + // if this is a Solr in solrcloud mode, gather some basic cluster info + if ("solrcloud".equals(sysInfoResponse.mode)) { + status.put("cloud", getCloudStatus(solrClient, sysInfoResponse.zkHost)); + } + return status; } diff --git a/solr/core/src/java/org/apache/solr/packagemanager/RepositoryManager.java b/solr/core/src/java/org/apache/solr/packagemanager/RepositoryManager.java index b56d6441709..a8de3a08538 100644 --- a/solr/core/src/java/org/apache/solr/packagemanager/RepositoryManager.java +++ b/solr/core/src/java/org/apache/solr/packagemanager/RepositoryManager.java @@ -47,7 +47,9 @@ import org.apache.solr.client.solrj.request.GenericSolrRequest; import org.apache.solr.client.solrj.request.GenericV2SolrRequest; import org.apache.solr.client.solrj.request.RequestWriter; +import org.apache.solr.client.solrj.request.SystemInfoRequest; import org.apache.solr.client.solrj.request.beans.PackagePayload; +import org.apache.solr.client.solrj.response.SystemInfoResponse; import org.apache.solr.common.SolrException; import org.apache.solr.common.SolrException.ErrorCode; import org.apache.solr.common.cloud.SolrZkClient; @@ -142,14 +144,17 @@ public void addRepository(String repoName, String uri) throws Exception { public void addKey(byte[] key, String destinationKeyFilename) throws Exception { // get solr_home directory from info servlet - NamedList systemInfo = - solrClient.request( - new GenericSolrRequest(SolrRequest.METHOD.GET, "/solr" + SYSTEM_INFO_PATH)); - String solrHome = (String) systemInfo.get("solr_home"); +// NamedList systemInfo = +// solrClient.request( +// new GenericSolrRequest(SolrRequest.METHOD.GET, "/solr" + SYSTEM_INFO_PATH)); +// String solrHome = (String) systemInfo.get("solr_home"); + + // SystemInfoRequest has prefix /solr by default + SystemInfoResponse sysInfoResponse = (new SystemInfoRequest()).process(solrClient); // put the public key into package store's trusted key store and request a sync. String path = ClusterFileStore.KEYS_DIR + "/" + destinationKeyFilename; - PackageUtils.uploadKey(key, path, Path.of(solrHome)); + PackageUtils.uploadKey(key, path, Path.of(sysInfoResponse.solrHome)); final var syncRequest = new FileStoreApi.SyncFile(path); final var syncResponse = syncRequest.process(solrClient); final var status = syncResponse.responseHeader.status; diff --git a/solr/core/src/test/org/apache/solr/packagemanager/TestRepositoryManager.java b/solr/core/src/test/org/apache/solr/packagemanager/TestRepositoryManager.java new file mode 100644 index 00000000000..1dfde975e5a --- /dev/null +++ b/solr/core/src/test/org/apache/solr/packagemanager/TestRepositoryManager.java @@ -0,0 +1,35 @@ +package org.apache.solr.packagemanager; + +import java.nio.file.Files; + +import org.apache.solr.cli.CLITestHelper; +import org.apache.solr.cli.ToolRuntime; +import org.apache.solr.client.solrj.request.CollectionAdminRequest; +import org.apache.solr.cloud.SolrCloudTestCase; +import org.apache.solr.filestore.ClusterFileStore; +import org.junit.Assert; +import org.junit.Before; + +public class TestRepositoryManager extends SolrCloudTestCase { + + @Before + @Override + public void setUp() throws Exception { + super.setUp(); + configureCluster(1).addConfig("conf", configset("conf3")).configure(); + +// CollectionAdminRequest.createCollection("collection1", "conf", 1, 1) +// .process(cluster.getSolrClient()); +// cluster.waitForActiveCollection("collection1", 1, 1); + } + + public void testAddKey() throws Exception { + String testFileName = "TestRepositoryManager.txt"; + String expectedPath = ClusterFileStore.KEYS_DIR + "/" + testFileName; + RepositoryManager mngr = new RepositoryManager(cluster.getSolrClient(), null); + + mngr.addKey("TestRepositoryManager".getBytes(), testFileName); + Assert.assertTrue("File should exist", Files.exists(testSolrHome.resolve(expectedPath))); + } + +} diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/response/SystemInfoResponse.java b/solr/solrj/src/java/org/apache/solr/client/solrj/response/SystemInfoResponse.java index 58f0ac33cce..a3c0f420384 100644 --- a/solr/solrj/src/java/org/apache/solr/client/solrj/response/SystemInfoResponse.java +++ b/solr/solrj/src/java/org/apache/solr/client/solrj/response/SystemInfoResponse.java @@ -42,7 +42,7 @@ public class SystemInfoResponse extends SolrJerseyResponse { public String zkHost; @JsonProperty("solr_home") - public String solrHome; + public String solrHome = "?"; @JsonProperty("core_root") public String coreRoot; @@ -59,7 +59,7 @@ public class SystemInfoResponse extends SolrJerseyResponse { @JsonProperty("security") public Security security; - private Map system = new HashMap<>();; + private Map system = new HashMap<>(); @JsonAnyGetter public Map getSystem() { @@ -106,6 +106,12 @@ public class JVM extends Vendor { @JsonProperty("vm") public Vendor vm; + + @JsonProperty("jmx") + public JvmJmx jmx; + + @JsonProperty("memory") + public JvmMemory memory; } public class JvmMemory { From 24703677bbd4c5fc2c8e4fc1e8d9f2d26f9aba40 Mon Sep 17 00:00:00 2001 From: Isabelle Giguere Date: Tue, 16 Dec 2025 17:51:02 -0500 Subject: [PATCH 03/15] SOLR-17136: replace GenericSolrRequest Adjust the API model SystemInfoResponse and tie it into the SolrResponseBase. Remove commented code. --- .../client/api/model/SystemInfoResponse.java | 111 +++++++++ .../java/org/apache/solr/cli/CLIUtils.java | 17 +- .../java/org/apache/solr/cli/CreateTool.java | 16 +- .../org/apache/solr/cli/HealthcheckTool.java | 17 +- .../java/org/apache/solr/cli/StatusTool.java | 62 +---- .../packagemanager/RepositoryManager.java | 15 +- .../TestEmbeddedSolrServerAdminHandler.java | 28 +-- .../packagemanager/TestRepositoryManager.java | 35 --- .../solrj/request/SystemInfoRequest.java | 38 +++- .../solrj/response/SystemInfoResponse.java | 214 +++++++----------- 10 files changed, 255 insertions(+), 298 deletions(-) create mode 100644 solr/api/src/java/org/apache/solr/client/api/model/SystemInfoResponse.java delete mode 100644 solr/core/src/test/org/apache/solr/packagemanager/TestRepositoryManager.java diff --git a/solr/api/src/java/org/apache/solr/client/api/model/SystemInfoResponse.java b/solr/api/src/java/org/apache/solr/client/api/model/SystemInfoResponse.java new file mode 100644 index 00000000000..91fd8dc8302 --- /dev/null +++ b/solr/api/src/java/org/apache/solr/client/api/model/SystemInfoResponse.java @@ -0,0 +1,111 @@ +/* + * 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 com.fasterxml.jackson.annotation.OptBoolean; +import java.util.Date; +import java.util.List; +import java.util.Map; + +public class SystemInfoResponse extends SolrJerseyResponse { + + @JsonProperty public String mode; + @JsonProperty public String zkHost; + + @JsonProperty("solr_home") + public String solrHome; + + @JsonProperty("core_root") + public String coreRoot; + + @JsonProperty public String node; + @JsonProperty public Lucene lucene; + @JsonProperty public JVM jvm; + @JsonProperty public Security security; + @JsonProperty public Map system; + + /** /admin/info/system/security */ + public static class Security { + @JsonProperty public boolean tls; + @JsonProperty public String authenticationPlugin; + @JsonProperty public String authorizationPlugin; + @JsonProperty public String username; + @JsonProperty public List roles; + @JsonProperty public List permissions; + } + + /** /admin/info/system/lucene */ + public static class Lucene { + @JsonProperty("solr-spec-version") + public String solrSpecVersion; + + @JsonProperty("solr-impl-version") + public String solrImplVersion; + + @JsonProperty("lucene-spec-version") + public String luceneSpecVersion; + + @JsonProperty("lucene-impl-version") + public String luceneImplVersion; + } + + /** /admin/info/system/jvm */ + public static class JVM extends Vendor { + @JsonProperty public int processors; + @JsonProperty public Vendor jre; + @JsonProperty public Vendor spec; + @JsonProperty public Vendor vm; + @JsonProperty public JvmJmx jmx; + @JsonProperty public JvmMemory memory; + } + + public static class JvmMemory { + @JsonProperty public String free; + @JsonProperty public String total; + @JsonProperty public String max; + @JsonProperty public String used; + @JsonProperty public JvmMemoryRaw raw; + } + + public static class JvmMemoryRaw { + @JsonProperty public long free; + @JsonProperty public long total; + @JsonProperty public long max; + @JsonProperty public long used; + + @JsonProperty("used%") + public double usedPercent; + } + + public static class Vendor { + @JsonProperty(isRequired = OptBoolean.FALSE) + public String name; + + @JsonProperty(isRequired = OptBoolean.FALSE) + public String vendor; + + @JsonProperty public String version; + } + + public static class JvmJmx { + @JsonProperty public String classpath; + @JsonProperty public Date startTime; + @JsonProperty public long upTimeMS; + @JsonProperty public List commandLineArgs; + } +} 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 7ea03579bb8..36baeb8357f 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.SYSTEM_INFO_PATH; import java.io.IOException; import java.net.SocketException; @@ -38,20 +37,17 @@ import org.apache.commons.cli.Option; import org.apache.commons.exec.OS; 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.CloudSolrClient; import org.apache.solr.client.solrj.impl.SolrZkClientTimeout; import org.apache.solr.client.solrj.jetty.HttpJettySolrClient; import org.apache.solr.client.solrj.request.CollectionAdminRequest; import org.apache.solr.client.solrj.request.CoresApi; -import org.apache.solr.client.solrj.request.GenericSolrRequest; import org.apache.solr.client.solrj.request.SystemInfoRequest; import org.apache.solr.client.solrj.response.SystemInfoResponse; import org.apache.solr.common.SolrException; import org.apache.solr.common.cloud.SolrZkClient; import org.apache.solr.common.cloud.ZkStateReader; -import org.apache.solr.common.params.CommonParams; import org.apache.solr.common.util.EnvUtils; import org.apache.solr.common.util.NamedList; @@ -250,12 +246,7 @@ public static String getZkHost(CommandLine cli) throws Exception { } try (SolrClient solrClient = getSolrClient(cli)) { -// // hit Solr to get system info -// NamedList systemInfo = -// solrClient.request( -// new GenericSolrRequest(SolrRequest.METHOD.GET, CommonParams.SYSTEM_INFO_PATH)); - - // convert raw JSON into user-friendly output + // hit Solr to get system info Map status = StatusTool.reportStatus(solrClient); @SuppressWarnings("unchecked") Map cloud = (Map) status.get("cloud"); @@ -359,10 +350,8 @@ public static boolean safeCheckCoreExists(String solrUrl, String coreName, Strin } public static boolean isCloudMode(SolrClient solrClient) throws SolrServerException, IOException { -// NamedList systemInfo = -// solrClient.request(new GenericSolrRequest(SolrRequest.METHOD.GET, SYSTEM_INFO_PATH)); - SystemInfoResponse sysInfoResponse = (new SystemInfoRequest()).process(solrClient); - return "solrcloud".equals(sysInfoResponse.mode); + SystemInfoResponse sysResponse = new SystemInfoRequest().process(solrClient); + return "solrcloud".equals(sysResponse.getMode()); } public static Path getConfigSetsDir(Path solrInstallDir) { diff --git a/solr/core/src/java/org/apache/solr/cli/CreateTool.java b/solr/core/src/java/org/apache/solr/cli/CreateTool.java index 05bff9b0949..e50fb796e9c 100644 --- a/solr/core/src/java/org/apache/solr/cli/CreateTool.java +++ b/solr/core/src/java/org/apache/solr/cli/CreateTool.java @@ -29,20 +29,17 @@ import org.apache.commons.io.file.PathUtils; import org.apache.solr.cli.CommonCLIOptions.DefaultValues; 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.CloudSolrClient; import org.apache.solr.client.solrj.jetty.HttpJettySolrClient; import org.apache.solr.client.solrj.request.CollectionAdminRequest; import org.apache.solr.client.solrj.request.CoreAdminRequest; -import org.apache.solr.client.solrj.request.GenericSolrRequest; import org.apache.solr.client.solrj.request.SystemInfoRequest; import org.apache.solr.client.solrj.response.CoreAdminResponse; import org.apache.solr.client.solrj.response.SystemInfoResponse; import org.apache.solr.client.solrj.response.json.JsonMapResponseParser; import org.apache.solr.cloud.ZkConfigSetService; import org.apache.solr.common.cloud.ZkStateReader; -import org.apache.solr.common.params.CommonParams; import org.apache.solr.common.util.NamedList; import org.apache.solr.core.ConfigSetService; import org.noggit.CharArr; @@ -159,17 +156,10 @@ protected void createCore(CommandLine cli, SolrClient solrClient) throws Excepti } printDefaultConfigsetWarningIfNecessary(cli); - //String coreRootDirectory; // usually same as solr home, but not always - -// NamedList systemInfo = -// solrClient.request( -// new GenericSolrRequest(SolrRequest.METHOD.GET, CommonParams.SYSTEM_INFO_PATH)); - SystemInfoResponse sysInfoResponse = (new SystemInfoRequest()).process(solrClient); - - // convert raw JSON into user-friendly output - //String coreRootDirectory = (String) systemInfo.get("core_root"); + SystemInfoResponse sysResponse = (new SystemInfoRequest()).process(solrClient); // usually same as solr home, but not always - String coreRootDirectory = sysInfoResponse.coreRoot != null ? sysInfoResponse.coreRoot : sysInfoResponse.solrHome; + String coreRootDirectory = + sysResponse.getCoreRoot() != null ? sysResponse.getCoreRoot() : sysResponse.getSolrHome(); if (CLIUtils.safeCheckCoreExists( solrUrl, coreName, cli.getOptionValue(CommonCLIOptions.CREDENTIALS_OPTION))) { diff --git a/solr/core/src/java/org/apache/solr/cli/HealthcheckTool.java b/solr/core/src/java/org/apache/solr/cli/HealthcheckTool.java index 216aad96369..0cf385cf22f 100644 --- a/solr/core/src/java/org/apache/solr/cli/HealthcheckTool.java +++ b/solr/core/src/java/org/apache/solr/cli/HealthcheckTool.java @@ -30,9 +30,7 @@ import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.Option; import org.apache.commons.cli.Options; -import org.apache.solr.client.solrj.SolrRequest; import org.apache.solr.client.solrj.impl.CloudSolrClient; -import org.apache.solr.client.solrj.request.GenericSolrRequest; import org.apache.solr.client.solrj.request.SolrQuery; import org.apache.solr.client.solrj.request.SystemInfoRequest; import org.apache.solr.client.solrj.response.QueryResponse; @@ -42,8 +40,6 @@ import org.apache.solr.common.cloud.Replica; import org.apache.solr.common.cloud.Slice; import org.apache.solr.common.cloud.ZkStateReader; -import org.apache.solr.common.params.CommonParams; -import org.apache.solr.common.util.NamedList; import org.noggit.CharArr; import org.noggit.JSONWriter; import org.slf4j.Logger; @@ -170,16 +166,9 @@ protected void runCloudTool(CloudSolrClient cloudSolrClient, CommandLine cli) th try (var solrClient = CLIUtils.getSolrClient( r.getBaseUrl(), cli.getOptionValue(CommonCLIOptions.CREDENTIALS_OPTION))) { - SystemInfoResponse sysInfoResponse = (new SystemInfoRequest()).process(solrClient); -// NamedList systemInfo = -// solrClient.request( -// new GenericSolrRequest( -// SolrRequest.METHOD.GET, CommonParams.SYSTEM_INFO_PATH)); - uptime = SolrCLI.uptime(sysInfoResponse.jvm != null && sysInfoResponse.jvm.jmx != null ? sysInfoResponse.jvm.jmx.upTimeMS : -1L); - // SolrCLI.uptime((Long) systemInfo._get(List.of("jvm", "jmx", "upTimeMS"), null)); -// String usedMemory = systemInfo._getStr(List.of("jvm", "memory", "used"), null); -// String totalMemory = systemInfo._getStr(List.of("jvm", "memory", "total"), null); - memory = sysInfoResponse.jvm.memory.used + " of " + sysInfoResponse.jvm.memory.total; + SystemInfoResponse sysResponse = (new SystemInfoRequest()).process(solrClient); + uptime = SolrCLI.uptime(sysResponse.getJVMUpTime()); + memory = sysResponse.getJVMMemoryUsed() + " of " + sysResponse.getJVMMemoryTtl(); } // if we get here, we can trust the state diff --git a/solr/core/src/java/org/apache/solr/cli/StatusTool.java b/solr/core/src/java/org/apache/solr/cli/StatusTool.java index 28be3cd2956..cbe17f66d43 100644 --- a/solr/core/src/java/org/apache/solr/cli/StatusTool.java +++ b/solr/core/src/java/org/apache/solr/cli/StatusTool.java @@ -31,12 +31,9 @@ import org.apache.commons.cli.Options; import org.apache.solr.cli.SolrProcessManager.SolrProcess; import org.apache.solr.client.solrj.SolrClient; -import org.apache.solr.client.solrj.SolrRequest; import org.apache.solr.client.solrj.request.CollectionAdminRequest; -import org.apache.solr.client.solrj.request.GenericSolrRequest; import org.apache.solr.client.solrj.request.SystemInfoRequest; import org.apache.solr.client.solrj.response.SystemInfoResponse; -import org.apache.solr.common.params.CommonParams; import org.apache.solr.common.util.NamedList; import org.apache.solr.common.util.URLUtil; import org.noggit.CharArr; @@ -294,63 +291,26 @@ public Map waitToSeeSolrUp( public Map getStatus(String solrUrl, String credentials) throws Exception { try (var solrClient = CLIUtils.getSolrClient(solrUrl, credentials)) { - return reportStatus(solrClient); + Map status = reportStatus(solrClient); + return status; } } -// public Map getStatus(SolrClient solrClient) throws Exception { -// Map status; -// -// NamedList systemInfo = -// solrClient.request( -// new GenericSolrRequest(SolrRequest.METHOD.GET, CommonParams.SYSTEM_INFO_PATH)); -// // convert raw JSON into user-friendly output -// status = reportStatus(systemInfo, solrClient); -// -// return status; -// } - - public static Map reportStatus(SolrClient solrClient) - throws Exception { + public static Map reportStatus(SolrClient solrClient) throws Exception { Map status = new LinkedHashMap<>(); - SystemInfoResponse sysInfoResponse = (new SystemInfoRequest()).process(solrClient); - status.put("solr_home", sysInfoResponse.solrHome); - status.put("version", sysInfoResponse.lucene != null ? sysInfoResponse.lucene.solrImplVersion : null); - status.put("startTime", sysInfoResponse.jvm != null && sysInfoResponse.jvm.jmx != null ? sysInfoResponse.jvm.jmx.startTime: null); - status.put("uptime", sysInfoResponse.jvm != null && sysInfoResponse.jvm.jmx != null ? sysInfoResponse.jvm.jmx.upTimeMS: null); - - String memoryUsed = sysInfoResponse.jvm != null && sysInfoResponse.jvm.memory != null ? sysInfoResponse.jvm.memory.used : null; - String memoryTtl = sysInfoResponse.jvm != null && sysInfoResponse.jvm.memory != null ? sysInfoResponse.jvm.memory.total : null; - status.put("memory", memoryUsed + " of " + memoryTtl); - - // if this is a Solr in solrcloud mode, gather some basic cluster info - if ("solrcloud".equals(sysInfoResponse.mode)) { - status.put("cloud", getCloudStatus(solrClient, sysInfoResponse.zkHost)); - } - - return status; - } + SystemInfoResponse sysResponse = (new SystemInfoRequest()).process(solrClient); + status.put("solr_home", sysResponse.getSolrHome() != null ? sysResponse.getSolrHome() : "?"); + status.put("version", sysResponse.getSolrImplVersion()); - public static Map reportStatus(NamedList info, SolrClient solrClient) - throws Exception { - Map status = new LinkedHashMap<>(); + status.put("startTime", sysResponse.getJVMStartTime()); + status.put("uptime", sysResponse.getJVMUpTime()); - String solrHome = (String) info.get("solr_home"); - status.put("solr_home", solrHome != null ? solrHome : "?"); - status.put("version", info._getStr(List.of("lucene", "solr-impl-version"), null)); - status.put("startTime", info._getStr(List.of("jvm", "jmx", "startTime"), null)); - status.put("uptime", SolrCLI.uptime((Long) info._get(List.of("jvm", "jmx", "upTimeMS"), null))); - - String usedMemory = info._getStr(List.of("jvm", "memory", "used"), null); - String totalMemory = info._getStr(List.of("jvm", "memory", "total"), null); - status.put("memory", usedMemory + " of " + totalMemory); + status.put("memory", sysResponse.getJVMMemoryUsed() + " of " + sysResponse.getJVMMemoryTtl()); // if this is a Solr in solrcloud mode, gather some basic cluster info - if ("solrcloud".equals(info.get("mode"))) { - String zkHost = (String) info.get("zkHost"); - status.put("cloud", getCloudStatus(solrClient, zkHost)); + if ("solrcloud".equals(sysResponse.getMode())) { + status.put("cloud", getCloudStatus(solrClient, sysResponse.getZkHost())); } - return status; } diff --git a/solr/core/src/java/org/apache/solr/packagemanager/RepositoryManager.java b/solr/core/src/java/org/apache/solr/packagemanager/RepositoryManager.java index a8de3a08538..301f051af90 100644 --- a/solr/core/src/java/org/apache/solr/packagemanager/RepositoryManager.java +++ b/solr/core/src/java/org/apache/solr/packagemanager/RepositoryManager.java @@ -18,7 +18,6 @@ package org.apache.solr.packagemanager; import static org.apache.solr.cli.SolrCLI.printGreen; -import static org.apache.solr.common.params.CommonParams.SYSTEM_INFO_PATH; import static org.apache.solr.packagemanager.PackageUtils.getMapper; import java.io.IOException; @@ -144,17 +143,15 @@ public void addRepository(String repoName, String uri) throws Exception { public void addKey(byte[] key, String destinationKeyFilename) throws Exception { // get solr_home directory from info servlet -// NamedList systemInfo = -// solrClient.request( -// new GenericSolrRequest(SolrRequest.METHOD.GET, "/solr" + SYSTEM_INFO_PATH)); -// String solrHome = (String) systemInfo.get("solr_home"); - - // SystemInfoRequest has prefix /solr by default - SystemInfoResponse sysInfoResponse = (new SystemInfoRequest()).process(solrClient); + // This method is only called from PackageTool ("add-repo", or "add-key"), where the Solr URL is + // normalized to remove the /solr path part + // So might as well ping the V2 API "/node/system" instead. + // Otherwise, this SystemInfoRequest ctr would need to set the full /solr/admin/info/system path + SystemInfoResponse sysResponse = new SystemInfoRequest("/node/system").process(solrClient); // put the public key into package store's trusted key store and request a sync. String path = ClusterFileStore.KEYS_DIR + "/" + destinationKeyFilename; - PackageUtils.uploadKey(key, path, Path.of(sysInfoResponse.solrHome)); + PackageUtils.uploadKey(key, path, Path.of(sysResponse.getSolrHome())); final var syncRequest = new FileStoreApi.SyncFile(path); final var syncResponse = syncRequest.process(solrClient); final var status = syncResponse.responseHeader.status; diff --git a/solr/core/src/test/org/apache/solr/client/solrj/embedded/TestEmbeddedSolrServerAdminHandler.java b/solr/core/src/test/org/apache/solr/client/solrj/embedded/TestEmbeddedSolrServerAdminHandler.java index 7118520fa12..33be625e1ad 100644 --- a/solr/core/src/test/org/apache/solr/client/solrj/embedded/TestEmbeddedSolrServerAdminHandler.java +++ b/solr/core/src/test/org/apache/solr/client/solrj/embedded/TestEmbeddedSolrServerAdminHandler.java @@ -18,12 +18,9 @@ import java.io.IOException; import org.apache.solr.SolrTestCaseJ4; -import org.apache.solr.client.solrj.SolrRequest; import org.apache.solr.client.solrj.SolrServerException; -import org.apache.solr.client.solrj.response.QueryResponse; -import org.apache.solr.common.params.ModifiableSolrParams; -import org.apache.solr.common.params.SolrParams; -import org.apache.solr.common.util.NamedList; +import org.apache.solr.client.solrj.request.SystemInfoRequest; +import org.apache.solr.client.solrj.response.SystemInfoResponse; import org.apache.solr.core.NodeConfig; import org.junit.Test; @@ -39,25 +36,8 @@ public void testPathIsAddedToContext() throws IOException, SolrServerException { try (final EmbeddedSolrServer server = new EmbeddedSolrServer(config, "collection1")) { final SystemInfoRequest info = new SystemInfoRequest(); - final NamedList response = server.request(info); - assertTrue(response.size() > 0); - } - } - - private static class SystemInfoRequest extends SolrRequest { - - public SystemInfoRequest() { - super(METHOD.GET, "/admin/info/system", SolrRequest.SolrRequestType.ADMIN); - } - - @Override - public SolrParams getParams() { - return new ModifiableSolrParams(); - } - - @Override - protected QueryResponse createResponse(final NamedList namedList) { - return new QueryResponse(); + final SystemInfoResponse response = info.process(server); + assertTrue(response.getResponse().size() > 0); } } } diff --git a/solr/core/src/test/org/apache/solr/packagemanager/TestRepositoryManager.java b/solr/core/src/test/org/apache/solr/packagemanager/TestRepositoryManager.java deleted file mode 100644 index 1dfde975e5a..00000000000 --- a/solr/core/src/test/org/apache/solr/packagemanager/TestRepositoryManager.java +++ /dev/null @@ -1,35 +0,0 @@ -package org.apache.solr.packagemanager; - -import java.nio.file.Files; - -import org.apache.solr.cli.CLITestHelper; -import org.apache.solr.cli.ToolRuntime; -import org.apache.solr.client.solrj.request.CollectionAdminRequest; -import org.apache.solr.cloud.SolrCloudTestCase; -import org.apache.solr.filestore.ClusterFileStore; -import org.junit.Assert; -import org.junit.Before; - -public class TestRepositoryManager extends SolrCloudTestCase { - - @Before - @Override - public void setUp() throws Exception { - super.setUp(); - configureCluster(1).addConfig("conf", configset("conf3")).configure(); - -// CollectionAdminRequest.createCollection("collection1", "conf", 1, 1) -// .process(cluster.getSolrClient()); -// cluster.waitForActiveCollection("collection1", 1, 1); - } - - public void testAddKey() throws Exception { - String testFileName = "TestRepositoryManager.txt"; - String expectedPath = ClusterFileStore.KEYS_DIR + "/" + testFileName; - RepositoryManager mngr = new RepositoryManager(cluster.getSolrClient(), null); - - mngr.addKey("TestRepositoryManager".getBytes(), testFileName); - Assert.assertTrue("File should exist", Files.exists(testSolrHome.resolve(expectedPath))); - } - -} diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/request/SystemInfoRequest.java b/solr/solrj/src/java/org/apache/solr/client/solrj/request/SystemInfoRequest.java index 617b15637c3..51748625781 100644 --- a/solr/solrj/src/java/org/apache/solr/client/solrj/request/SystemInfoRequest.java +++ b/solr/solrj/src/java/org/apache/solr/client/solrj/request/SystemInfoRequest.java @@ -1,21 +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.solrj.request; - import org.apache.solr.client.solrj.SolrRequest; import org.apache.solr.client.solrj.response.SystemInfoResponse; import org.apache.solr.common.params.CommonParams; import org.apache.solr.common.params.SolrParams; import org.apache.solr.common.util.NamedList; -/** - * Class to get a "/admin/info/system" response. - */ +/** Class to get a system info response. */ public class SystemInfoRequest extends SolrRequest { private static final long serialVersionUID = 1L; - + public SystemInfoRequest() { - super(METHOD.GET, CommonParams.SYSTEM_INFO_PATH, SolrRequestType.ADMIN); + this(CommonParams.SYSTEM_INFO_PATH); } public SystemInfoRequest(String path) { @@ -24,12 +37,21 @@ public SystemInfoRequest(String path) { @Override public SolrParams getParams() { - return null; // no params to return + return SolrParams.of(); // no params to return, but avoid NPE } @Override protected SystemInfoResponse createResponse(NamedList namedList) { - return (SystemInfoResponse) namedList.get("response"); + return new SystemInfoResponse(namedList); } + @Override + public ApiVersion getApiVersion() { + if (CommonParams.SYSTEM_INFO_PATH.equals(getPath())) { + // (/solr) /admin/info/system + return ApiVersion.V1; + } + // Ref. org.apache.solr.handler.admin.api.NodeSystemInfoAPI : /node/system + return ApiVersion.V2; + } } diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/response/SystemInfoResponse.java b/solr/solrj/src/java/org/apache/solr/client/solrj/response/SystemInfoResponse.java index a3c0f420384..41c389de6ca 100644 --- a/solr/solrj/src/java/org/apache/solr/client/solrj/response/SystemInfoResponse.java +++ b/solr/solrj/src/java/org/apache/solr/client/solrj/response/SystemInfoResponse.java @@ -16,151 +16,105 @@ */ package org.apache.solr.client.solrj.response; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.apache.solr.client.api.model.SolrJerseyResponse; +import java.lang.invoke.MethodHandles; +import java.util.Date; +import org.apache.solr.client.solrj.request.json.JacksonContentWriter; import org.apache.solr.common.util.NamedList; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -import com.fasterxml.jackson.annotation.JsonAnyGetter; -import com.fasterxml.jackson.annotation.JsonAnySetter; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.OptBoolean; +/** This class holds the response from V1 "/admin/info/system" or V2 "/node/system" */ +public class SystemInfoResponse extends SolrResponseBase { -/** - * This class represents a response from "/admin/info/system" - */ -public class SystemInfoResponse extends SolrJerseyResponse { + private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); private static final long serialVersionUID = 1L; - @JsonProperty("mode") - public String mode; - - @JsonProperty("zkHost") - public String zkHost; - - @JsonProperty("solr_home") - public String solrHome = "?"; - - @JsonProperty("core_root") - public String coreRoot; - - @JsonProperty("node") - public String node; - - @JsonProperty("lucene") - public Lucene lucene; - - @JsonProperty("jvm") - public JVM jvm; - - @JsonProperty("security") - public Security security; - - private Map system = new HashMap<>(); - - @JsonAnyGetter - public Map getSystem() { - return system; + private org.apache.solr.client.api.model.SystemInfoResponse fullResponse; + + public SystemInfoResponse(NamedList namedList) { + setResponse(namedList); + init(); + } + + private void init() { + try { + fullResponse = + JacksonContentWriter.DEFAULT_MAPPER.convertValue( + getResponse(), org.apache.solr.client.api.model.SystemInfoResponse.class); + } catch (Throwable t) { + log.error("Cannot convert NamedList response to API model SystemInfoResponse", t); + } } - - @JsonAnySetter - public void setSystemProperty(String key, String value) { - this.system.put(key, value); + + /* SolrRequest.process only needs to set 'elapsedTime' */ + @Override + public void setResponse(NamedList response) { + if (getResponse() == null) super.setResponse(response); } - - /** /admin/info/system/security */ - public class Security { - @JsonProperty("tls") - public boolean tls; + + public String getMode() { + return getFullResponse().mode; } - /** /admin/info/system/lucene */ - public class Lucene { - @JsonProperty("solr-spec-version") - public String solrSpecVersion; - - @JsonProperty("solr-impl-version") - public String solrImplVersion; - - @JsonProperty("lucene-spec-version") - public String luceneSpecVersion; - - @JsonProperty("lucene-impl-version") - public String luceneImplVersion; - + public String getZkHost() { + return getFullResponse().zkHost; } - - /** /admin/info/system/jvm */ - public class JVM extends Vendor { - @JsonProperty("processors") - public int processors; - - @JsonProperty("jre") - public Vendor jre; - - @JsonProperty("spec") - public Vendor spec; - - @JsonProperty("vm") - public Vendor vm; - - @JsonProperty("jmx") - public JvmJmx jmx; - - @JsonProperty("memory") - public JvmMemory memory; + + public String getSolrHome() { + return getFullResponse().solrHome; } - - public class JvmMemory { - - @JsonProperty("free") - public String free; - @JsonProperty("total") - public String total; - @JsonProperty("max") - public String max; - @JsonProperty("used") - public String used; - - @JsonProperty("raw") - public JvmMemoryRaw raw; + + public String getCoreRoot() { + return getFullResponse().coreRoot; } - public class JvmMemoryRaw { - @JsonProperty("free") - public long free; - @JsonProperty("total") - public long total; - @JsonProperty("max") - public long max; - @JsonProperty("used") - public long used; - @JsonProperty("used%") - public double usedPercent; + public String getNode() { + return getFullResponse().node; } - - - public class Vendor { - @JsonProperty(value="name", isRequired=OptBoolean.FALSE) - public String name; - @JsonProperty(value="vendor", isRequired=OptBoolean.FALSE) - public String vendor; - @JsonProperty(value="version") - public String version; + + public org.apache.solr.client.api.model.SystemInfoResponse getFullResponse() { + return fullResponse; } - - public class JvmJmx { - @JsonProperty(value="classpath") - public String classpath; - @JsonProperty(value="startTime") - public String startTime; - @JsonProperty(value="upTimeMS") - public long upTimeMS; - - @JsonProperty(value="commandLineArgs") - public List commandLineArgs; + + // ************************* + // Shortcuts for StatusTool.reportStatus(SolrClient) + // *********** + public String getSolrImplVersion() { + return getFullResponse() != null && getFullResponse().lucene != null + ? getFullResponse().lucene.solrImplVersion + : null; + } + + public Date getJVMStartTime() { + return getFullResponse() != null + && getFullResponse().jvm != null + && getFullResponse().jvm.jmx != null + ? getFullResponse().jvm.jmx.startTime + : null; + } + + public Long getJVMUpTime() { + return getFullResponse() != null + && getFullResponse().jvm != null + && getFullResponse().jvm.jmx != null + ? getFullResponse().jvm.jmx.upTimeMS + : null; + } + + public String getJVMMemoryUsed() { + return getFullResponse() != null + && getFullResponse().jvm != null + && getFullResponse().jvm.memory != null + ? getFullResponse().jvm.memory.used + : null; + } + + public String getJVMMemoryTtl() { + return getFullResponse() != null + && getFullResponse().jvm != null + && getFullResponse().jvm.memory != null + ? getFullResponse().jvm.memory.total + : null; } } From eec06435dae4a41eeb0c92f74b52f6c023ed605b Mon Sep 17 00:00:00 2001 From: Isabelle Giguere Date: Tue, 16 Dec 2025 18:30:19 -0500 Subject: [PATCH 04/15] SOLR-17136: add changelog file --- .../unreleased/SOLR-17136-replace-GenericSolrRequest.yml | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 changelog/unreleased/SOLR-17136-replace-GenericSolrRequest.yml diff --git a/changelog/unreleased/SOLR-17136-replace-GenericSolrRequest.yml b/changelog/unreleased/SOLR-17136-replace-GenericSolrRequest.yml new file mode 100644 index 00000000000..13912dc89eb --- /dev/null +++ b/changelog/unreleased/SOLR-17136-replace-GenericSolrRequest.yml @@ -0,0 +1,7 @@ +title: Replace most uses of GenericSolrRequest in Solr code +type: changed +authors: +- name: Isabelle Giguère +links: +- name: SOLR-17136 + url: https://issues.apache.org/jira/browse/SOLR-17136 \ No newline at end of file From e21943583d08362a093c37d6b978a5d23509381c Mon Sep 17 00:00:00 2001 From: Isabelle Giguere Date: Wed, 17 Dec 2025 16:45:07 -0500 Subject: [PATCH 05/15] SOLR-17136: more replacements of GenericSolrRequest add MetricsRequest, more replacements of GenericSolrRequest, use the constants for "/admin/info/system" and "/admin/metrics" --- .../cloud/api/collections/SplitShardCmd.java | 8 +-- .../solr/cloud/BasicDistributedZkTest.java | 10 +-- .../solr/cloud/TestBaseStatsCacheCloud.java | 11 +-- .../handler/admin/AdminHandlersProxyTest.java | 33 +++------ .../handler/admin/MetricsHandlerTest.java | 16 ++--- .../TestPrometheusResponseWriter.java | 21 ++---- .../TestPrometheusResponseWriterCloud.java | 12 +--- .../solr/security/MultiAuthPluginTest.java | 9 +-- .../jwt/JWTAuthPluginIntegrationTest.java | 11 +-- .../opentelemetry/TestDistributedTracing.java | 6 +- .../opentelemetry/TestMetricExemplars.java | 10 +-- .../solrj/io/sql/DatabaseMetaDataImpl.java | 14 ++-- .../client/solrj/impl/NodeValueFetcher.java | 6 +- .../impl/SolrClientNodeStateProvider.java | 5 +- .../client/solrj/request/MetricsRequest.java | 69 +++++++++++++++++++ .../solrj/request/SystemInfoRequest.java | 40 ++++++++++- .../solrj/response/SystemInfoResponse.java | 10 ++- .../solr/client/solrj/SolrExampleTests.java | 2 +- .../solr/util/SolrJMetricTestUtils.java | 24 ++----- 19 files changed, 178 insertions(+), 139 deletions(-) create mode 100644 solr/solrj/src/java/org/apache/solr/client/solrj/request/MetricsRequest.java diff --git a/solr/core/src/java/org/apache/solr/cloud/api/collections/SplitShardCmd.java b/solr/core/src/java/org/apache/solr/cloud/api/collections/SplitShardCmd.java index 8548a0bc17a..c799ec98969 100644 --- a/solr/core/src/java/org/apache/solr/cloud/api/collections/SplitShardCmd.java +++ b/solr/core/src/java/org/apache/solr/cloud/api/collections/SplitShardCmd.java @@ -39,13 +39,12 @@ import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; -import org.apache.solr.client.solrj.SolrRequest; import org.apache.solr.client.solrj.SolrResponse; import org.apache.solr.client.solrj.cloud.DistribStateManager; import org.apache.solr.client.solrj.cloud.SolrCloudManager; import org.apache.solr.client.solrj.cloud.VersionedData; import org.apache.solr.client.solrj.request.CoreAdminRequest; -import org.apache.solr.client.solrj.request.GenericSolrRequest; +import org.apache.solr.client.solrj.request.MetricsRequest; import org.apache.solr.cloud.DistributedClusterStateUpdater; import org.apache.solr.cloud.Overseer; import org.apache.solr.cloud.api.collections.CollectionHandlingUtils.ShardRequestTracker; @@ -869,10 +868,7 @@ public static void checkDiskSpace( new ModifiableSolrParams() .add("key", indexSizeMetricName) .add("key", freeDiskSpaceMetricName); - SolrResponse rsp = - new GenericSolrRequest( - SolrRequest.METHOD.GET, "/admin/metrics", SolrRequest.SolrRequestType.ADMIN, params) - .process(cloudManager.getSolrClient()); + SolrResponse rsp = new MetricsRequest(params).process(cloudManager.getSolrClient()); Number size = (Number) rsp.getResponse()._get(List.of("metrics", indexSizeMetricName), null); if (size == null) { diff --git a/solr/core/src/test/org/apache/solr/cloud/BasicDistributedZkTest.java b/solr/core/src/test/org/apache/solr/cloud/BasicDistributedZkTest.java index f9779cfb551..d87207217df 100644 --- a/solr/core/src/test/org/apache/solr/cloud/BasicDistributedZkTest.java +++ b/solr/core/src/test/org/apache/solr/cloud/BasicDistributedZkTest.java @@ -43,14 +43,13 @@ import org.apache.solr.JSONTestUtil; import org.apache.solr.SolrTestCaseJ4.SuppressSSL; 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.apache.HttpSolrClient; import org.apache.solr.client.solrj.request.AbstractUpdateRequest; import org.apache.solr.client.solrj.request.CollectionAdminRequest; import org.apache.solr.client.solrj.request.CoreAdminRequest.Create; import org.apache.solr.client.solrj.request.CoreAdminRequest.Unload; -import org.apache.solr.client.solrj.request.GenericSolrRequest; +import org.apache.solr.client.solrj.request.MetricsRequest; import org.apache.solr.client.solrj.request.QueryRequest; import org.apache.solr.client.solrj.request.SolrQuery; import org.apache.solr.client.solrj.request.StreamingUpdateRequest; @@ -1286,12 +1285,7 @@ private Long getNumCommits(HttpSolrClient sourceClient) throws SolrServerExcepti .withConnectionTimeout(15000, TimeUnit.MILLISECONDS) .withSocketTimeout(60000, TimeUnit.MILLISECONDS) .build()) { - var req = - new GenericSolrRequest( - SolrRequest.METHOD.GET, - "/admin/metrics", - SolrRequest.SolrRequestType.ADMIN, - SolrParams.of("wt", "prometheus")); + var req = new MetricsRequest(SolrParams.of("wt", "prometheus")); req.setResponseParser(new InputStreamResponseParser("prometheus")); NamedList resp = client.request(req); diff --git a/solr/core/src/test/org/apache/solr/cloud/TestBaseStatsCacheCloud.java b/solr/core/src/test/org/apache/solr/cloud/TestBaseStatsCacheCloud.java index 11cff8c431c..5fc8bf904a8 100644 --- a/solr/core/src/test/org/apache/solr/cloud/TestBaseStatsCacheCloud.java +++ b/solr/core/src/test/org/apache/solr/cloud/TestBaseStatsCacheCloud.java @@ -22,12 +22,10 @@ import java.util.Map; import java.util.function.Function; import org.apache.solr.client.solrj.SolrClient; -import org.apache.solr.client.solrj.SolrRequest.METHOD; -import org.apache.solr.client.solrj.SolrRequest.SolrRequestType; import org.apache.solr.client.solrj.embedded.EmbeddedSolrServer; import org.apache.solr.client.solrj.impl.CloudSolrClient; import org.apache.solr.client.solrj.request.CollectionAdminRequest; -import org.apache.solr.client.solrj.request.GenericSolrRequest; +import org.apache.solr.client.solrj.request.MetricsRequest; import org.apache.solr.client.solrj.request.UpdateRequest; import org.apache.solr.client.solrj.response.InputStreamResponseParser; import org.apache.solr.client.solrj.response.QueryResponse; @@ -140,12 +138,7 @@ public void testBasicStats() throws Exception { StatsCache.StatsCacheMetrics statsCacheMetrics = new StatsCache.StatsCacheMetrics(); for (JettySolrRunner jettySolrRunner : cluster.getJettySolrRunners()) { try (SolrClient client = getHttpSolrClient(jettySolrRunner.getBaseUrl().toString())) { - var req = - new GenericSolrRequest( - METHOD.GET, - "/admin/metrics", - SolrRequestType.ADMIN, - SolrParams.of("wt", "prometheus")); + var req = new MetricsRequest(SolrParams.of("wt", "prometheus")); req.setResponseParser(new InputStreamResponseParser("prometheus")); NamedList resp = client.request(req); diff --git a/solr/core/src/test/org/apache/solr/handler/admin/AdminHandlersProxyTest.java b/solr/core/src/test/org/apache/solr/handler/admin/AdminHandlersProxyTest.java index ef39b046a6f..56431b254cf 100644 --- a/solr/core/src/test/org/apache/solr/handler/admin/AdminHandlersProxyTest.java +++ b/solr/core/src/test/org/apache/solr/handler/admin/AdminHandlersProxyTest.java @@ -21,12 +21,11 @@ import java.util.Collections; import java.util.Set; import org.apache.http.client.HttpClient; -import org.apache.solr.client.solrj.SolrRequest; import org.apache.solr.client.solrj.SolrServerException; import org.apache.solr.client.solrj.apache.CloudLegacySolrClient; import org.apache.solr.client.solrj.impl.CloudSolrClient; -import org.apache.solr.client.solrj.request.GenericSolrRequest; -import org.apache.solr.client.solrj.response.SimpleSolrResponse; +import org.apache.solr.client.solrj.request.SystemInfoRequest; +import org.apache.solr.client.solrj.response.SystemInfoResponse; import org.apache.solr.cloud.SolrCloudTestCase; import org.apache.solr.common.SolrException; import org.apache.solr.common.params.MapSolrParams; @@ -56,13 +55,9 @@ public void setUp() throws Exception { @Test public void proxySystemInfoHandlerAllNodes() throws IOException, SolrServerException { MapSolrParams params = new MapSolrParams(Collections.singletonMap("nodes", "all")); - GenericSolrRequest req = - new GenericSolrRequest( - SolrRequest.METHOD.GET, - "/admin/info/system", - SolrRequest.SolrRequestType.ADMIN, - params); - SimpleSolrResponse rsp = req.process(solrClient, null); + + SystemInfoRequest req = new SystemInfoRequest(params); + SystemInfoResponse rsp = req.process(solrClient, null); NamedList nl = rsp.getResponse(); assertEquals(3, nl.size()); assertTrue(nl.getName(1).endsWith("_solr")); @@ -75,13 +70,8 @@ public void proxySystemInfoHandlerAllNodes() throws IOException, SolrServerExcep public void proxySystemInfoHandlerNonExistingNode() throws IOException, SolrServerException { MapSolrParams params = new MapSolrParams(Collections.singletonMap("nodes", "example.com:1234_solr")); - GenericSolrRequest req = - new GenericSolrRequest( - SolrRequest.METHOD.GET, - "/admin/info/system", - SolrRequest.SolrRequestType.ADMIN, - params); - SimpleSolrResponse rsp = req.process(solrClient, null); + SystemInfoRequest req = new SystemInfoRequest(params); + SystemInfoResponse rsp = req.process(solrClient, null); } @Test @@ -91,13 +81,8 @@ public void proxySystemInfoHandlerOneNode() { nodes.forEach( node -> { MapSolrParams params = new MapSolrParams(Collections.singletonMap("nodes", node)); - GenericSolrRequest req = - new GenericSolrRequest( - SolrRequest.METHOD.GET, - "/admin/info/system", - SolrRequest.SolrRequestType.ADMIN, - params); - SimpleSolrResponse rsp = null; + SystemInfoRequest req = new SystemInfoRequest(params); + SystemInfoResponse rsp = null; try { rsp = req.process(solrClient, null); } catch (Exception e) { diff --git a/solr/core/src/test/org/apache/solr/handler/admin/MetricsHandlerTest.java b/solr/core/src/test/org/apache/solr/handler/admin/MetricsHandlerTest.java index f2ea61a9fb5..7cf04d54375 100644 --- a/solr/core/src/test/org/apache/solr/handler/admin/MetricsHandlerTest.java +++ b/solr/core/src/test/org/apache/solr/handler/admin/MetricsHandlerTest.java @@ -51,7 +51,7 @@ public void testMetricNamesFiltering() throws Exception { handler.handleRequestBody( req( CommonParams.QT, - "/admin/metrics", + CommonParams.METRICS_PATH, CommonParams.WT, MetricsHandler.PROMETHEUS_METRICS_WT, MetricsHandler.METRIC_NAME_PARAM, @@ -78,7 +78,7 @@ public void testMultipleMetricNamesFiltering() throws Exception { handler.handleRequestBody( req( CommonParams.QT, - "/admin/metrics", + CommonParams.METRICS_PATH, CommonParams.WT, MetricsHandler.PROMETHEUS_METRICS_WT, MetricsHandler.METRIC_NAME_PARAM, @@ -103,7 +103,7 @@ public void testNonExistentMetricNameFiltering() throws Exception { handler.handleRequestBody( req( CommonParams.QT, - "/admin/metrics", + CommonParams.METRICS_PATH, CommonParams.WT, MetricsHandler.PROMETHEUS_METRICS_WT, MetricsHandler.METRIC_NAME_PARAM, @@ -124,7 +124,7 @@ public void testLabelFiltering() throws Exception { handler.handleRequestBody( req( CommonParams.QT, - "/admin/metrics", + CommonParams.METRICS_PATH, CommonParams.WT, MetricsHandler.PROMETHEUS_METRICS_WT, MetricsHandler.CATEGORY_PARAM, @@ -154,7 +154,7 @@ public void testMultipleLabelFiltering() throws Exception { handler.handleRequestBody( req( CommonParams.QT, - "/admin/metrics", + CommonParams.METRICS_PATH, CommonParams.WT, MetricsHandler.PROMETHEUS_METRICS_WT, MetricsHandler.CATEGORY_PARAM, @@ -186,7 +186,7 @@ public void testNonExistentLabelFiltering() throws Exception { handler.handleRequestBody( req( CommonParams.QT, - "/admin/metrics", + CommonParams.METRICS_PATH, CommonParams.WT, MetricsHandler.PROMETHEUS_METRICS_WT, MetricsHandler.CORE_PARAM, @@ -208,7 +208,7 @@ public void testMixedLabelFiltering() throws Exception { handler.handleRequestBody( req( CommonParams.QT, - "/admin/metrics", + CommonParams.METRICS_PATH, CommonParams.WT, MetricsHandler.PROMETHEUS_METRICS_WT, MetricsHandler.CORE_PARAM, @@ -243,7 +243,7 @@ public void testMetricNamesAndLabelFiltering() throws Exception { handler.handleRequestBody( req( CommonParams.QT, - "/admin/metrics", + CommonParams.METRICS_PATH, CommonParams.WT, MetricsHandler.PROMETHEUS_METRICS_WT, MetricsHandler.CATEGORY_PARAM, diff --git a/solr/core/src/test/org/apache/solr/response/TestPrometheusResponseWriter.java b/solr/core/src/test/org/apache/solr/response/TestPrometheusResponseWriter.java index 2c86fc41bfb..fc2754da254 100644 --- a/solr/core/src/test/org/apache/solr/response/TestPrometheusResponseWriter.java +++ b/solr/core/src/test/org/apache/solr/response/TestPrometheusResponseWriter.java @@ -29,9 +29,7 @@ import org.apache.lucene.tests.util.LuceneTestCase; import org.apache.solr.SolrTestCaseJ4; import org.apache.solr.client.solrj.SolrClient; -import org.apache.solr.client.solrj.SolrRequest.METHOD; -import org.apache.solr.client.solrj.SolrRequest.SolrRequestType; -import org.apache.solr.client.solrj.request.GenericSolrRequest; +import org.apache.solr.client.solrj.request.MetricsRequest; import org.apache.solr.client.solrj.response.InputStreamResponseParser; import org.apache.solr.common.params.ModifiableSolrParams; import org.apache.solr.common.util.NamedList; @@ -74,7 +72,7 @@ public static void beforeClass() throws Exception { public void testPrometheusStructureOutput() throws Exception { ModifiableSolrParams params = new ModifiableSolrParams(); params.set("wt", "prometheus"); - var req = new GenericSolrRequest(METHOD.GET, "/admin/metrics", SolrRequestType.ADMIN, params); + var req = new MetricsRequest(params); req.setResponseParser(new InputStreamResponseParser("prometheus")); try (SolrClient adminClient = getHttpSolrClient(solrClientTestRule.getBaseUrl())) { @@ -122,8 +120,7 @@ public void testPrometheusStructureOutput() throws Exception { @Test public void testAcceptHeaderOpenMetricsFormat() throws Exception { - ModifiableSolrParams params = new ModifiableSolrParams(); - var req = new GenericSolrRequest(METHOD.GET, "/admin/metrics", SolrRequestType.ADMIN, params); + var req = new MetricsRequest(); req.setResponseParser(new InputStreamResponseParser(null)); @@ -143,8 +140,7 @@ public void testAcceptHeaderOpenMetricsFormat() throws Exception { @Test public void testWtParameterOpenMetricsFormat() throws Exception { - ModifiableSolrParams params = new ModifiableSolrParams(); - var req = new GenericSolrRequest(METHOD.GET, "/admin/metrics", SolrRequestType.ADMIN, params); + var req = new MetricsRequest(); req.setResponseParser(new InputStreamResponseParser("openmetrics")); @@ -162,8 +158,7 @@ public void testWtParameterOpenMetricsFormat() throws Exception { @Test public void testDefaultPrometheusFormat() throws Exception { - ModifiableSolrParams params = new ModifiableSolrParams(); - var req = new GenericSolrRequest(METHOD.GET, "/admin/metrics", SolrRequestType.ADMIN, params); + var req = new MetricsRequest(); req.setResponseParser(new InputStreamResponseParser("prometheus")); @@ -181,8 +176,7 @@ public void testDefaultPrometheusFormat() throws Exception { @Test public void testDefaultPrometheusFormatNoWtParam() throws Exception { - ModifiableSolrParams params = new ModifiableSolrParams(); - var req = new GenericSolrRequest(METHOD.GET, "/admin/metrics", SolrRequestType.ADMIN, params); + var req = new MetricsRequest(); req.setResponseParser(new InputStreamResponseParser(null)); @@ -200,8 +194,7 @@ public void testDefaultPrometheusFormatNoWtParam() throws Exception { @Test public void testUnsupportedMetricsFormat() throws Exception { - ModifiableSolrParams params = new ModifiableSolrParams(); - var req = new GenericSolrRequest(METHOD.GET, "/admin/metrics", SolrRequestType.ADMIN, params); + var req = new MetricsRequest(); req.setResponseParser(new InputStreamResponseParser("unknownFormat")); diff --git a/solr/core/src/test/org/apache/solr/response/TestPrometheusResponseWriterCloud.java b/solr/core/src/test/org/apache/solr/response/TestPrometheusResponseWriterCloud.java index cd5149ca37f..ffc254723e1 100644 --- a/solr/core/src/test/org/apache/solr/response/TestPrometheusResponseWriterCloud.java +++ b/solr/core/src/test/org/apache/solr/response/TestPrometheusResponseWriterCloud.java @@ -19,10 +19,8 @@ import java.io.InputStream; import java.nio.charset.StandardCharsets; import org.apache.solr.client.solrj.SolrClient; -import org.apache.solr.client.solrj.SolrRequest.METHOD; -import org.apache.solr.client.solrj.SolrRequest.SolrRequestType; import org.apache.solr.client.solrj.request.CollectionAdminRequest; -import org.apache.solr.client.solrj.request.GenericSolrRequest; +import org.apache.solr.client.solrj.request.MetricsRequest; import org.apache.solr.client.solrj.request.SolrQuery; import org.apache.solr.client.solrj.response.InputStreamResponseParser; import org.apache.solr.cloud.SolrCloudTestCase; @@ -69,9 +67,7 @@ public void testPrometheusCloudLabels() throws Exception { SolrQuery query = new SolrQuery("*:*"); solrClient.query("collection1", query); - var req = - new GenericSolrRequest( - METHOD.GET, "/admin/metrics", SolrRequestType.ADMIN, SolrParams.of("wt", "prometheus")); + var req = new MetricsRequest(SolrParams.of("wt", "prometheus")); req.setResponseParser(new InputStreamResponseParser("prometheus")); NamedList resp = solrClient.request(req); @@ -101,9 +97,7 @@ public void testCollectionDeletePrometheusOutput() throws Exception { solrClient.query("collection1", query); solrClient.query("collection2", query); - var req = - new GenericSolrRequest( - METHOD.GET, "/admin/metrics", SolrRequestType.ADMIN, SolrParams.of("wt", "prometheus")); + var req = new MetricsRequest(SolrParams.of("wt", "prometheus")); req.setResponseParser(new InputStreamResponseParser("prometheus")); NamedList resp = solrClient.request(req); diff --git a/solr/core/src/test/org/apache/solr/security/MultiAuthPluginTest.java b/solr/core/src/test/org/apache/solr/security/MultiAuthPluginTest.java index f04bd268cda..8a9c2ca9205 100644 --- a/solr/core/src/test/org/apache/solr/security/MultiAuthPluginTest.java +++ b/solr/core/src/test/org/apache/solr/security/MultiAuthPluginTest.java @@ -48,6 +48,7 @@ import org.apache.solr.client.solrj.SolrClient; import org.apache.solr.client.solrj.apache.HttpClientUtil; import org.apache.solr.common.SolrException; +import org.apache.solr.common.params.CommonParams; import org.apache.solr.common.util.CommandOperation; import org.apache.solr.common.util.Utils; import org.apache.solr.embedded.JettySolrRunner; @@ -132,12 +133,12 @@ public void testMultiAuthEditAPI() throws Exception { pass); // anonymous requests are blocked by all plugins - int statusCode = doHttpGetAnonymous(httpClient, baseUrl + "/admin/info/system"); + int statusCode = doHttpGetAnonymous(httpClient, baseUrl + CommonParams.SYSTEM_INFO_PATH); assertEquals("anonymous get succeeded but should not have", 401, statusCode); // update blockUnknown to allow anonymous for the basic plugin String command = "{\n" + "'set-property': { 'basic': {'blockUnknown':false} }\n" + "}"; doHttpPost(httpClient, baseUrl + authcPrefix, command, user, pass, 200); - statusCode = doHttpGetAnonymous(httpClient, baseUrl + "/admin/info/system"); + statusCode = doHttpGetAnonymous(httpClient, baseUrl + CommonParams.SYSTEM_INFO_PATH); assertEquals("anonymous get failed but should have succeeded", 200, statusCode); // For the multi-auth plugin, every command is wrapped with an object that identifies the @@ -474,7 +475,7 @@ public void testMultiAuthWithBasicPluginAndAjax() throws Exception { securityConfHandler.securityConfEdited(); // Pretend to send unauthorized AJAX request - HttpGet httpGet = new HttpGet(baseUrl + "/admin/info/system"); + HttpGet httpGet = new HttpGet(baseUrl + CommonParams.SYSTEM_INFO_PATH); httpGet.addHeader(new BasicHeader("X-Requested-With", "XMLHttpRequest")); HttpResponse response = httpClient.execute(httpGet); @@ -515,7 +516,7 @@ private int doHttpGetAnonymous(HttpClient cl, String url) throws IOException { private void verifyWWWAuthenticateHeaders(HttpClient httpClient, String baseUrl) throws Exception { - HttpGet httpGet = new HttpGet(baseUrl + "/admin/info/system"); + HttpGet httpGet = new HttpGet(baseUrl + CommonParams.SYSTEM_INFO_PATH); HttpResponse response = httpClient.execute(httpGet); Header[] headers = response.getHeaders(HttpHeaders.WWW_AUTHENTICATE); List actualSchemes = diff --git a/solr/modules/jwt-auth/src/test/org/apache/solr/security/jwt/JWTAuthPluginIntegrationTest.java b/solr/modules/jwt-auth/src/test/org/apache/solr/security/jwt/JWTAuthPluginIntegrationTest.java index 1650fbb4788..dc281359b8c 100644 --- a/solr/modules/jwt-auth/src/test/org/apache/solr/security/jwt/JWTAuthPluginIntegrationTest.java +++ b/solr/modules/jwt-auth/src/test/org/apache/solr/security/jwt/JWTAuthPluginIntegrationTest.java @@ -61,6 +61,7 @@ import org.apache.solr.cloud.MiniSolrCloudCluster; import org.apache.solr.cloud.SolrCloudAuthTestCase; import org.apache.solr.common.SolrException; +import org.apache.solr.common.params.CommonParams; import org.apache.solr.common.util.Pair; import org.apache.solr.common.util.TimeSource; import org.apache.solr.embedded.JettySolrRunner; @@ -142,11 +143,11 @@ public void mockOAuth2Server() throws Exception { String baseUrl = myCluster.getRandomJetty(random()).getBaseUrl().toString(); // First attempt without token fails - Map headers = getHeaders(baseUrl + "/admin/info/system", null); + Map headers = getHeaders(baseUrl + CommonParams.SYSTEM_INFO_PATH, null); assertEquals("Should have received 401 code", "401", headers.get("code")); // Second attempt with token from Oauth mock server succeeds - headers = getHeaders(baseUrl + "/admin/info/system", mockOAuthToken); + headers = getHeaders(baseUrl + CommonParams.SYSTEM_INFO_PATH, mockOAuthToken); assertEquals("200", headers.get("code")); myCluster.shutdown(); } @@ -162,10 +163,10 @@ public void testStaticJwtKeys() throws Exception { String baseUrl = myCluster.getRandomJetty(random()).getBaseUrl().toString(); // No token fails - assertThrows(IOException.class, () -> get(baseUrl + "/admin/info/system", null)); + assertThrows(IOException.class, () -> get(baseUrl + CommonParams.SYSTEM_INFO_PATH, null)); // Validate X-Solr-AuthData headers - Map headers = getHeaders(baseUrl + "/admin/info/system", null); + Map headers = getHeaders(baseUrl + CommonParams.SYSTEM_INFO_PATH, null); assertEquals("Should have received 401 code", "401", headers.get("code")); assertEquals("Bearer realm=\"my-solr-jwt\"", headers.get("WWW-Authenticate")); String authData = new String(Base64.getDecoder().decode(headers.get("X-Solr-AuthData")), UTF_8); @@ -188,7 +189,7 @@ public void infoRequestValidateXSolrAuthHeadersBlockUnknownFalse() throws Except configureClusterStaticKeys("jwt_plugin_jwk_security_blockUnknownFalse.json"); String baseUrl = myCluster.getRandomJetty(random()).getBaseUrl().toString(); - Map headers = getHeaders(baseUrl + "/admin/info/system", null); + Map headers = getHeaders(baseUrl + CommonParams.SYSTEM_INFO_PATH, null); assertEquals("Should have received 401 code", "401", headers.get("code")); assertEquals( "Bearer realm=\"my-solr-jwt-blockunknown-false\"", headers.get("WWW-Authenticate")); diff --git a/solr/modules/opentelemetry/src/test/org/apache/solr/opentelemetry/TestDistributedTracing.java b/solr/modules/opentelemetry/src/test/org/apache/solr/opentelemetry/TestDistributedTracing.java index bc90908bc6d..97916c007ca 100644 --- a/solr/modules/opentelemetry/src/test/org/apache/solr/opentelemetry/TestDistributedTracing.java +++ b/solr/modules/opentelemetry/src/test/org/apache/solr/opentelemetry/TestDistributedTracing.java @@ -31,11 +31,10 @@ import java.util.List; import java.util.Map; import org.apache.solr.client.solrj.SolrRequest; -import org.apache.solr.client.solrj.SolrRequest.SolrRequestType; import org.apache.solr.client.solrj.SolrServerException; import org.apache.solr.client.solrj.impl.CloudSolrClient; import org.apache.solr.client.solrj.request.CollectionAdminRequest; -import org.apache.solr.client.solrj.request.GenericSolrRequest; +import org.apache.solr.client.solrj.request.MetricsRequest; import org.apache.solr.client.solrj.request.SolrQuery; import org.apache.solr.client.solrj.request.V2Request; import org.apache.solr.client.solrj.response.CollectionAdminResponse; @@ -136,8 +135,7 @@ public void test() throws IOException, SolrServerException { public void testAdminApi() throws Exception { CloudSolrClient cloudClient = cluster.getSolrClient(); - GenericSolrRequest request = - new GenericSolrRequest(SolrRequest.METHOD.GET, "/admin/metrics", SolrRequestType.ADMIN); + MetricsRequest request = new MetricsRequest(); request.setResponseParser(new InputStreamResponseParser(PROMETHEUS_METRICS_WT)); NamedList rsp = cloudClient.request(request); ((InputStream) rsp.get("stream")).close(); diff --git a/solr/modules/opentelemetry/src/test/org/apache/solr/opentelemetry/TestMetricExemplars.java b/solr/modules/opentelemetry/src/test/org/apache/solr/opentelemetry/TestMetricExemplars.java index 0a842d57915..6eaf0c07e17 100644 --- a/solr/modules/opentelemetry/src/test/org/apache/solr/opentelemetry/TestMetricExemplars.java +++ b/solr/modules/opentelemetry/src/test/org/apache/solr/opentelemetry/TestMetricExemplars.java @@ -23,10 +23,9 @@ import io.opentelemetry.api.trace.TracerProvider; import java.io.InputStream; import java.nio.charset.StandardCharsets; -import org.apache.solr.client.solrj.SolrRequest; import org.apache.solr.client.solrj.impl.CloudSolrClient; import org.apache.solr.client.solrj.request.CollectionAdminRequest; -import org.apache.solr.client.solrj.request.GenericSolrRequest; +import org.apache.solr.client.solrj.request.MetricsRequest; import org.apache.solr.client.solrj.response.InputStreamResponseParser; import org.apache.solr.cloud.SolrCloudTestCase; import org.apache.solr.common.params.ModifiableSolrParams; @@ -79,12 +78,7 @@ public void testOpenMetricExemplars() throws Exception { var spans = getAndClearSpans(); var expectedTrace = getRootTraceId(spans); - var req = - new GenericSolrRequest( - SolrRequest.METHOD.GET, - "/admin/metrics", - SolrRequest.SolrRequestType.ADMIN, - new ModifiableSolrParams().set("wt", "openmetrics")); + var req = new MetricsRequest(new ModifiableSolrParams().set("wt", "openmetrics")); req.setResponseParser(new InputStreamResponseParser("openmetrics")); NamedList resp = cloudClient.request(req); diff --git a/solr/solrj-streaming/src/java/org/apache/solr/client/solrj/io/sql/DatabaseMetaDataImpl.java b/solr/solrj-streaming/src/java/org/apache/solr/client/solrj/io/sql/DatabaseMetaDataImpl.java index 643b51b57b1..cde6a04b068 100644 --- a/solr/solrj-streaming/src/java/org/apache/solr/client/solrj/io/sql/DatabaseMetaDataImpl.java +++ b/solr/solrj-streaming/src/java/org/apache/solr/client/solrj/io/sql/DatabaseMetaDataImpl.java @@ -28,10 +28,9 @@ import org.apache.solr.client.solrj.SolrServerException; import org.apache.solr.client.solrj.impl.CloudSolrClient; import org.apache.solr.client.solrj.jetty.HttpJettySolrClient; -import org.apache.solr.client.solrj.request.SolrQuery; -import org.apache.solr.client.solrj.response.QueryResponse; +import org.apache.solr.client.solrj.request.SystemInfoRequest; +import org.apache.solr.client.solrj.response.SystemInfoResponse; import org.apache.solr.common.cloud.ClusterState; -import org.apache.solr.common.util.SimpleOrderedMap; import org.apache.solr.common.util.Utils; class DatabaseMetaDataImpl implements DatabaseMetaData { @@ -111,8 +110,6 @@ public String getDatabaseProductName() throws SQLException { @Override public String getDatabaseProductVersion() throws SQLException { // Returns the version for the first live node in the Solr cluster. - SolrQuery sysQuery = new SolrQuery(); - sysQuery.setRequestHandler("/admin/info/system"); CloudSolrClient cloudSolrClient = this.connection.getClient(); Set liveNodes = cloudSolrClient.getClusterState().getLiveNodes(); @@ -126,9 +123,10 @@ public String getDatabaseProductVersion() throws SQLException { String nodeURL = Utils.getBaseUrlForNodeName(node, urlScheme); solrClient = new HttpJettySolrClient.Builder(nodeURL).build(); - QueryResponse rsp = solrClient.query(sysQuery); - return String.valueOf( - ((SimpleOrderedMap) rsp.getResponse().get("lucene")).get("solr-spec-version")); + SystemInfoRequest req = new SystemInfoRequest(); + SystemInfoResponse resp = req.process(solrClient); + + return resp.getSolrSpecVersion(); } catch (SolrServerException | IOException ignore) { return ""; } finally { diff --git a/solr/solrj-zookeeper/src/java/org/apache/solr/client/solrj/impl/NodeValueFetcher.java b/solr/solrj-zookeeper/src/java/org/apache/solr/client/solrj/impl/NodeValueFetcher.java index 868cbfd2926..3dd91fca5d3 100644 --- a/solr/solrj-zookeeper/src/java/org/apache/solr/client/solrj/impl/NodeValueFetcher.java +++ b/solr/solrj-zookeeper/src/java/org/apache/solr/client/solrj/impl/NodeValueFetcher.java @@ -30,9 +30,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Stream; -import org.apache.solr.client.solrj.SolrRequest.METHOD; -import org.apache.solr.client.solrj.SolrRequest.SolrRequestType; -import org.apache.solr.client.solrj.request.GenericSolrRequest; +import org.apache.solr.client.solrj.request.MetricsRequest; import org.apache.solr.client.solrj.response.InputStreamResponseParser; import org.apache.solr.client.solrj.response.SimpleSolrResponse; import org.apache.solr.common.SolrException; @@ -176,7 +174,7 @@ private void getRemoteMetricsFromTags( params.add("name", StrUtils.join(uniqueMetricNames, ',')); try { - var req = new GenericSolrRequest(METHOD.GET, "/admin/metrics", SolrRequestType.ADMIN, params); + var req = new MetricsRequest(params); req.setResponseParser(new InputStreamResponseParser("prometheus")); String baseUrl = diff --git a/solr/solrj-zookeeper/src/java/org/apache/solr/client/solrj/impl/SolrClientNodeStateProvider.java b/solr/solrj-zookeeper/src/java/org/apache/solr/client/solrj/impl/SolrClientNodeStateProvider.java index cc8365067ae..ef6bb5dbd21 100644 --- a/solr/solrj-zookeeper/src/java/org/apache/solr/client/solrj/impl/SolrClientNodeStateProvider.java +++ b/solr/solrj-zookeeper/src/java/org/apache/solr/client/solrj/impl/SolrClientNodeStateProvider.java @@ -34,6 +34,7 @@ import org.apache.solr.client.solrj.SolrServerException; import org.apache.solr.client.solrj.cloud.NodeStateProvider; import org.apache.solr.client.solrj.request.GenericSolrRequest; +import org.apache.solr.client.solrj.request.MetricsRequest; import org.apache.solr.client.solrj.response.InputStreamResponseParser; import org.apache.solr.client.solrj.response.JavaBinResponseParser; import org.apache.solr.client.solrj.response.SimpleSolrResponse; @@ -212,9 +213,7 @@ static void processMetricStream( params.add("wt", "prometheus"); params.add("name", String.join(",", metricNames)); - var req = - new GenericSolrRequest( - SolrRequest.METHOD.GET, "/admin/metrics", SolrRequest.SolrRequestType.ADMIN, params); + var req = new MetricsRequest(params); req.setResponseParser(new InputStreamResponseParser("prometheus")); String baseUrl = diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/request/MetricsRequest.java b/solr/solrj/src/java/org/apache/solr/client/solrj/request/MetricsRequest.java new file mode 100644 index 00000000000..8f1f1f6a59f --- /dev/null +++ b/solr/solrj/src/java/org/apache/solr/client/solrj/request/MetricsRequest.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.solr.client.solrj.request; + +import org.apache.solr.client.solrj.SolrRequest; +import org.apache.solr.client.solrj.SolrResponse; +import org.apache.solr.client.solrj.response.SolrResponseBase; +import org.apache.solr.common.params.CommonParams; +import org.apache.solr.common.params.ModifiableSolrParams; +import org.apache.solr.common.params.SolrParams; +import org.apache.solr.common.util.NamedList; + +/** Request to "/admin/metrics" */ +public class MetricsRequest extends SolrRequest { + + private static final long serialVersionUID = 1L; + + private final SolrParams params; + + /** Request to "/admin/metrics" by default, without params */ + public MetricsRequest() { + this(METHOD.GET, CommonParams.METRICS_PATH, SolrRequestType.ADMIN, new ModifiableSolrParams()); + } + + /** + * @param params the Solr parameters to use for this request. + */ + public MetricsRequest(SolrParams params) { + this(METHOD.GET, CommonParams.METRICS_PATH, SolrRequestType.ADMIN, params); + } + + /** + * @param method the HTTP method to use for this request. + * @param path the HTTP path to use for this request. Supports V1 "/admin/info/system" (default) + * or V2 "/node/system" + * @param requestType the type of this request + * @param params query parameter names and values for making this request. + */ + public MetricsRequest( + METHOD method, String path, SolrRequestType requestType, SolrParams params) { + super(method, path, requestType); + this.params = params; + } + + @Override + public SolrParams getParams() { + return params; + } + + @Override + protected SolrResponse createResponse(NamedList namedList) { + SolrResponseBase resp = new SolrResponseBase(); + return (SolrResponse) resp; + } +} diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/request/SystemInfoRequest.java b/solr/solrj/src/java/org/apache/solr/client/solrj/request/SystemInfoRequest.java index 51748625781..81ac3e31a25 100644 --- a/solr/solrj/src/java/org/apache/solr/client/solrj/request/SystemInfoRequest.java +++ b/solr/solrj/src/java/org/apache/solr/client/solrj/request/SystemInfoRequest.java @@ -19,6 +19,7 @@ import org.apache.solr.client.solrj.SolrRequest; import org.apache.solr.client.solrj.response.SystemInfoResponse; import org.apache.solr.common.params.CommonParams; +import org.apache.solr.common.params.ModifiableSolrParams; import org.apache.solr.common.params.SolrParams; import org.apache.solr.common.util.NamedList; @@ -27,17 +28,52 @@ public class SystemInfoRequest extends SolrRequest { private static final long serialVersionUID = 1L; + private final SolrParams params; + + /** Request to "/admin/info/system" by default, without params. */ public SystemInfoRequest() { this(CommonParams.SYSTEM_INFO_PATH); } + /** + * @param path the HTTP path to use for this request. Supports V1 "/admin/info/system" (default) + * or V2 "/node/system" + */ public SystemInfoRequest(String path) { - super(METHOD.GET, path, SolrRequestType.ADMIN); + this(METHOD.GET, path, SolrRequestType.ADMIN, new ModifiableSolrParams()); + } + + /** + * @param params the Solr parameters to use for this request. + */ + public SystemInfoRequest(SolrParams params) { + this(METHOD.GET, CommonParams.SYSTEM_INFO_PATH, SolrRequestType.ADMIN, params); + } + + /** + * @param path the HTTP path to use for this request. Supports V1 "/admin/info/system" (default) + * or V2 "/node/system" + * @param params query parameter names and values for making this request. + */ + public SystemInfoRequest(String path, SolrParams params) { + this(METHOD.GET, path, SolrRequestType.ADMIN, params); + } + + /** + * @param method the HTTP method to use for this request. + * @param path the HTTP path to use for this request. Supports V1 "/admin/info/system" (default) + * or V2 "/node/system" + * @param type the type of this request + * @param params query parameter names and values for making this request. + */ + public SystemInfoRequest(METHOD method, String path, SolrRequestType type, SolrParams params) { + super(method, path, type); + this.params = params; } @Override public SolrParams getParams() { - return SolrParams.of(); // no params to return, but avoid NPE + return params; } @Override diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/response/SystemInfoResponse.java b/solr/solrj/src/java/org/apache/solr/client/solrj/response/SystemInfoResponse.java index 41c389de6ca..a00f8e63c1d 100644 --- a/solr/solrj/src/java/org/apache/solr/client/solrj/response/SystemInfoResponse.java +++ b/solr/solrj/src/java/org/apache/solr/client/solrj/response/SystemInfoResponse.java @@ -47,10 +47,10 @@ private void init() { } } - /* SolrRequest.process only needs to set 'elapsedTime' */ @Override public void setResponse(NamedList response) { if (getResponse() == null) super.setResponse(response); + if (fullResponse == null) init(); } public String getMode() { @@ -78,7 +78,7 @@ public org.apache.solr.client.api.model.SystemInfoResponse getFullResponse() { } // ************************* - // Shortcuts for StatusTool.reportStatus(SolrClient) + // Shortcuts // *********** public String getSolrImplVersion() { return getFullResponse() != null && getFullResponse().lucene != null @@ -86,6 +86,12 @@ public String getSolrImplVersion() { : null; } + public String getSolrSpecVersion() { + return getFullResponse() != null && getFullResponse().lucene != null + ? getFullResponse().lucene.solrSpecVersion + : null; + } + public Date getJVMStartTime() { return getFullResponse() != null && getFullResponse().jvm != null diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/SolrExampleTests.java b/solr/solrj/src/test/org/apache/solr/client/solrj/SolrExampleTests.java index 8935d0301a5..759dd72a153 100644 --- a/solr/solrj/src/test/org/apache/solr/client/solrj/SolrExampleTests.java +++ b/solr/solrj/src/test/org/apache/solr/client/solrj/SolrExampleTests.java @@ -441,7 +441,7 @@ public void testExampleConfig() throws Exception { String url = getBaseUrl(); try (SolrClient adminClient = getHttpSolrClient(url)) { SolrQuery q = new SolrQuery(); - q.set("qt", "/admin/info/system"); + q.set("qt", CommonParams.SYSTEM_INFO_PATH); QueryResponse rsp = adminClient.query(q); assertNotNull(rsp.getResponse().get("mode")); diff --git a/solr/test-framework/src/java/org/apache/solr/util/SolrJMetricTestUtils.java b/solr/test-framework/src/java/org/apache/solr/util/SolrJMetricTestUtils.java index 556aee93650..1ec456202c6 100644 --- a/solr/test-framework/src/java/org/apache/solr/util/SolrJMetricTestUtils.java +++ b/solr/test-framework/src/java/org/apache/solr/util/SolrJMetricTestUtils.java @@ -21,10 +21,9 @@ import java.io.InputStream; import java.nio.charset.StandardCharsets; 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.jetty.HttpJettySolrClient; -import org.apache.solr.client.solrj.request.GenericSolrRequest; +import org.apache.solr.client.solrj.request.MetricsRequest; import org.apache.solr.client.solrj.response.InputStreamResponseParser; import org.apache.solr.common.params.SolrParams; import org.apache.solr.common.util.NamedList; @@ -33,12 +32,7 @@ public final class SolrJMetricTestUtils { public static double getPrometheusMetricValue(SolrClient solrClient, String metricName) throws SolrServerException, IOException { - var req = - new GenericSolrRequest( - SolrRequest.METHOD.GET, - "/admin/metrics", - SolrRequest.SolrRequestType.ADMIN, - SolrParams.of("wt", "prometheus")); + var req = new MetricsRequest(SolrParams.of("wt", "prometheus")); req.setResponseParser(new InputStreamResponseParser("prometheus")); NamedList resp = solrClient.request(req); @@ -57,12 +51,7 @@ public static Double getNumCoreRequests( throws SolrServerException, IOException { try (var client = new HttpJettySolrClient.Builder(baseUrl).build()) { - var req = - new GenericSolrRequest( - SolrRequest.METHOD.GET, - "/admin/metrics", - SolrRequest.SolrRequestType.ADMIN, - SolrParams.of("wt", "prometheus")); + var req = new MetricsRequest(SolrParams.of("wt", "prometheus")); req.setResponseParser(new InputStreamResponseParser("prometheus")); NamedList resp = client.request(req); @@ -90,12 +79,7 @@ public static Double getNumNodeRequestErrors(String baseUrl, String category, St throws SolrServerException, IOException { try (var client = new HttpJettySolrClient.Builder(baseUrl).build()) { - var req = - new GenericSolrRequest( - SolrRequest.METHOD.GET, - "/admin/metrics", - SolrRequest.SolrRequestType.ADMIN, - SolrParams.of("wt", "prometheus")); + var req = new MetricsRequest(SolrParams.of("wt", "prometheus")); req.setResponseParser(new InputStreamResponseParser("prometheus")); NamedList resp = client.request(req); From 955fc46050dcffdfe66b6f23ecc864d25406a679 Mon Sep 17 00:00:00 2001 From: Isabelle Giguere Date: Wed, 17 Dec 2025 19:17:32 -0500 Subject: [PATCH 06/15] SOLR-17136: address code review comments. Remove permissive constructors --- .../client/solrj/request/MetricsRequest.java | 16 ++------------ .../solrj/request/SystemInfoRequest.java | 17 +++----------- .../solrj/response/SystemInfoResponse.java | 22 +++++++++---------- 3 files changed, 15 insertions(+), 40 deletions(-) diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/request/MetricsRequest.java b/solr/solrj/src/java/org/apache/solr/client/solrj/request/MetricsRequest.java index 8f1f1f6a59f..82c10659586 100644 --- a/solr/solrj/src/java/org/apache/solr/client/solrj/request/MetricsRequest.java +++ b/solr/solrj/src/java/org/apache/solr/client/solrj/request/MetricsRequest.java @@ -33,26 +33,14 @@ public class MetricsRequest extends SolrRequest { /** Request to "/admin/metrics" by default, without params */ public MetricsRequest() { - this(METHOD.GET, CommonParams.METRICS_PATH, SolrRequestType.ADMIN, new ModifiableSolrParams()); + this(new ModifiableSolrParams()); } /** * @param params the Solr parameters to use for this request. */ public MetricsRequest(SolrParams params) { - this(METHOD.GET, CommonParams.METRICS_PATH, SolrRequestType.ADMIN, params); - } - - /** - * @param method the HTTP method to use for this request. - * @param path the HTTP path to use for this request. Supports V1 "/admin/info/system" (default) - * or V2 "/node/system" - * @param requestType the type of this request - * @param params query parameter names and values for making this request. - */ - public MetricsRequest( - METHOD method, String path, SolrRequestType requestType, SolrParams params) { - super(method, path, requestType); + super(METHOD.GET, CommonParams.METRICS_PATH, SolrRequestType.ADMIN); this.params = params; } diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/request/SystemInfoRequest.java b/solr/solrj/src/java/org/apache/solr/client/solrj/request/SystemInfoRequest.java index 81ac3e31a25..33a4e12fd44 100644 --- a/solr/solrj/src/java/org/apache/solr/client/solrj/request/SystemInfoRequest.java +++ b/solr/solrj/src/java/org/apache/solr/client/solrj/request/SystemInfoRequest.java @@ -40,14 +40,14 @@ public SystemInfoRequest() { * or V2 "/node/system" */ public SystemInfoRequest(String path) { - this(METHOD.GET, path, SolrRequestType.ADMIN, new ModifiableSolrParams()); + this(path, new ModifiableSolrParams()); } /** * @param params the Solr parameters to use for this request. */ public SystemInfoRequest(SolrParams params) { - this(METHOD.GET, CommonParams.SYSTEM_INFO_PATH, SolrRequestType.ADMIN, params); + this(CommonParams.SYSTEM_INFO_PATH, params); } /** @@ -56,18 +56,7 @@ public SystemInfoRequest(SolrParams params) { * @param params query parameter names and values for making this request. */ public SystemInfoRequest(String path, SolrParams params) { - this(METHOD.GET, path, SolrRequestType.ADMIN, params); - } - - /** - * @param method the HTTP method to use for this request. - * @param path the HTTP path to use for this request. Supports V1 "/admin/info/system" (default) - * or V2 "/node/system" - * @param type the type of this request - * @param params query parameter names and values for making this request. - */ - public SystemInfoRequest(METHOD method, String path, SolrRequestType type, SolrParams params) { - super(method, path, type); + super(METHOD.GET, path, SolrRequestType.ADMIN); this.params = params; } diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/response/SystemInfoResponse.java b/solr/solrj/src/java/org/apache/solr/client/solrj/response/SystemInfoResponse.java index a00f8e63c1d..19ba5e8eb6d 100644 --- a/solr/solrj/src/java/org/apache/solr/client/solrj/response/SystemInfoResponse.java +++ b/solr/solrj/src/java/org/apache/solr/client/solrj/response/SystemInfoResponse.java @@ -33,24 +33,22 @@ public class SystemInfoResponse extends SolrResponseBase { private org.apache.solr.client.api.model.SystemInfoResponse fullResponse; public SystemInfoResponse(NamedList namedList) { + if (namedList == null) throw new IllegalArgumentException("Null NamedList is not allowed."); setResponse(namedList); - init(); - } - - private void init() { - try { - fullResponse = - JacksonContentWriter.DEFAULT_MAPPER.convertValue( - getResponse(), org.apache.solr.client.api.model.SystemInfoResponse.class); - } catch (Throwable t) { - log.error("Cannot convert NamedList response to API model SystemInfoResponse", t); - } } @Override public void setResponse(NamedList response) { if (getResponse() == null) super.setResponse(response); - if (fullResponse == null) init(); + if (fullResponse == null) { + try { + fullResponse = + JacksonContentWriter.DEFAULT_MAPPER.convertValue( + getResponse(), org.apache.solr.client.api.model.SystemInfoResponse.class); + } catch (Throwable t) { + log.error("Cannot convert NamedList response to API model SystemInfoResponse", t); + } + } } public String getMode() { From f09182d14f60635105872a27f813a3cb154a006f Mon Sep 17 00:00:00 2001 From: Isabelle Giguere Date: Wed, 17 Dec 2025 22:06:25 -0500 Subject: [PATCH 07/15] SOLR-17136: Adjust SystemInfoResponse with new "gpu" field --- .../client/api/model/SystemInfoResponse.java | 28 ++++++++++++++++--- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/solr/api/src/java/org/apache/solr/client/api/model/SystemInfoResponse.java b/solr/api/src/java/org/apache/solr/client/api/model/SystemInfoResponse.java index 91fd8dc8302..d77464449d1 100644 --- a/solr/api/src/java/org/apache/solr/client/api/model/SystemInfoResponse.java +++ b/solr/api/src/java/org/apache/solr/client/api/model/SystemInfoResponse.java @@ -33,10 +33,20 @@ public class SystemInfoResponse extends SolrJerseyResponse { @JsonProperty("core_root") public String coreRoot; + @JsonProperty(isRequired = OptBoolean.FALSE) + public String environment; + + @JsonProperty(value = "environment_label", isRequired = OptBoolean.FALSE) + public String environmentLabel; + + @JsonProperty(value = "environment_color", isRequired = OptBoolean.FALSE) + public String environmentColor; + @JsonProperty public String node; @JsonProperty public Lucene lucene; @JsonProperty public JVM jvm; @JsonProperty public Security security; + @JsonProperty public GPU gpu; @JsonProperty public Map system; /** /admin/info/system/security */ @@ -82,16 +92,19 @@ public static class JvmMemory { @JsonProperty public JvmMemoryRaw raw; } - public static class JvmMemoryRaw { - @JsonProperty public long free; - @JsonProperty public long total; + public static class JvmMemoryRaw extends MemoryRaw { @JsonProperty public long max; - @JsonProperty public long used; @JsonProperty("used%") public double usedPercent; } + public static class MemoryRaw { + @JsonProperty public long free; + @JsonProperty public long total; + @JsonProperty public long used; + } + public static class Vendor { @JsonProperty(isRequired = OptBoolean.FALSE) public String name; @@ -108,4 +121,11 @@ public static class JvmJmx { @JsonProperty public long upTimeMS; @JsonProperty public List commandLineArgs; } + + public static class GPU { + @JsonProperty public boolean available; + @JsonProperty public long count; + @JsonProperty public MemoryRaw memory; + @JsonProperty Map devices; + } } From bd60a18627c51c20a1aa917b5948a673497437e2 Mon Sep 17 00:00:00 2001 From: Isabelle Giguere Date: Fri, 19 Dec 2025 15:50:14 -0500 Subject: [PATCH 08/15] SOLR-17136: address review comments --- .../core/src/java/org/apache/solr/cli/CreateTool.java | 3 +-- .../apache/solr/packagemanager/RepositoryManager.java | 7 +++++-- .../solr/handler/admin/AdminHandlersProxyTest.java | 11 +++++------ .../solr/client/solrj/request/SystemInfoRequest.java | 1 + .../client/solrj/response/SystemInfoResponse.java | 3 --- .../org/apache/solr/common/params/CommonParams.java | 1 + 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/solr/core/src/java/org/apache/solr/cli/CreateTool.java b/solr/core/src/java/org/apache/solr/cli/CreateTool.java index e50fb796e9c..d18d2e0c40e 100644 --- a/solr/core/src/java/org/apache/solr/cli/CreateTool.java +++ b/solr/core/src/java/org/apache/solr/cli/CreateTool.java @@ -158,8 +158,7 @@ protected void createCore(CommandLine cli, SolrClient solrClient) throws Excepti SystemInfoResponse sysResponse = (new SystemInfoRequest()).process(solrClient); // usually same as solr home, but not always - String coreRootDirectory = - sysResponse.getCoreRoot() != null ? sysResponse.getCoreRoot() : sysResponse.getSolrHome(); + String coreRootDirectory = sysResponse.getCoreRoot(); if (CLIUtils.safeCheckCoreExists( solrUrl, coreName, cli.getOptionValue(CommonCLIOptions.CREDENTIALS_OPTION))) { diff --git a/solr/core/src/java/org/apache/solr/packagemanager/RepositoryManager.java b/solr/core/src/java/org/apache/solr/packagemanager/RepositoryManager.java index 301f051af90..7e5c0e16b29 100644 --- a/solr/core/src/java/org/apache/solr/packagemanager/RepositoryManager.java +++ b/solr/core/src/java/org/apache/solr/packagemanager/RepositoryManager.java @@ -52,6 +52,7 @@ import org.apache.solr.common.SolrException; import org.apache.solr.common.SolrException.ErrorCode; import org.apache.solr.common.cloud.SolrZkClient; +import org.apache.solr.common.params.CommonParams; import org.apache.solr.common.util.NamedList; import org.apache.solr.common.util.Utils; import org.apache.solr.filestore.ClusterFileStore; @@ -146,8 +147,10 @@ public void addKey(byte[] key, String destinationKeyFilename) throws Exception { // This method is only called from PackageTool ("add-repo", or "add-key"), where the Solr URL is // normalized to remove the /solr path part // So might as well ping the V2 API "/node/system" instead. - // Otherwise, this SystemInfoRequest ctr would need to set the full /solr/admin/info/system path - SystemInfoResponse sysResponse = new SystemInfoRequest("/node/system").process(solrClient); + // Otherwise, this SystemInfoRequest constructor would need to set the full + // /solr/admin/info/system path + SystemInfoResponse sysResponse = + new SystemInfoRequest(CommonParams.V2_SYSTEM_INFO_PATH).process(solrClient); // put the public key into package store's trusted key store and request a sync. String path = ClusterFileStore.KEYS_DIR + "/" + destinationKeyFilename; diff --git a/solr/core/src/test/org/apache/solr/handler/admin/AdminHandlersProxyTest.java b/solr/core/src/test/org/apache/solr/handler/admin/AdminHandlersProxyTest.java index 56431b254cf..965df4fdb82 100644 --- a/solr/core/src/test/org/apache/solr/handler/admin/AdminHandlersProxyTest.java +++ b/solr/core/src/test/org/apache/solr/handler/admin/AdminHandlersProxyTest.java @@ -82,16 +82,15 @@ public void proxySystemInfoHandlerOneNode() { node -> { MapSolrParams params = new MapSolrParams(Collections.singletonMap("nodes", node)); SystemInfoRequest req = new SystemInfoRequest(params); - SystemInfoResponse rsp = null; try { - rsp = req.process(solrClient, null); + SystemInfoResponse rsp = req.process(solrClient, null); + NamedList nl = rsp.getResponse(); + assertEquals(2, nl.size()); + assertEquals("solrcloud", ((NamedList) nl.get(nl.getName(1))).get("mode")); + assertEquals(nl.getName(1), ((NamedList) nl.get(nl.getName(1))).get("node")); } catch (Exception e) { fail("Exception while proxying request to node " + node); } - NamedList nl = rsp.getResponse(); - assertEquals(2, nl.size()); - assertEquals("solrcloud", ((NamedList) nl.get(nl.getName(1))).get("mode")); - assertEquals(nl.getName(1), ((NamedList) nl.get(nl.getName(1))).get("node")); }); } } diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/request/SystemInfoRequest.java b/solr/solrj/src/java/org/apache/solr/client/solrj/request/SystemInfoRequest.java index 33a4e12fd44..345c0650256 100644 --- a/solr/solrj/src/java/org/apache/solr/client/solrj/request/SystemInfoRequest.java +++ b/solr/solrj/src/java/org/apache/solr/client/solrj/request/SystemInfoRequest.java @@ -32,6 +32,7 @@ public class SystemInfoRequest extends SolrRequest { /** Request to "/admin/info/system" by default, without params. */ public SystemInfoRequest() { + // TODO: support V2 by default. Requires refactoring throughout the CLI tools, at least this(CommonParams.SYSTEM_INFO_PATH); } diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/response/SystemInfoResponse.java b/solr/solrj/src/java/org/apache/solr/client/solrj/response/SystemInfoResponse.java index 19ba5e8eb6d..d755afa471e 100644 --- a/solr/solrj/src/java/org/apache/solr/client/solrj/response/SystemInfoResponse.java +++ b/solr/solrj/src/java/org/apache/solr/client/solrj/response/SystemInfoResponse.java @@ -75,9 +75,6 @@ public org.apache.solr.client.api.model.SystemInfoResponse getFullResponse() { return fullResponse; } - // ************************* - // Shortcuts - // *********** public String getSolrImplVersion() { return getFullResponse() != null && getFullResponse().lucene != null ? getFullResponse().lucene.solrImplVersion diff --git a/solr/solrj/src/java/org/apache/solr/common/params/CommonParams.java b/solr/solrj/src/java/org/apache/solr/common/params/CommonParams.java index 0387038aa84..eb122557b76 100644 --- a/solr/solrj/src/java/org/apache/solr/common/params/CommonParams.java +++ b/solr/solrj/src/java/org/apache/solr/common/params/CommonParams.java @@ -205,6 +205,7 @@ public interface CommonParams { String ZK_STATUS_PATH = "/admin/zookeeper/status"; String SYSTEM_INFO_PATH = "/admin/info/system"; String METRICS_PATH = "/admin/metrics"; + String V2_SYSTEM_INFO_PATH = "/node/system"; String STATUS = "status"; From d3357709c7bdc7ad3d3efac81e30071aef64927a Mon Sep 17 00:00:00 2001 From: Isabelle Giguere Date: Fri, 19 Dec 2025 16:10:57 -0500 Subject: [PATCH 09/15] SOLR-17136: rename API model SystemInfoResponse to NodeSystemResponse --- .../{SystemInfoResponse.java => NodeSystemResponse.java} | 9 +++++---- .../solr/client/solrj/response/SystemInfoResponse.java | 9 +++++---- 2 files changed, 10 insertions(+), 8 deletions(-) rename solr/api/src/java/org/apache/solr/client/api/model/{SystemInfoResponse.java => NodeSystemResponse.java} (95%) diff --git a/solr/api/src/java/org/apache/solr/client/api/model/SystemInfoResponse.java b/solr/api/src/java/org/apache/solr/client/api/model/NodeSystemResponse.java similarity index 95% rename from solr/api/src/java/org/apache/solr/client/api/model/SystemInfoResponse.java rename to solr/api/src/java/org/apache/solr/client/api/model/NodeSystemResponse.java index d77464449d1..83354402673 100644 --- a/solr/api/src/java/org/apache/solr/client/api/model/SystemInfoResponse.java +++ b/solr/api/src/java/org/apache/solr/client/api/model/NodeSystemResponse.java @@ -22,7 +22,8 @@ import java.util.List; import java.util.Map; -public class SystemInfoResponse extends SolrJerseyResponse { +/** Response from /node/system */ +public class NodeSystemResponse extends SolrJerseyResponse { @JsonProperty public String mode; @JsonProperty public String zkHost; @@ -49,7 +50,7 @@ public class SystemInfoResponse extends SolrJerseyResponse { @JsonProperty public GPU gpu; @JsonProperty public Map system; - /** /admin/info/system/security */ + /** /node/system/security */ public static class Security { @JsonProperty public boolean tls; @JsonProperty public String authenticationPlugin; @@ -59,7 +60,7 @@ public static class Security { @JsonProperty public List permissions; } - /** /admin/info/system/lucene */ + /** /node/system/lucene */ public static class Lucene { @JsonProperty("solr-spec-version") public String solrSpecVersion; @@ -74,7 +75,7 @@ public static class Lucene { public String luceneImplVersion; } - /** /admin/info/system/jvm */ + /** /node/system/jvm */ public static class JVM extends Vendor { @JsonProperty public int processors; @JsonProperty public Vendor jre; diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/response/SystemInfoResponse.java b/solr/solrj/src/java/org/apache/solr/client/solrj/response/SystemInfoResponse.java index d755afa471e..a09fc4e623c 100644 --- a/solr/solrj/src/java/org/apache/solr/client/solrj/response/SystemInfoResponse.java +++ b/solr/solrj/src/java/org/apache/solr/client/solrj/response/SystemInfoResponse.java @@ -18,6 +18,7 @@ import java.lang.invoke.MethodHandles; import java.util.Date; +import org.apache.solr.client.api.model.NodeSystemResponse; import org.apache.solr.client.solrj.request.json.JacksonContentWriter; import org.apache.solr.common.util.NamedList; import org.slf4j.Logger; @@ -30,7 +31,7 @@ public class SystemInfoResponse extends SolrResponseBase { private static final long serialVersionUID = 1L; - private org.apache.solr.client.api.model.SystemInfoResponse fullResponse; + private NodeSystemResponse fullResponse; public SystemInfoResponse(NamedList namedList) { if (namedList == null) throw new IllegalArgumentException("Null NamedList is not allowed."); @@ -44,9 +45,9 @@ public void setResponse(NamedList response) { try { fullResponse = JacksonContentWriter.DEFAULT_MAPPER.convertValue( - getResponse(), org.apache.solr.client.api.model.SystemInfoResponse.class); + getResponse(), NodeSystemResponse.class); } catch (Throwable t) { - log.error("Cannot convert NamedList response to API model SystemInfoResponse", t); + log.error("Cannot convert NamedList response to API model NodeSystemResponse", t); } } } @@ -71,7 +72,7 @@ public String getNode() { return getFullResponse().node; } - public org.apache.solr.client.api.model.SystemInfoResponse getFullResponse() { + public NodeSystemResponse getFullResponse() { return fullResponse; } From 8c6c75d9fde0abc991142f67d14a964eaffcf684 Mon Sep 17 00:00:00 2001 From: Isabelle Giguere Date: Mon, 22 Dec 2025 11:26:08 -0500 Subject: [PATCH 10/15] SOLR/17136: address review comments, rename methods for clarity --- .../client/api/model/NodeSystemResponse.java | 16 +++++----------- .../org/apache/solr/cli/HealthcheckTool.java | 7 +++++-- .../src/java/org/apache/solr/cli/StatusTool.java | 8 ++++++-- .../handler/admin/AdminHandlersProxyTest.java | 4 ++-- .../solrj/response/SystemInfoResponse.java | 6 +++--- 5 files changed, 21 insertions(+), 20 deletions(-) diff --git a/solr/api/src/java/org/apache/solr/client/api/model/NodeSystemResponse.java b/solr/api/src/java/org/apache/solr/client/api/model/NodeSystemResponse.java index 83354402673..09bc35f0867 100644 --- a/solr/api/src/java/org/apache/solr/client/api/model/NodeSystemResponse.java +++ b/solr/api/src/java/org/apache/solr/client/api/model/NodeSystemResponse.java @@ -17,7 +17,6 @@ package org.apache.solr.client.api.model; import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.OptBoolean; import java.util.Date; import java.util.List; import java.util.Map; @@ -34,13 +33,12 @@ public class NodeSystemResponse extends SolrJerseyResponse { @JsonProperty("core_root") public String coreRoot; - @JsonProperty(isRequired = OptBoolean.FALSE) - public String environment; + @JsonProperty public String environment; - @JsonProperty(value = "environment_label", isRequired = OptBoolean.FALSE) + @JsonProperty(value = "environment_label") public String environmentLabel; - @JsonProperty(value = "environment_color", isRequired = OptBoolean.FALSE) + @JsonProperty(value = "environment_color") public String environmentColor; @JsonProperty public String node; @@ -107,12 +105,8 @@ public static class MemoryRaw { } public static class Vendor { - @JsonProperty(isRequired = OptBoolean.FALSE) - public String name; - - @JsonProperty(isRequired = OptBoolean.FALSE) - public String vendor; - + @JsonProperty public String name; + @JsonProperty public String vendor; @JsonProperty public String version; } diff --git a/solr/core/src/java/org/apache/solr/cli/HealthcheckTool.java b/solr/core/src/java/org/apache/solr/cli/HealthcheckTool.java index 0cf385cf22f..8745941cff3 100644 --- a/solr/core/src/java/org/apache/solr/cli/HealthcheckTool.java +++ b/solr/core/src/java/org/apache/solr/cli/HealthcheckTool.java @@ -167,8 +167,11 @@ protected void runCloudTool(CloudSolrClient cloudSolrClient, CommandLine cli) th CLIUtils.getSolrClient( r.getBaseUrl(), cli.getOptionValue(CommonCLIOptions.CREDENTIALS_OPTION))) { SystemInfoResponse sysResponse = (new SystemInfoRequest()).process(solrClient); - uptime = SolrCLI.uptime(sysResponse.getJVMUpTime()); - memory = sysResponse.getJVMMemoryUsed() + " of " + sysResponse.getJVMMemoryTtl(); + uptime = SolrCLI.uptime(sysResponse.getJVMUpTimeMillis()); + memory = + sysResponse.getHumanReadableJVMMemoryUsed() + + " of " + + sysResponse.getHumanReadableJVMMemoryTotal(); } // if we get here, we can trust the state diff --git a/solr/core/src/java/org/apache/solr/cli/StatusTool.java b/solr/core/src/java/org/apache/solr/cli/StatusTool.java index cbe17f66d43..9fee5940a17 100644 --- a/solr/core/src/java/org/apache/solr/cli/StatusTool.java +++ b/solr/core/src/java/org/apache/solr/cli/StatusTool.java @@ -303,9 +303,13 @@ public static Map reportStatus(SolrClient solrClient) throws Exc status.put("version", sysResponse.getSolrImplVersion()); status.put("startTime", sysResponse.getJVMStartTime()); - status.put("uptime", sysResponse.getJVMUpTime()); + status.put("uptime", sysResponse.getJVMUpTimeMillis()); - status.put("memory", sysResponse.getJVMMemoryUsed() + " of " + sysResponse.getJVMMemoryTtl()); + status.put( + "memory", + sysResponse.getHumanReadableJVMMemoryUsed() + + " of " + + sysResponse.getHumanReadableJVMMemoryTotal()); // if this is a Solr in solrcloud mode, gather some basic cluster info if ("solrcloud".equals(sysResponse.getMode())) { diff --git a/solr/core/src/test/org/apache/solr/handler/admin/AdminHandlersProxyTest.java b/solr/core/src/test/org/apache/solr/handler/admin/AdminHandlersProxyTest.java index 965df4fdb82..a3b82ac5934 100644 --- a/solr/core/src/test/org/apache/solr/handler/admin/AdminHandlersProxyTest.java +++ b/solr/core/src/test/org/apache/solr/handler/admin/AdminHandlersProxyTest.java @@ -86,8 +86,8 @@ public void proxySystemInfoHandlerOneNode() { SystemInfoResponse rsp = req.process(solrClient, null); NamedList nl = rsp.getResponse(); assertEquals(2, nl.size()); - assertEquals("solrcloud", ((NamedList) nl.get(nl.getName(1))).get("mode")); - assertEquals(nl.getName(1), ((NamedList) nl.get(nl.getName(1))).get("node")); + assertEquals("solrcloud", rsp.getMode()); + assertEquals(nl.getName(1), rsp.getNode()); } catch (Exception e) { fail("Exception while proxying request to node " + node); } diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/response/SystemInfoResponse.java b/solr/solrj/src/java/org/apache/solr/client/solrj/response/SystemInfoResponse.java index a09fc4e623c..65d0b980d1b 100644 --- a/solr/solrj/src/java/org/apache/solr/client/solrj/response/SystemInfoResponse.java +++ b/solr/solrj/src/java/org/apache/solr/client/solrj/response/SystemInfoResponse.java @@ -96,7 +96,7 @@ && getFullResponse().jvm.jmx != null : null; } - public Long getJVMUpTime() { + public Long getJVMUpTimeMillis() { return getFullResponse() != null && getFullResponse().jvm != null && getFullResponse().jvm.jmx != null @@ -104,7 +104,7 @@ && getFullResponse().jvm.jmx != null : null; } - public String getJVMMemoryUsed() { + public String getHumanReadableJVMMemoryUsed() { return getFullResponse() != null && getFullResponse().jvm != null && getFullResponse().jvm.memory != null @@ -112,7 +112,7 @@ && getFullResponse().jvm.memory != null : null; } - public String getJVMMemoryTtl() { + public String getHumanReadableJVMMemoryTotal() { return getFullResponse() != null && getFullResponse().jvm != null && getFullResponse().jvm.memory != null From edc6b23e5053299aa92f909d3d5479884230effc Mon Sep 17 00:00:00 2001 From: Eric Pugh Date: Sat, 27 Dec 2025 14:13:29 -0500 Subject: [PATCH 11/15] Small lint clean up. --- .../org/apache/solr/handler/admin/AdminHandlersProxyTest.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/solr/core/src/test/org/apache/solr/handler/admin/AdminHandlersProxyTest.java b/solr/core/src/test/org/apache/solr/handler/admin/AdminHandlersProxyTest.java index a3b82ac5934..14411a38876 100644 --- a/solr/core/src/test/org/apache/solr/handler/admin/AdminHandlersProxyTest.java +++ b/solr/core/src/test/org/apache/solr/handler/admin/AdminHandlersProxyTest.java @@ -20,9 +20,7 @@ import java.io.IOException; import java.util.Collections; import java.util.Set; -import org.apache.http.client.HttpClient; import org.apache.solr.client.solrj.SolrServerException; -import org.apache.solr.client.solrj.apache.CloudLegacySolrClient; import org.apache.solr.client.solrj.impl.CloudSolrClient; import org.apache.solr.client.solrj.request.SystemInfoRequest; import org.apache.solr.client.solrj.response.SystemInfoResponse; @@ -35,7 +33,6 @@ import org.junit.Test; public class AdminHandlersProxyTest extends SolrCloudTestCase { - private HttpClient httpClient; private CloudSolrClient solrClient; @BeforeClass @@ -49,7 +46,6 @@ public static void setupCluster() throws Exception { public void setUp() throws Exception { super.setUp(); solrClient = cluster.getSolrClient(); - httpClient = ((CloudLegacySolrClient) solrClient).getHttpClient(); } @Test From 74325ac9b0ebee4e15ee10a152bbc8e05d71e3ac Mon Sep 17 00:00:00 2001 From: Isabelle Giguere Date: Sat, 27 Dec 2025 15:18:38 -0500 Subject: [PATCH 12/15] SOLR-17136: adjust response mapping for single or multi nodes --- .../solrj/response/SystemInfoResponse.java | 199 +++++++++++++++--- .../response/SystemInfoResponseTest.java | 83 ++++++++ 2 files changed, 247 insertions(+), 35 deletions(-) create mode 100644 solr/solrj/src/test/org/apache/solr/client/solrj/response/SystemInfoResponseTest.java diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/response/SystemInfoResponse.java b/solr/solrj/src/java/org/apache/solr/client/solrj/response/SystemInfoResponse.java index 65d0b980d1b..8dbea63118b 100644 --- a/solr/solrj/src/java/org/apache/solr/client/solrj/response/SystemInfoResponse.java +++ b/solr/solrj/src/java/org/apache/solr/client/solrj/response/SystemInfoResponse.java @@ -18,6 +18,10 @@ import java.lang.invoke.MethodHandles; import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; import org.apache.solr.client.api.model.NodeSystemResponse; import org.apache.solr.client.solrj.request.json.JacksonContentWriter; import org.apache.solr.common.util.NamedList; @@ -31,7 +35,7 @@ public class SystemInfoResponse extends SolrResponseBase { private static final long serialVersionUID = 1L; - private NodeSystemResponse fullResponse; + private final Map nodesInfo = new HashMap<>(); public SystemInfoResponse(NamedList namedList) { if (namedList == null) throw new IllegalArgumentException("Null NamedList is not allowed."); @@ -41,82 +45,207 @@ public SystemInfoResponse(NamedList namedList) { @Override public void setResponse(NamedList response) { if (getResponse() == null) super.setResponse(response); - if (fullResponse == null) { - try { - fullResponse = - JacksonContentWriter.DEFAULT_MAPPER.convertValue( - getResponse(), NodeSystemResponse.class); - } catch (Throwable t) { - log.error("Cannot convert NamedList response to API model NodeSystemResponse", t); + if (getResponse().get("node") == null) { + // multi-nodes response, NamedList of "host:port_solr"->NodeSystemResponse + for (Entry node : getResponse()) { + if (node.getKey().endsWith("_solr")) { + nodesInfo.put( + node.getKey(), + JacksonContentWriter.DEFAULT_MAPPER.convertValue( + node.getValue(), NodeSystemResponse.class)); + } } + } else { + // single-node response + nodesInfo.put( + getResponse().get("node").toString(), + JacksonContentWriter.DEFAULT_MAPPER.convertValue( + getResponse(), NodeSystemResponse.class)); } } + /** Get the mode from a single node system info */ public String getMode() { - return getFullResponse().mode; + if (nodesInfo.size() == 1) { + return nodesInfo.values().stream().findFirst().orElseThrow().mode; + } else { + throw new UnsupportedOperationException( + "Multiple nodes system info available, use method 'getAllModes', or 'getModeForNode(String)'."); + } + } + + /** Get all modes, per node */ + public Map getAllModes() { + Map allModes = new HashMap<>(); + nodesInfo.entrySet().stream().forEach(e -> allModes.put(e.getKey(), e.getValue().mode)); + return allModes; + } + + /** Get the mode for the given node name */ + public String getModeForNode(String node) { + return nodesInfo.get(node).mode; } + /** Get the ZK host from a single node system info */ public String getZkHost() { - return getFullResponse().zkHost; + if (nodesInfo.size() == 1) { + return nodesInfo.values().stream().findFirst().orElseThrow().zkHost; + } else { + throw new UnsupportedOperationException( + "Multiple nodes system info available, use method 'getAllZkHosts', or 'getZkHostForNode(String)'."); + } + } + + /** Get all ZK hosts, per node */ + public Map getAllZkHosts() { + Map allModes = new HashMap<>(); + nodesInfo.entrySet().stream().forEach(e -> allModes.put(e.getKey(), e.getValue().zkHost)); + return allModes; } + /** Get the ZK host for the given node name */ + public String getZkHostForNode(String node) { + return nodesInfo.get(node).zkHost; + } + + /** Get the Solr home from a single node system info */ public String getSolrHome() { - return getFullResponse().solrHome; + if (nodesInfo.size() == 1) { + return nodesInfo.values().stream().findFirst().orElseThrow().solrHome; + } else { + throw new UnsupportedOperationException( + "Multiple nodes system info available, use method 'getAllSolrHomes', or 'getSolrHomeForNode(String)'."); + } + } + + /** Get all Solr homes, per node */ + public Map getAllSolrHomes() { + Map allModes = new HashMap<>(); + nodesInfo.entrySet().stream().forEach(e -> allModes.put(e.getKey(), e.getValue().solrHome)); + return allModes; + } + + /** Get the Solr home for the given node name */ + public String getSolrHomeForNode(String node) { + return nodesInfo.get(node).solrHome; } + /** Get the core root from a single node system info */ public String getCoreRoot() { - return getFullResponse().coreRoot; + if (nodesInfo.size() == 1) { + return nodesInfo.values().stream().findFirst().orElseThrow().coreRoot; + } else { + throw new UnsupportedOperationException( + "Multiple nodes system info available, use method 'getAllCoreRoots', or 'getCoreRootForNode(String)'."); + } } + /** Get all core roots, per node */ + public Map getAllCoreRoots() { + Map allModes = new HashMap<>(); + nodesInfo.entrySet().stream().forEach(e -> allModes.put(e.getKey(), e.getValue().coreRoot)); + return allModes; + } + + /** Get the core root for the given node name */ + public String getCoreRootForNode(String node) { + return nodesInfo.get(node).coreRoot; + } + + /** Get the node name from a single node system info */ public String getNode() { - return getFullResponse().node; + if (nodesInfo.size() == 1) { + return nodesInfo.values().stream().findFirst().orElseThrow().node; + } else { + throw new UnsupportedOperationException( + "Multiple nodes system info available, use method 'getAllNodes', or 'getNodeForSolrHome(String)', or 'getNodeForCoreRoot(String)'."); + } + } + + /** Get all nodes names */ + public Set getAllNodes() { + return nodesInfo.keySet(); + } + + /** Get the node name for the given Solr home */ + public String getNodeForSolrHome(String solrHome) { + return nodesInfo.values().stream() + .filter(v -> solrHome.equals(v.solrHome)) + .map(v -> v.node) + .findFirst() + .get(); + } + + /** Get the node name for the given core root */ + public String getNodeForCoreRoot(String coreRoot) { + return nodesInfo.values().stream() + .filter(v -> coreRoot.equals(v.coreRoot)) + .map(v -> v.node) + .findFirst() + .get(); + } + + /** Get the {@code NodeSystemResponse} for a single node */ + public NodeSystemResponse getNodeResponse() { + if (nodesInfo.size() == 1) { + return nodesInfo.values().stream().findFirst().get(); + } else { + throw new UnsupportedOperationException( + "Multiple nodes system info available, use method 'getAllNodeResponses', or 'getNodeResponseForNode(String)'."); + } + } + + /** Get all {@code NodeSystemResponse}s */ + public Map getAllNodeResponses() { + return nodesInfo; } - public NodeSystemResponse getFullResponse() { - return fullResponse; + /** Get the {@code NodeSystemResponse} for the given node name */ + public NodeSystemResponse getNodeResponseForNode(String node) { + return nodesInfo.get(node); } public String getSolrImplVersion() { - return getFullResponse() != null && getFullResponse().lucene != null - ? getFullResponse().lucene.solrImplVersion + return getNodeResponse() != null && getNodeResponse().lucene != null + ? getNodeResponse().lucene.solrImplVersion : null; } public String getSolrSpecVersion() { - return getFullResponse() != null && getFullResponse().lucene != null - ? getFullResponse().lucene.solrSpecVersion + return getNodeResponse() != null && getNodeResponse().lucene != null + ? getNodeResponse().lucene.solrSpecVersion : null; } public Date getJVMStartTime() { - return getFullResponse() != null - && getFullResponse().jvm != null - && getFullResponse().jvm.jmx != null - ? getFullResponse().jvm.jmx.startTime + return getNodeResponse() != null + && getNodeResponse().jvm != null + && getNodeResponse().jvm.jmx != null + ? getNodeResponse().jvm.jmx.startTime : null; } public Long getJVMUpTimeMillis() { - return getFullResponse() != null - && getFullResponse().jvm != null - && getFullResponse().jvm.jmx != null - ? getFullResponse().jvm.jmx.upTimeMS + return getNodeResponse() != null + && getNodeResponse().jvm != null + && getNodeResponse().jvm.jmx != null + ? getNodeResponse().jvm.jmx.upTimeMS : null; } public String getHumanReadableJVMMemoryUsed() { - return getFullResponse() != null - && getFullResponse().jvm != null - && getFullResponse().jvm.memory != null - ? getFullResponse().jvm.memory.used + return getNodeResponse() != null + && getNodeResponse().jvm != null + && getNodeResponse().jvm.memory != null + ? getNodeResponse().jvm.memory.used : null; } public String getHumanReadableJVMMemoryTotal() { - return getFullResponse() != null - && getFullResponse().jvm != null - && getFullResponse().jvm.memory != null - ? getFullResponse().jvm.memory.total + return getNodeResponse() != null + && getNodeResponse().jvm != null + && getNodeResponse().jvm.memory != null + ? getNodeResponse().jvm.memory.total : null; } } diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/response/SystemInfoResponseTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/response/SystemInfoResponseTest.java new file mode 100644 index 00000000000..e22a1b2a723 --- /dev/null +++ b/solr/solrj/src/test/org/apache/solr/client/solrj/response/SystemInfoResponseTest.java @@ -0,0 +1,83 @@ +/* + * 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.response; + +import java.io.IOException; +import java.util.Map; +import org.apache.solr.client.solrj.SolrServerException; +import org.apache.solr.client.solrj.impl.CloudSolrClient; +import org.apache.solr.client.solrj.request.SystemInfoRequest; +import org.apache.solr.cloud.SolrCloudTestCase; +import org.apache.solr.common.params.MapSolrParams; +import org.junit.Assert; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +public class SystemInfoResponseTest extends SolrCloudTestCase { + + private CloudSolrClient solrClient; + + @BeforeClass + public static void setupCluster() throws Exception { + configureCluster(2) + //.withJettyConfig(jettyCfg -> jettyCfg.enableV2(true)) + .addConfig("config", getFile("solrj/solr/collection1/conf")) + .configure(); + } + + @Before + @Override + public void setUp() throws Exception { + super.setUp(); + solrClient = cluster.getSolrClient(); + } + + @Test + public void testAllNodesResponse() throws SolrServerException, IOException { + MapSolrParams params = new MapSolrParams(Map.of("nodes", "all")); + + SystemInfoRequest req = new SystemInfoRequest(params); + SystemInfoResponse rsp = req.process(solrClient); + + try { + rsp.getNodeResponse(); + Assert.fail("Should throw UnsupportedOperationException"); + } catch (UnsupportedOperationException e) { + Assert.assertTrue(e.getMessage().startsWith("Multiple nodes system info available")); + } + + Assert.assertEquals(2, rsp.getAllNodeResponses().size()); + Assert.assertEquals(2, rsp.getAllCoreRoots().size()); + Assert.assertEquals(2, rsp.getAllModes().size()); + } + + @Test + public void testResponseForGivenNode() throws SolrServerException, IOException { + MapSolrParams params = new MapSolrParams(Map.of("nodes", "all")); + + SystemInfoRequest req = new SystemInfoRequest(params); + SystemInfoResponse rsp = req.process(solrClient); + + for (String node : rsp.getAllNodes()) { + String coreRoot = rsp.getCoreRootForNode(node); + Assert.assertEquals(node, rsp.getNodeForCoreRoot(coreRoot)); + String solrHome = rsp.getCoreRootForNode(node); + Assert.assertEquals(node, rsp.getNodeForSolrHome(solrHome)); + } + } +} From a6beb5c9807be3b66cbe3aa58df123c05622b0a7 Mon Sep 17 00:00:00 2001 From: Jason Gerlowski Date: Sun, 28 Dec 2025 11:22:17 -0500 Subject: [PATCH 13/15] Fix tidy --- .../solr/client/solrj/response/SystemInfoResponseTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/response/SystemInfoResponseTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/response/SystemInfoResponseTest.java index e22a1b2a723..5d603aca378 100644 --- a/solr/solrj/src/test/org/apache/solr/client/solrj/response/SystemInfoResponseTest.java +++ b/solr/solrj/src/test/org/apache/solr/client/solrj/response/SystemInfoResponseTest.java @@ -35,7 +35,6 @@ public class SystemInfoResponseTest extends SolrCloudTestCase { @BeforeClass public static void setupCluster() throws Exception { configureCluster(2) - //.withJettyConfig(jettyCfg -> jettyCfg.enableV2(true)) .addConfig("config", getFile("solrj/solr/collection1/conf")) .configure(); } From 0c65369aeb24ae1df7f2a40c8f2cbf0f6f39820b Mon Sep 17 00:00:00 2001 From: Jason Gerlowski Date: Sun, 28 Dec 2025 11:24:27 -0500 Subject: [PATCH 14/15] Fix tidy again --- .../solr/client/solrj/response/SystemInfoResponseTest.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/response/SystemInfoResponseTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/response/SystemInfoResponseTest.java index 5d603aca378..f129d63f10e 100644 --- a/solr/solrj/src/test/org/apache/solr/client/solrj/response/SystemInfoResponseTest.java +++ b/solr/solrj/src/test/org/apache/solr/client/solrj/response/SystemInfoResponseTest.java @@ -34,9 +34,7 @@ public class SystemInfoResponseTest extends SolrCloudTestCase { @BeforeClass public static void setupCluster() throws Exception { - configureCluster(2) - .addConfig("config", getFile("solrj/solr/collection1/conf")) - .configure(); + configureCluster(2).addConfig("config", getFile("solrj/solr/collection1/conf")).configure(); } @Before From af10b785dc5b8632d8a0e3a33a829610043e9e70 Mon Sep 17 00:00:00 2001 From: Jason Gerlowski Date: Sun, 28 Dec 2025 12:50:42 -0500 Subject: [PATCH 15/15] Tweak changelog entry to highlight end-user value --- .../unreleased/SOLR-17136-replace-GenericSolrRequest.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/changelog/unreleased/SOLR-17136-replace-GenericSolrRequest.yml b/changelog/unreleased/SOLR-17136-replace-GenericSolrRequest.yml index 13912dc89eb..4d20d1a613f 100644 --- a/changelog/unreleased/SOLR-17136-replace-GenericSolrRequest.yml +++ b/changelog/unreleased/SOLR-17136-replace-GenericSolrRequest.yml @@ -1,7 +1,7 @@ -title: Replace most uses of GenericSolrRequest in Solr code -type: changed +title: Introduce new SolrJ SolrRequest classes for metrics and "system info" requests. +type: added authors: - name: Isabelle Giguère links: - name: SOLR-17136 - url: https://issues.apache.org/jira/browse/SOLR-17136 \ No newline at end of file + url: https://issues.apache.org/jira/browse/SOLR-17136