This is an automated email from the ASF dual-hosted git repository.
sseifert pushed a commit to branch master
in repository
https://gitbox.apache.org/repos/asf/sling-feature-launcher-maven-plugin.git
The following commit(s) were added to refs/heads/master by this push:
new dbfd557 SLING-11934 Fix shutting down launcher started via bat file
on Windows (#24)
dbfd557 is described below
commit dbfd5577a6664ccc4d0d3d8509970485e95b1964
Author: Stefan Seifert <[email protected]>
AuthorDate: Mon Nov 24 16:26:56 2025 +0100
SLING-11934 Fix shutting down launcher started via bat file on Windows (#24)
---
pom.xml | 16 +----
.../maven/feature/launcher/ProcessTracker.java | 73 ++++++++++++++++++----
2 files changed, 63 insertions(+), 26 deletions(-)
diff --git a/pom.xml b/pom.xml
index b8c6908..f690130 100644
--- a/pom.xml
+++ b/pom.xml
@@ -45,7 +45,7 @@
</prerequisites>
<properties>
- <sling.java.version>8</sling.java.version>
+ <sling.java.version>11</sling.java.version>
<maven.version>3.8.1</maven.version>
<project.build.outputTimestamp>1757086693</project.build.outputTimestamp>
<github.project.id>apache/sling-feature-launcher-maven-plugin</github.project.id>
@@ -196,18 +196,4 @@
</plugins>
</reporting>
- <profiles>
- <!-- remove once SLING-11934 is fixed -->
- <profile>
- <id>skip-invoker-windows</id>
- <activation>
- <os>
- <family>Windows</family>
- </os>
- </activation>
- <properties>
- <invoker.skip>true</invoker.skip>
- </properties>
- </profile>
- </profiles>
</project>
diff --git
a/src/main/java/org/apache/sling/maven/feature/launcher/ProcessTracker.java
b/src/main/java/org/apache/sling/maven/feature/launcher/ProcessTracker.java
index cc26d5e..72e33ae 100644
--- a/src/main/java/org/apache/sling/maven/feature/launcher/ProcessTracker.java
+++ b/src/main/java/org/apache/sling/maven/feature/launcher/ProcessTracker.java
@@ -25,51 +25,102 @@ import java.util.concurrent.TimeUnit;
import javax.inject.Named;
import javax.inject.Singleton;
+import org.apache.maven.shared.utils.Os;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
@Named
@Singleton
public class ProcessTracker {
+ private static final Logger LOG =
LoggerFactory.getLogger(ProcessTracker.class);
+
static void stop(Process process) throws InterruptedException {
+ LOG.debug("Destroy process: {}", process);
process.destroy();
boolean stopped = process.waitFor(30, TimeUnit.SECONDS);
- if ( !stopped )
+ if ( !stopped ) {
+ LOG.debug("Forcibly destroy process after 30sec: {}", process);
process.destroyForcibly();
+ }
+ LOG.debug("Destroy process finished: {}", process);
+ }
+
+ /**
+ * On windows, this method is used for stopping the launcher.
+ * The Launcher is started from a .bat file, and killing the known process
only kills the .bat process, not the spawned java process.
+ * So we try to kill all descendant processes first.
+ */
+ static void stopWithDescendants(Process process) throws
InterruptedException {
+ LOG.debug("Destroy process with descendants: {}", process);
+
+ ProcessHandle processHandle =
ProcessHandle.of(process.pid()).orElse(null);
+ if (processHandle == null) {
+ LOG.error("Unable to shutdown process, no process handle for pid
{}", process.pid());
+ return;
+ }
+
+ processHandle.descendants().forEach(childProcess -> {
+ LOG.debug("Destroy child process: {}", childProcess);
+ childProcess.destroy();
+ try {
+ boolean stopped = childProcess.onExit().get(30,
TimeUnit.SECONDS) != null;
+ if ( !stopped ) {
+ LOG.debug("Forcibly destroy child process after 30sec:
{}", childProcess);
+ childProcess.destroyForcibly();
+ }
+ LOG.debug("Destroy child process finished: {}", childProcess);
+ } catch (Exception ex) {
+ LOG.error("Error while stopping child process {}: {}",
childProcess, ex.getMessage(), ex);
+ }
+ });
+
+ stop(process);
}
-
+
private final Object sync = new Object();
-
+
private boolean hookAdded = false;
private final Map<String, Process> processes = new HashMap<>();
-
+
public void startTracking(String launchId, Process process) {
synchronized (sync) {
if ( processes.containsKey(launchId) )
throw new IllegalArgumentException("Launch id " + launchId + "
already associated with a process");
+ LOG.debug("Start tracking process for launch {}: {}", launchId,
process);
processes.put(launchId, process);
if ( ! hookAdded ) {
Runtime.getRuntime().addShutdownHook(new
Thread("process-tracker-shutdown") {
@Override
public void run() {
+ LOG.debug("Shutdown hook is running for launch {}:
{}", launchId, process);
for ( Map.Entry<String, Process> entry :
processes.entrySet() ) {
- System.err.println("Launch " + entry.getKey() + "
was not shut down! Destroying forcibly from shutdown hook..");
+ LOG.error("Launch {} was not shut down! Destroying
forcibly from shutdown hook.", entry.getKey());
process.destroyForcibly();
}
}
-
+
});
hookAdded = true;
}
}
}
-
+
public void stop(String id) throws InterruptedException {
Process process;
synchronized (sync) {
- process = processes.remove(id);
+ process = processes.remove(id);
}
- if ( process == null )
+ if ( process == null ) {
+ LOG.warn("Process not found in process list, skip stopping: {}",
id);
return;
-
- stop(process);
+ }
+
+ if (Os.isFamily(Os.FAMILY_WINDOWS)) {
+ stopWithDescendants(process);
+ }
+ else {
+ stop(process);
+ }
}
}