This is an automated email from the ASF dual-hosted git repository.
janhoy pushed a commit to branch branch_9x
in repository https://gitbox.apache.org/repos/asf/solr.git
The following commit(s) were added to refs/heads/branch_9x by this push:
new 58c7218f514 SOLR-17717 Use Powershell instead of WMIC to get cmdline
on Windows (backport 9x) (#3331)
58c7218f514 is described below
commit 58c7218f51436e3b24a88109468094fb197dafb7
Author: Jan Høydahl <[email protected]>
AuthorDate: Mon Aug 4 15:25:38 2025 +0200
SOLR-17717 Use Powershell instead of WMIC to get cmdline on Windows
(backport 9x) (#3331)
(cherry picked from commit 6315d3fe2ca46fe7a30a33f4b8deea906d7090c0)
---
solr/CHANGES.txt | 2 +
.../org/apache/solr/cli/SolrProcessManager.java | 93 ++++++++++++++--------
.../apache/solr/cli/SolrProcessManagerTest.java | 11 +++
3 files changed, 72 insertions(+), 34 deletions(-)
diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index f65dab8dd9a..f44ec86509f 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -21,6 +21,8 @@ Bug Fixes
---------------------
* SOLR-17824: RecoveryStrategy.pingLeader could NPE when there's no shard
leader (David Smiley)
+* SOLR-17717: Starting solr on newer Windows 11 Home complained about missing
wmic (Jan Høydahl)
+
* SOLR-17721: NPE can occur when doing Atomic Update using Add Distinct on
documents with a null field value. (puneetSharma via Eric Pugh)
* SOLR-17789: When Solr forwards/proxies requests to another node that can
service the request, it needs to pass authorization headers.
diff --git a/solr/core/src/java/org/apache/solr/cli/SolrProcessManager.java
b/solr/core/src/java/org/apache/solr/cli/SolrProcessManager.java
index 42b38b2e7a8..fb63dc0f99d 100644
--- a/solr/core/src/java/org/apache/solr/cli/SolrProcessManager.java
+++ b/solr/core/src/java/org/apache/solr/cli/SolrProcessManager.java
@@ -18,9 +18,10 @@ package org.apache.solr.cli;
import static
org.apache.solr.servlet.SolrDispatchFilter.SOLR_INSTALL_DIR_ATTRIBUTE;
-import java.io.BufferedReader;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
-import java.io.InputStreamReader;
import java.lang.invoke.MethodHandles;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
@@ -29,6 +30,7 @@ import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@@ -36,6 +38,7 @@ import java.util.Optional;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
+import org.apache.commons.io.IOUtils;
import org.apache.lucene.util.Constants;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.util.EnvUtils;
@@ -53,8 +56,12 @@ public class SolrProcessManager {
// Set this to true during testing to allow the SolrProcessManager to find
only mock Solr
// processes
public static boolean enableTestingMode = false;
+ private static final Map<Long, String> pidToWindowsCommandLineMap = new
HashMap<>();
public SolrProcessManager() {
+ if (Constants.WINDOWS) {
+ pidToWindowsCommandLineMap.putAll(commandLinesWindows());
+ }
pidProcessMap =
ProcessHandle.allProcesses()
.filter(p -> p.info().command().orElse("").contains("java"))
@@ -152,8 +159,8 @@ public class SolrProcessManager {
}
/**
- * Gets the command line of a process as a string. This is a workaround for
the fact that
- * ProcessHandle.info().command() is not (yet) implemented on Windows.
+ * Gets the command line of a process as a string. For Windows we need to
fetch command lines
+ * using a PowerShell command.
*
* @param ph the process handle
* @return the command line of the process
@@ -162,42 +169,60 @@ public class SolrProcessManager {
if (!Constants.WINDOWS) {
return ph.info().commandLine();
} else {
- long desiredProcessid = ph.pid();
- try {
- Process process =
- new ProcessBuilder(
- "wmic",
- "process",
- "where",
- "ProcessID=" + desiredProcessid,
- "get",
- "commandline",
- "/format:list")
- .redirectErrorStream(true)
- .start();
- try (InputStreamReader inputStreamReader =
- new InputStreamReader(process.getInputStream(),
StandardCharsets.UTF_8);
- BufferedReader reader = new BufferedReader(inputStreamReader)) {
- while (true) {
- String line = reader.readLine();
- if (line == null) {
- return Optional.empty();
- }
- if (!line.startsWith("CommandLine=")) {
- continue;
- }
- return Optional.of(line.substring("CommandLine=".length()));
- }
- }
- } catch (IOException e) {
+ return Optional.ofNullable(pidToWindowsCommandLineMap.get(ph.pid()));
+ }
+ }
+
+ /**
+ * Gets the command lines of all java processes on Windows using PowerShell.
+ *
+ * @return a map of process IDs to command lines
+ */
+ private static Map<Long, String> commandLinesWindows() {
+ try {
+ Process process =
+ new ProcessBuilder(
+ "powershell.exe",
+ "-Command",
+ "Get-CimInstance -ClassName Win32_Process | Where-Object {
$_.Name -like '*java*' } | Select-Object ProcessId, CommandLine |
ConvertTo-Json -Depth 1")
+ .redirectErrorStream(true)
+ .start();
+ String output = IOUtils.toString(process.getInputStream(),
StandardCharsets.UTF_8);
+ int exitCode = process.waitFor();
+ if (exitCode != 0) {
+ String errorText = IOUtils.toString(process.getErrorStream(),
StandardCharsets.UTF_8);
throw new SolrException(
SolrException.ErrorCode.SERVER_ERROR,
- "Error getting command line for process " + desiredProcessid,
- e);
+ "Error getting command lines for Windows: " + errorText);
}
+ return parseWindowsPidToCommandLineJson(output);
+ } catch (IOException e) {
+ throw new SolrException(
+ SolrException.ErrorCode.SERVER_ERROR, "Error getting command lines
for Windows");
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw new SolrException(
+ SolrException.ErrorCode.SERVER_ERROR,
+ "Interrupted while getting command lines for Windows");
}
}
+ static Map<Long, String> parseWindowsPidToCommandLineJson(String jsonString)
+ throws JsonProcessingException {
+ // Json format: [{"ProcessId": 1234, "CommandLine": "java foo"}]
+ ObjectMapper mapper = new ObjectMapper();
+ List<WindowsProcessInfo> processInfoList =
+ mapper.readValue(jsonString, new TypeReference<>() {});
+ return processInfoList.stream()
+ .filter(p -> p.CommandLine != null)
+ .collect(Collectors.toMap(p -> p.ProcessId, p -> p.CommandLine));
+ }
+
+ public static class WindowsProcessInfo {
+ public long ProcessId;
+ public String CommandLine;
+ }
+
/**
* Gets the arguments of a process as a list of strings. With workaround for
Windows.
*
diff --git a/solr/core/src/test/org/apache/solr/cli/SolrProcessManagerTest.java
b/solr/core/src/test/org/apache/solr/cli/SolrProcessManagerTest.java
index dca7e685a46..4655ebab2ab 100644
--- a/solr/core/src/test/org/apache/solr/cli/SolrProcessManagerTest.java
+++ b/solr/core/src/test/org/apache/solr/cli/SolrProcessManagerTest.java
@@ -16,6 +16,7 @@
*/
package org.apache.solr.cli;
+import com.fasterxml.jackson.core.JsonProcessingException;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
@@ -28,6 +29,7 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Collection;
+import java.util.Map;
import java.util.stream.Collectors;
import org.apache.commons.math3.util.Pair;
import org.apache.solr.SolrTestCase;
@@ -176,6 +178,15 @@ public class SolrProcessManagerTest extends SolrTestCase {
assertEquals("https://localhost:" + processHttps.getKey() + "/solr",
https.getLocalUrl());
}
+ public void testParseWindowsPidToCommandLineJson() throws
JsonProcessingException {
+ String jsonResponseFromPowershell =
+ "[{\"ProcessId\": 9356, \"CommandLine\": \"date\"}, {\"ProcessId\":
4736, \"CommandLine\": null}\n]";
+ Map<Long, String> pidToCommandLine =
+
SolrProcessManager.parseWindowsPidToCommandLineJson(jsonResponseFromPowershell);
+ assertEquals(1, pidToCommandLine.size());
+ assertEquals("date", pidToCommandLine.get(9356L));
+ }
+
/**
* This class is started as new java process by {@link
SolrProcessManagerTest#createProcess}, and
* it listens to a HTTP(s) port to simulate a real Solr process.