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.

Reply via email to