Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions solr/CHANGES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,8 @@ Bug Fixes
* SOLR-12831: Clean up shard metadata in ZooKeeper nodes after shard deletion is invoked. This makes sure Zookeeper
nodes for leader election and terms are not left behind (Andy Vuong, Pierre Salagnac).

* SOLR-17717: Starting solr on newer Windows 11 Home complained about missing wmic (Jan Høydahl)

Dependency Upgrades
---------------------
* SOLR-17471: Upgrade Lucene to 9.12.1. (Pierre Salagnac, Christine Poerschke)
Expand Down
93 changes: 59 additions & 34 deletions solr/core/src/java/org/apache/solr/cli/SolrProcessManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,23 +18,26 @@

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;
import java.nio.file.Path;
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;
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;
Expand All @@ -52,8 +55,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"))
Expand Down Expand Up @@ -151,8 +158,8 @@ private boolean isProcessSsl(ProcessHandle ph) {
}

/**
* 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
Expand All @@ -161,42 +168,60 @@ private static Optional<String> commandLine(ProcessHandle ph) {
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.
*
Expand Down
11 changes: 11 additions & 0 deletions solr/core/src/test/org/apache/solr/cli/SolrProcessManagerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
*/
package org.apache.solr.cli;

import com.fasterxml.jackson.core.JsonProcessingException;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
Expand All @@ -27,6 +28,7 @@
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;
Expand Down Expand Up @@ -177,6 +179,15 @@ public void testSolrProcessMethods() {
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.
Expand Down
Loading