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);
+        }
     }
 }

Reply via email to