Skip to content
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
80e2582
SOLR-17136 initial commit: classes SystemInfoRequest and
Dec 12, 2025
226448b
SOLR-17136: integrate SystemInfoRequest/Response
Dec 14, 2025
f79c5ae
Merge branch 'apache:main' into SOLR-17136-replace-GenericSolrRequest
igiguere Dec 16, 2025
2470367
SOLR-17136: replace GenericSolrRequest
Dec 16, 2025
eec0643
SOLR-17136: add changelog file
Dec 16, 2025
e219435
SOLR-17136: more replacements of GenericSolrRequest
Dec 17, 2025
74df237
Merge branch 'apache:main' into SOLR-17136-replace-GenericSolrRequest
igiguere Dec 17, 2025
0c11fc1
Merge branch 'SOLR-17136-replace-GenericSolrRequest' of git@github.co…
Dec 17, 2025
712ce54
Merge branch 'apache:main' into SOLR-17136-replace-GenericSolrRequest
igiguere Dec 17, 2025
955fc46
SOLR-17136: address code review comments. Remove permissive constructors
Dec 18, 2025
f09182d
SOLR-17136: Adjust SystemInfoResponse with new "gpu" field
Dec 18, 2025
bd60a18
SOLR-17136: address review comments
Dec 19, 2025
d335770
SOLR-17136: rename API model SystemInfoResponse to NodeSystemResponse
Dec 19, 2025
5b106e7
Merge remote-tracking branch 'upstream/main' into pr/3955
epugh Dec 20, 2025
eae6b7a
Merge branch 'apache:main' into SOLR-17136-replace-GenericSolrRequest
igiguere Dec 22, 2025
8c6c75d
SOLR/17136: address review comments, rename methods for clarity
Dec 22, 2025
be9e779
Merge branch 'apache:main' into SOLR-17136-replace-GenericSolrRequest
igiguere Dec 24, 2025
edc6b23
Small lint clean up.
epugh Dec 27, 2025
74325ac
SOLR-17136: adjust response mapping for single or multi nodes
Dec 27, 2025
a8451d6
Merge branch 'SOLR-17136-replace-GenericSolrRequest' of git@github.co…
Dec 27, 2025
4507494
Merge branch 'apache:main' into SOLR-17136-replace-GenericSolrRequest
igiguere Dec 27, 2025
a6beb5c
Fix tidy
gerlowskija Dec 28, 2025
0c65369
Fix tidy again
gerlowskija Dec 28, 2025
af10b78
Tweak changelog entry to highlight end-user value
gerlowskija Dec 28, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
/*
* 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;

/** Response from /node/system */
public class NodeSystemResponse extends SolrJerseyResponse {

@JsonProperty public String mode;
@JsonProperty public String zkHost;

@JsonProperty("solr_home")
public String solrHome;

@JsonProperty("core_root")
public String coreRoot;

@JsonProperty(isRequired = OptBoolean.FALSE)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Q] I'm a little confused by these isRequired = OptBoolean.FALSE bits. My understanding was that these annotations defaulted to treat properties as "non-required" unless otherwise specified. Is there a purpose to agreeing with the default so explicitly?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess I was being too cautious. I can remove the explicit setting.

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<String, String> system;

/** /node/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<String> roles;
@JsonProperty public List<String> permissions;
}

/** /node/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;
}

/** /node/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 extends MemoryRaw {
@JsonProperty public long max;

@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;

@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<String> commandLineArgs;
}

public static class GPU {
@JsonProperty public boolean available;
@JsonProperty public long count;
@JsonProperty public MemoryRaw memory;
@JsonProperty Map<String, Object> devices;
}
}
18 changes: 5 additions & 13 deletions solr/core/src/java/org/apache/solr/cli/CLIUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -38,18 +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;

Expand Down Expand Up @@ -249,12 +247,7 @@ public static String getZkHost(CommandLine cli) throws Exception {

try (SolrClient solrClient = getSolrClient(cli)) {
// hit Solr to get system info
NamedList<Object> systemInfo =
solrClient.request(
new GenericSolrRequest(SolrRequest.METHOD.GET, CommonParams.SYSTEM_INFO_PATH));

// convert raw JSON into user-friendly output
Map<String, Object> status = StatusTool.reportStatus(systemInfo, solrClient);
Map<String, Object> status = StatusTool.reportStatus(solrClient);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe for future, but is it odd a CLIUtils would call StatusTool for a method reportStatus? Should it be moved to CLIUtils instead and the StatusTool should call CLIUtils.reportStatus?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are 17 calls to CLIUtils.getZkHost(CommandLine), where we find this call to StatusTool.reportStatus(SolrClient).
That's half the *Tool found in org.apache.solr.cli. That means CLIUtils serves as a "bridge" between tools (StatusTool and the rest of them).
Personally, I think it makes sense that the StatusTool would be the one reporting the status, rather than have it depend on the CLIUtils to essentially do the status reporting job (i.e.: CLIUtils.reportStatus).
Otherwise, if we don't want CLIUtils to call StatusTool, then, we have to add a call to StatusTool.reportStatus(SolrClient) in each of the 17 locations, and for each, find the ZK host currently provided by CLIUtils.getZkHost(CommandLine).

In a nutchell : I would not change this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

makes sense, thanks for digging into this.

@SuppressWarnings("unchecked")
Map<String, Object> cloud = (Map<String, Object>) status.get("cloud");
if (cloud != null) {
Expand Down Expand Up @@ -357,9 +350,8 @@ public static boolean safeCheckCoreExists(String solrUrl, String coreName, Strin
}

public static boolean isCloudMode(SolrClient solrClient) throws SolrServerException, IOException {
NamedList<Object> systemInfo =
solrClient.request(new GenericSolrRequest(SolrRequest.METHOD.GET, SYSTEM_INFO_PATH));
return "solrcloud".equals(systemInfo.get("mode"));
SystemInfoResponse sysResponse = new SystemInfoRequest().process(solrClient);
return "solrcloud".equals(sysResponse.getMode());
}

public static Path getConfigSetsDir(Path solrInstallDir) {
Expand Down
16 changes: 5 additions & 11 deletions solr/core/src/java/org/apache/solr/cli/CreateTool.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,18 +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;
Expand Down Expand Up @@ -157,14 +156,9 @@ 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));

// convert raw JSON into user-friendly output
coreRootDirectory = (String) systemInfo.get("core_root");
SystemInfoResponse sysResponse = (new SystemInfoRequest()).process(solrClient);
// usually same as solr home, but not always
String coreRootDirectory = sysResponse.getCoreRoot();

if (CLIUtils.safeCheckCoreExists(
solrUrl, coreName, cli.getOptionValue(CommonCLIOptions.CREDENTIALS_OPTION))) {
Expand Down
18 changes: 5 additions & 13 deletions solr/core/src/java/org/apache/solr/cli/HealthcheckTool.java
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lovely change!

Original file line number Diff line number Diff line change
Expand Up @@ -30,18 +30,16 @@
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;
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;
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;
Expand Down Expand Up @@ -168,15 +166,9 @@ protected void runCloudTool(CloudSolrClient cloudSolrClient, CommandLine cli) th
try (var solrClient =
CLIUtils.getSolrClient(
r.getBaseUrl(), cli.getOptionValue(CommonCLIOptions.CREDENTIALS_OPTION))) {
NamedList<Object> 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 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
Expand Down
43 changes: 13 additions & 30 deletions solr/core/src/java/org/apache/solr/cli/StatusTool.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +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.common.params.CommonParams;
import org.apache.solr.client.solrj.request.SystemInfoRequest;
import org.apache.solr.client.solrj.response.SystemInfoResponse;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.common.util.URLUtil;
import org.noggit.CharArr;
Expand Down Expand Up @@ -292,42 +291,26 @@ public Map<String, Object> waitToSeeSolrUp(

public Map<String, Object> getStatus(String solrUrl, String credentials) throws Exception {
try (var solrClient = CLIUtils.getSolrClient(solrUrl, credentials)) {
return getStatus(solrClient);
Map<String, Object> status = reportStatus(solrClient);
return status;
}
}

public Map<String, Object> getStatus(SolrClient solrClient) throws Exception {
Map<String, Object> status;

NamedList<Object> 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<String, Object> reportStatus(NamedList<Object> info, SolrClient solrClient)
throws Exception {
public static Map<String, Object> reportStatus(SolrClient solrClient) throws Exception {
Map<String, Object> status = new LinkedHashMap<>();
SystemInfoResponse sysResponse = (new SystemInfoRequest()).process(solrClient);
status.put("solr_home", sysResponse.getSolrHome() != null ? sysResponse.getSolrHome() : "?");
status.put("version", sysResponse.getSolrImplVersion());

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)));
status.put("startTime", sysResponse.getJVMStartTime());
status.put("uptime", sysResponse.getJVMUpTime());

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()));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LIkewise in a future pr, would be nice to figure out is it cloud or solrcloud and use a single term everywhere ;-)

}

return status;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand Down
Loading