"Kill a process tree" on windows using Java - java

I have a Java webstart process that is part of a windows batch script. I'm using the javaws command in a batch script in this case.
This match script ( start.bat) is invoked programatically using the "apache commons exec". Under some conditions the java process invoked by javaws hangs and I'd have to kill the entire process thread starting from the batch script start.bat.
Is there a programatic way of doing killing an entire process tree through apache commons exec?
I've tried using the "execWatchdog.destroyProcess();" on the "start.bat" script. However it only kills the start.bat process and not the entire process tree.
Is there a way of killing the entire process tree through apache-commons-exec or a similar code?
I've seen this question Performing equivalent of "Kill Process Tree" in c++ on windows that performs an equivalent task in c++. I'm wondering if anyone has implemented calling windows native system calls through JNI.

Finally got something workable even though its a roundabout way.
Apache Commons Exec API contains the CommandLauncher class that returns a java.lang.Process object. Thanks to the link
Here the link to get the windows Process Id from a java.lang.Process. This uses the JNA libraries.
Finally with the Process Id, here the command string that kills the process tree
//String killCmd = "taskkill /F /T /PID " + JNAHandler.getPid(process);

Unfortunately, as you've discovered, there isn't a pure Java way of doing this. You'll have to resort to native commands or JNI libraries, all of which are platform-dependent and more complex than a pure Java solution would be.
It may be worth upvoting the relevant bug in the Java bug database: http://bugs.sun.com/view_bug.do?bug_id=4770092
With luck we can persuade the Java developers that the poor handling of subprocesses is worth fixing for Java 8.

As far as I know, there's no such option in commons-exec. It's not even possible to obtain the PID of whatever process you just started. You could trap the kill signal within your bash script, and have the handler kill the subprocess(es) when the script process is killed.

Java Version 9 Onwards,
Java has come up with feature that can query and kill the main process and its descendants.
A code snippet to query about the child processes
import java.io.IOException;
public class ProcessTreeTest {
public static void main(String args[]) throws IOException {
Runtime.getRuntime().exec("cmd");
System.out.println("Showing children processes:");
ProcessHandle processHandle = ProcessHandle.current();
processHandle.children().forEach(childProcess ->
System.out.println("PID: " + childProcess.pid() + " Command: " + childProcess.info().command().get()));
System.out.println("Showing descendant processes:");
processHandle.descendants().forEach(descendantProcess ->
System.out.println("PID: " + descendantProcess.pid() + " Command: " + descendantProcess.info().command().get()));
}
}
To kill the process and its children, Java9 has API
Iterate through all the children of the process and call destroy on each of them
For Example : As in your case you are getting Process object from apache-commons, then try out following code
Process child = ...;
kill (child.toHandle());
public void kill (ProcessHandle handle)
{
handle.descendants().forEach((child) -> kill(child));
handle.destroy();
}
References :
https://docs.oracle.com/javase/9/docs/api/java/lang/ProcessHandle.html
https://www.tutorialspoint.com/how-to-traverse-a-process-tree-of-process-api-in-java-9
How do i terminate a process tree from Java?
Note - I have not tried this feature, Just reading about Java9 and
found helpful to share here.

Related

Start an external process while stopping current process

Not sure what's the right word for this or if this is possible.
I would like to start an external process and stop the current process in the same terminal window.
(I would like to avoid piping I/O streams for the child process.)
public static void main(String[] args) {
String ip = chooseFromCommandLine();
String cmdLine = "ping " + ip;
// launch cmdLine in the same terminal and exit this process
}
Essentially to create a "launcher"-type application but for the terminal.
In UNIX / Linux / POSIX, the terminology for this is "execing" the application. The current executing process is replaced with a new application.
Unfortunately, you can't do that in pure Java. You may be able to do it from native code that you call from Java.
Java's Runtime.exec(...) etcetera methods do the equivalent of a POSIX fork followed by exec in the child process. In other words, the parent process (i.e. the JVM) keeps running.

How can I programmatically terminate a running process in the same script that started it?

How do I start processes from a script in a way that also allows me to terminate them?
Basically, I can easily terminate the main script, but terminating the external processes that this main script starts has been the issue. I googled like crazy for Perl 6 solutions. I was just about to post my question and then thought I'd open the question up to solutions in other languages.
Starting external processes is easy with PerlĀ 6:
my $proc = shell("possibly_long_running_command");
shell returns a process object after the process finishes. So, I don't know how to programmatically find out the PID of the running process because the variable $proc isn't even created until the external process finishes. (side note: after it finishes, $proc.pid returns an undefined Any, so it doesn't tell me what PID it used to have.)
Here is some code demonstrating some of my attempts to create a "self destructing" script:
#!/bin/env perl6
say "PID of the main script: $*PID";
# limit run time of this script
Promise.in(10).then( {
say "Took too long! Killing job with PID of $*PID";
shell "kill $*PID"
} );
my $example = shell('echo "PID of bash command: $$"; sleep 20; echo "PID of bash command after sleeping is still $$"');
say "This line is never printed";
This results in the following output which kills the main script, but not the externally created process (see output after the word Terminated):
[prompt]$ ./self_destruct.pl6
PID of the main script: 30432
PID of bash command: 30436
Took too long! Killing job with PID of 30432
Terminated
[prompt]$ my PID after sleeping is still 30436
By the way, the PID of sleep was also different (i.e. 30437) according to top.
I'm also not sure how to make this work with Proc::Async. Unlike the result of shell, the asynchronous process object it creates doesn't have a pid method.
I was originally looking for a Perl 6 solution, but I'm open to solutions in Python, Perl 5, Java, or any language that interacts with the "shell" reasonably well.
For Perl 6, there seems to be the Proc::Async module
Proc::Async allows you to run external commands asynchronously, capturing standard output and error handles, and optionally write to its standard input.
# command with arguments
my $proc = Proc::Async.new('echo', 'foo', 'bar');
# subscribe to new output from out and err handles:
$proc.stdout.tap(-> $v { print "Output: $v" });
$proc.stderr.tap(-> $v { print "Error: $v" });
say "Starting...";
my $promise = $proc.start;
# wait for the external program to terminate
await $promise;
say "Done.";
Method kill:
kill(Proc::Async:D: $signal = "HUP")
Sends a signal to the running program. The signal can be a signal name ("KILL" or "SIGKILL"), an integer (9) or an element of the Signal enum (Signal::SIGKILL).
An example on how to use it:
#!/usr/bin/env perl6
use v6;
say 'Start';
my $proc = Proc::Async.new('sleep', 10);
my $promise= $proc.start;
say 'Process started';
sleep 2;
$proc.kill;
await $promise;
say 'Process killed';
As you can see, $proc has a method to kill the process.
Neither Perl, Perl 6, nor Java, but bash:
timeout 5 bash -c "echo hello; sleep 10; echo goodbye" &
In Java you can create a process like this:
ProcessBuilder processBuilder = new ProcessBuilder("C:\\Path\program.exe", "param1", "param2", "ecc...");
Process process = processBuilder.start(); // start the process
process.waitFor(timeLimit, timeUnit); // This causes the current thread to wait until the process has terminated or the specified time elapses
// when you want to kill the process
if(process.isAlive()) {
process.destroy();
}
Or you can use process.destroyForcibly();, see the Process documentation for more info.
To execute a bash command point to the bash executable and set the command as a parameter.

Killing a Java Process object

I have a cross-platform (Linux and Windows) program, currently running on Java 7. The program will launch one or more workers as Processes using ProcessBuilder (using instructions from here):
String javaHome = System.getProperty("java.home");
String javaBin = javaHome + File.separator + "bin" + File.separator + "java";
String classpath = System.getProperty("java.class.path");
String className = MyClass.class.getCanonicalName();
ProcessBuilder pb = new ProcessBuilder(javaBin, "-cp", classpath, className);
pb.directory(null);
pb.inheritIO();
Process p = pb.start();
The reason for this approach was designed to solve an issue with the older system, which ran the workers in threads. However, due to the nature of the code we're running, it's possible for the workers to enter a loop from which they won't exit. Since Java does not support the idea of terminating a thread, we moved to a completely separate process under the assumption that Java could actually kill a process.
However, it appears that isn't true in Java 7 - Process.destroy() sends a SIGTERM, not a SIGKILL, and our stalled workers aren't being killed as we'd expect. Java 8 implemented Process.destroyForcibly(), which would solve the problem, but upgrading the core Java version is likely to introduce a number of bugs, and upgrading is something we'd like to avoid if at all possible.
Most other solutions involve reflecting into the UNIXProcess class, getting the pid, and piping a kill command to the shell. However, this won't work for us as we run on Windows, where Process does not have any grasp of a pid, and further forces us to include OS-specific code paths which is extremely undesirable.
Is there some reliable way to get Java to terminate something?
You can try this API SIGAR.
Sample code:
final Sigar sigar = new Sigar();
final long[] processes = sigar.getProcList();
for (final long processId : processes) {
System.out.println(processId + " = " + ProcUtil.getDescription(sigar, processId));
final String processDescription = ProcUtil.getDescription(sigar, processId);
if(processDescription.contains("notepad.exe")){
System.out.println("Found notepad.exe with id [" + processId + "] - KILLING IT!");
sigar.kill(processId, -9);
}
}
Code source:Link
The solution in my case, since I was writing both the parent and the child processes, was to add a ShutdownHook which halted the JVM (a simple System.exit(1) was not sufficient):
Runtime.getRuntime().addShutdownHook(new Thread() {
#Override
public void run() {
Runtime.getRuntime().halt(5);
}
});
After this, when a SIGTERM was received the process would terminate, as though it had been sent SIGKILL by the parent process.

How to create a process which can execute concurrently with respect to java process [duplicate]

I am working on a program written in Java which, for some actions, launches external programs using user-configured command lines. Currently it uses Runtime.exec() and does not retain the Process reference (the launched programs are either a text editor or archive utility, so no need for the system in/out/err streams).
There is a minor problem with this though, in that when the Java program exits, it doesn't really quit until all the launched programs are exited.
I would greatly prefer it if the launched programs were completely independent of the JVM which launched them.
The target operating system is multiple, with Windows, Linux and Mac being the minimum, but any GUI system with a JVM is really what is desired (hence the user configurability of the actual command lines).
Does anyone know how to make the launched program execute completely independently of the JVM?
Edit in response to a comment
The launch code is as follows. The code may launch an editor positioned at a specific line and column, or it may launch an archive viewer. Quoted values in the configured command line are treated as ECMA-262 encoded, and are decoded and the quotes stripped to form the desired exec parameter.
The launch occurs on the EDT.
static Throwable launch(String cmd, File fil, int lin, int col) throws Throwable {
String frs[][]={
{ "$FILE$" ,fil.getAbsolutePath().replace('\\','/') },
{ "$LINE$" ,(lin>0 ? Integer.toString(lin) : "") },
{ "$COLUMN$",(col>0 ? Integer.toString(col) : "") },
};
String[] arr; // array of parsed tokens (exec(cmd) does not handle quoted values)
cmd=TextUtil.replace(cmd,frs,true,"$$","$");
arr=(String[])ArrayUtil.removeNulls(TextUtil.stringComponents(cmd,' ',-1,true,true,true));
for(int xa=0; xa<arr.length; xa++) {
if(TextUtil.isQuoted(arr[xa],true)) {
arr[xa]=TextDecode.ecma262(TextUtil.stripQuotes(arr[xa]));
}
}
log.println("Launching: "+cmd);
Runtime.getRuntime().exec(arr);
return null;
}
This appears to be happening only when the program is launched from my IDE. I am closing this question since the problem exists only in my development environment; it is not a problem in production. From the test program in one of the answers, and further testing I have conducted I am satisfied that it is not a problem that will be seen by any user of the program on any platform.
There is a parent child relation between your processes and you have to break that.
For Windows you can try:
Runtime.getRuntime().exec("cmd /c start editor.exe");
For Linux the process seem to run detached anyway, no nohup necessary.
I tried it with gvim, midori and acroread.
import java.io.IOException;
public class Exec {
public static void main(String[] args) {
try {
Runtime.getRuntime().exec("/usr/bin/acroread");
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("Finished");
}
}
I think it is not possible to to it with Runtime.exec in a platform independent way.
for POSIX-Compatible system:
Runtime.getRuntime().exec(new String[]{"/bin/sh", "-c", "your command"}).waitFor();
I have some observations that may help other people facing similar issue.
When you use Runtime.getRuntime().exec() and then you ignore the java.lang.Process handle you get back (like in the code from original poster), there is a chance that the launched process may hang.
I have faced this issue in Windows environment and traced the problem to the stdout and stderr streams. If the launched application is writing to these streams, and the buffer for these stream fills up then the launched application may appear to hang when it tries to write to the streams. The solutions are:
Capture the Process handle and empty out the streams continually - but if you want to terminate the java application right after launching the process then this is not a feasible solution
Execute the process call as cmd /c <<process>> (this is only for Windows environment).
Suffix the process command and redirect the stdout and stderr streams to nul using 'command > nul 2>&1'
It may help if you post a test section of minimal code needed to reproduce the problem. I tested the following code on Windows and a Linux system.
public class Main {
/**
* #param args the command line arguments
*/
public static void main(String[] args) throws Exception {
Runtime.getRuntime().exec(args[0]);
}
}
And tested with the following on Linux:
java -jar JustForTesting.jar /home/monceaux/Desktop/__TMP/test.sh
where test.sh looks like:
#!/bin/bash
ping -i 20 localhost
as well as this on Linux:
java -jar JustForTesting.jar gedit
And tested this on Windows:
java -jar JustForTesting.jar notepad.exe
All of these launched their intended programs, but the Java application had no problems exiting. I have the following versions of Sun's JVM as reported by java -version :
Windows: 1.6.0_13-b03
Linux: 1.6.0_10-b33
I have not had a chance to test on my Mac yet. Perhaps there is some interaction occuring with other code in your project that may not be clear. You may want to try this test app and see what the results are.
You want to launch the program in the background, and separate it from the parent. I'd consider nohup(1).
I suspect this would require a actual process fork. Basically, the C equivalent of what you want is:
pid_t id = fork();
if(id == 0)
system(command_line);
The problem is you can't do a fork() in pure Java. What I would do is:
Thread t = new Thread(new Runnable()
{
public void run()
{
try
{
Runtime.getRuntime().exec(command);
}
catch(IOException e)
{
// Handle error.
e.printStackTrace();
}
}
});
t.start();
That way the JVM still won't exit, but no GUI and only a limited memory footprint will remain.
I tried everything mentioned here but without success. Main parent Java process can't quit until the quit of subthread even with cmd /c start and redirecting streams tu nul.
Only one reliable solution for me is this:
try {
Runtime.getRuntime().exec("psexec -i cmd /c start cmd.cmd");
}
catch (Exception e) {
// handle it
}
I know that this is not clear, but this small utility from SysInternals is very helpful and proven. Here is the link.
One way I can think of is to use Runtime.addShutdownHook to register a thread that kills off all the processes (you'd need to retain the process objects somewhere of course).
The shutdown hook is only called when the JVM exits so it should work fine.
A little bit of a hack but effective.

Running a program from within Java code

What is the simplest way to call a program from with a piece of Java code? (The program I want to run is aiSee and it can be run from command line or from Windows GUI; and I am on Vista but the code will also be run on Linux systems).
Take a look at Process and Runtime classes. Keep in mind that what you are trying to accomplish is probably not platform independent.
Here is a little piece of code that might be helpful:
public class YourClass
{
public static void main(String args[])
throws Exception
{
Runtime rt = Runtime.getRuntime();
Process proc = rt.exec("name_of_your_application.exe");
int exitVal = proc.exitValue();
System.out.println("Process exitValue: " + exitVal);
}
}
One question in S.O. discussing similiar issues. Another one. And another one.
You can get a runtime instance using Runtime.getRuntime() and call the runtime's exec method, with the command to execute the program as an argument.
For example:
Runtime runTime = Runtime.getRuntime ();
Process proc = rt.exec("iSee.exe");
You can also capture the output of the program by using getting the InputStream from the process.
The difficulty you will run into is how to get the application to know the path. You may want to use an xml or config file, but if you use this link, it should explain how to run a file:
http://www.javacoffeebreak.com/faq/faq0030.html
You may also want to consider passing in some kind of argument to your program to facilitate finding the specific program you want to run.
This could be with command line arguments, properties files or system properties.

Categories