In Java, I start one new Process using Runtime.exec(), and this process in turn spawns several child processes.
I want to be able to kill all the processes, and have previously been trying process.destroy() and process.destroyForcibly() - but the docs say that destroyForcibly() just calls destroy() in the default implementation and destroy() may not kill all subprocesses (I've tried and it clearly doesn't kill the child processes).
I'm now trying a different approach, looking up the PID of the parent process using the method suggested here and then calling ps repeatedly to traverse the PIDs of child processes, then killing them all using kill. (It only needs to run on Linux).
I've managed the first bit - looking up the PID, and am trying the following command to call ps to get the child PIDs:
String command = "/bin/ps --ppid " + pid;
Process process = new ProcessBuilder(command).start();
process.waitFor();
Unfortunately the 2nd line above is throwing an IOException, with the following message: java.io.IOException: Cannot run program "/bin/ps --ppid 21886": error=2, No such file or directory
The command runs fine if I paste it straight into the terminal on Ubuntu 16.04.
Any ideas would be very much appreciated.
Thanks
Calling the command you wish to run this way is always destined to fail.
Since Process does not effectively run a shell session, the command is basically handed over to the underlying OS to run. This means that it'll fail, since the path to t he program to be executed (in this case ps), is not the full one hence the error you're getting.
Also, testing whether your command works using a terminal is not correct. Using a terminal contains the notion of performing an action with an active logged in user with a correct path etc etc. All the above are not the case though when running a command through Process as these are not taken into consideration.
Furthermore, you also need to account for cases where the actual java application could be running under a different user, with a different set of permissions, paths etc.
In order for your to fix this, you can simply do either of the following:
1) Invoke your ps command using the full path to it (still not sure if it would work)
2) Change the way your create the Process object into something like: p = new ProcessBuilder("bash", "-c", command).start();
The second, will effectively run a bash session, passing in the ps command as an argument thus obtaining the desired result.
http://commons.apache.org/proper/commons-exec/tutorial.html
```
String line = "AcroRd32.exe /p /h " + file.getAbsolutePath();
CommandLine cmdLine = CommandLine.parse(line);
DefaultExecutor executor = new DefaultExecutor();
int exitValue = executor.execute(cmdLine);
```
Related
I would like to know how to "kill" a process that has started up. I am aware of the Process API, but I am not sure, If I can use that to "kill" an already running process, such as firefox.exe etc. If the Process API can be used, can you please point me into the correct direction? If not, what are the other available options? Thanks.
If you start the process from with in your Java application (ex. by calling Runtime.exec() or ProcessBuilder.start()) then you have a valid Process reference to it, and you can invoke the destroy() method in Process class to kill that particular process.
But be aware that if the process that you invoke creates new sub-processes, those may not be terminated (see https://bugs.openjdk.org/browse/JDK-4770092).
On the other hand, if you want to kill external processes (which you did not spawn from your Java app), then one thing you can do is to call O/S utilities which allow you to do that. For example, you can try a Runtime.exec() on kill command under Unix / Linux and check for return values to ensure that the application was killed or not (0 means success, -1 means error). But that of course will make your application platform dependent.
On Windows, you could use this command.
taskkill /F /IM <processname>.exe
To kill it forcefully, you may use;
Runtime.getRuntime().exec("taskkill /F /IM <processname>.exe")
AFAIU java.lang.Process is the process created by java itself (like Runtime.exec('firefox'))
You can use system-dependant commands like
Runtime rt = Runtime.getRuntime();
if (System.getProperty("os.name").toLowerCase().indexOf("windows") > -1)
rt.exec("taskkill " +....);
else
rt.exec("kill -9 " +....);
With Java 9, we can use ProcessHandle which makes it easier to identify and control native processes:
ProcessHandle
.allProcesses()
.filter(p -> p.info().commandLine().map(c -> c.contains("firefox")).orElse(false))
.findFirst()
.ifPresent(ProcessHandle::destroy)
where "firefox" is the process to kill.
This:
First lists all processes running on the system as a Stream<ProcessHandle>
Lazily filters this stream to only keep processes whose launched command line contains "firefox". Both commandLine or command can be used depending on how we want to retrieve the process.
Finds the first filtered process meeting the filtering condition.
And if at least one process' command line contained "firefox", then kills it using destroy.
No import necessary as ProcessHandle is part of java.lang.
Accidentally i stumbled upon another way to do a force kill on Unix (for those who use Weblogic). This is cheaper and more elegant than running /bin/kill -9 via Runtime.exec().
import weblogic.nodemanager.util.Platform;
import weblogic.nodemanager.util.ProcessControl;
...
ProcessControl pctl = Platform.getProcessControl();
pctl.killProcess(pid);
And if you struggle to get the pid, you can use reflection on java.lang.UNIXProcess, e.g.:
Process proc = Runtime.getRuntime().exec(cmdarray, envp);
if (proc instanceof UNIXProcess) {
Field f = proc.getClass().getDeclaredField("pid");
f.setAccessible(true);
int pid = f.get(proc);
}
You can kill a (SIGTERM) a windows process that was started from Java by calling the destroy method on the Process object. You can also kill any child Processes (since Java 9).
The following code starts a batch file, waits for ten seconds then kills all sub-processes and finally kills the batch process itself.
ProcessBuilder pb = new ProcessBuilder("cmd /c my_script.bat"));
Process p = pb.start();
p.waitFor(10, TimeUnit.SECONDS);
p.descendants().forEach(ph -> {
ph.destroy();
});
p.destroy();
Try it:
String command = "killall <your_proccess>";
Process p = Runtime.getRuntime().exec(command);
p.destroy();
if the process is still alive, add:
p.destroyForcibly();
It might be a java interpreter defect, but java on HPUX does not do a kill -9, but only a kill -TERM.
I did a small test testDestroy.java:
ProcessBuilder pb = new ProcessBuilder(args);
Process process = pb.start();
Thread.sleep(1000);
process.destroy();
process.waitFor();
And the invocation:
$ tusc -f -p -s signal,kill -e /opt/java1.5/bin/java testDestroy sh -c 'trap "echo TERM" TERM; sleep 10'
dies after 10s (not killed after 1s as expected) and shows:
...
[19999] Received signal 15, SIGTERM, in waitpid(), [caught], no siginfo
[19998] kill(19999, SIGTERM) ............................................................................. = 0
...
Doing the same on windows seems to kill the process fine even if signal is handled (but that might be due to windows not using signals to destroy).
Actually i found Java - Process.destroy() source code for Linux related thread and openjava implementation seems to use -TERM as well, which seems very wrong.
I am using the JDI to debug another running java application.
What I do that works:
Run two applications using Eclipse. The debugger is launched with the following VM Options:
-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=4000
The other application connects to the socket at port 4000, and follows normal procedures (break points, etc.) to get a value of a Local Variable.
Works properly and gives me that value.
What I want to do now:
Instead of using Eclipse to launch two processes, I launch one in Eclipse, and that Process uses a ProcessBuilder to launch another one with the following arguments:
String[] args1 = {getJavaDir(),"-cp",classpath,"-Xdebug", "-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=4000", "processII.Main2"};
ProcessBuilder builder = new ProcessBuilder(args1);
builder.directory(directory);
Process process = builder.start();
The process starts successfully. However, when I try to access it through the first process, I get the following Error:
com.sun.jdi.connect.IllegalConnectorArgumentsException: Argument invalid
Looked this up online, and there is little information about what the Exception is.
I would appreciate any help figuring out what the problem is!
This exception is throw when there is an error on the connector parameters to debug the JVM. I think that your debug parameters must go in the same argument together instead of two separate arguments (put -Xdebug with -Xrunjdwp... on the same argument), try with:
String[] args1 = {getJavaDir(),"-cp",classpath,"-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=4000", "processII.Main2"};
ProcessBuilder builder = new ProcessBuilder(args1);
builder.directory(directory);
Process process = builder.start();
Hope this helps,
You missed this code :import com.sun.jdi.connect.IllegalConnectorArgumentsException;
It depends on the jdk/lib/tool.jar.If you add this jar to you classpath,you can fixout your problem.
So I'm creating a Java program and I want to make it so that you can ask it to open a program.
But, here's the catch, I want the program it opens to be taken from the user input, right now I'm trying to change this
try{Process p = Runtime.getRuntime().exec("notepad.exe");}
catch(Exception e1){}
Into something that opens a program that you asked it to open.
Here's an example of what I want:
User: Can you open chrome?
Program: Of course, here you go!
chrome opens
Could anyone tell me how I would be able to do this?
You can do it in two ways:
1.By Using Runtime:
Runtime.getRuntime().exec(...)
So, for example, on Windows,
Runtime.getRuntime().exec("C:\application.exe -arg1 -arg2");
2.By Using ProcessBuilder:
ProcessBuilder b = new ProcessBuilder("C:\application.exe", "-arg1", "-arg2");
or alternatively
List<String> params = java.util.Arrays.asList("C:\application.exe", "-arg1", "-arg2");
ProcessBuilder b = new ProcessBuilder(params);
or
ProcessBuilder b = new ProcessBuilder("C:\application.exe -arg1 -arg2");
The difference between the two is :
Runtime.getRuntime().exec(...)
takes a single string and passes it directly to a shell or cmd.exe process. The ProcessBuilder constructors, on the other hand, take a varargs array of strings or a List of strings, where each string in the array or list is assumed to be an individual argument.
So,Runtime.getRuntime.exec() will pass the line C:\application.exe -arg1 -arg2 to cmd.exe, which runs a application.exe program with the two given arguments. However, ProcessBuilder method will fail, unless there happens to be a program whose name is application.exe -arg1 -arg2 in C:.
You can try it with like. Pass whole path of where you install chrome.
try{
Process p = Runtime.getRuntime().exec("C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe");
}
catch(Exception e1){
}
When using exec, it is essentially the same as if you were using the command line on windows. Open Command Prompt, type open, and see if it gives details as to how it opens files. If not, find the opener. Usually when dealing with command line operations, there are multiple parameters that are required for opening files/applications. An example of this would be for opening the "TextEdit.app" application on a mac.
Process p = Runtime.getRuntime().exec("open -a TextEdit.app");
Terminal(for mac) would open the app using the -a flag, meaning "application." You could open a file doing:
Process p = Runtime.getRuntime().exec("open filename.file_ext -a TextEdit.app");
The second one will tell the computer to find the application named <app_name>.app and open the file filename.file_ext
I know this is not going to work for a windows machine, but it's only to show how to use the command line operations for opening files and applications. It should be similar for windows though.
Hope this helps
I want to run different commands which can be executed on my command prompt or terminal through Java.
I did search few place but did not get appropriate reply.
I want to run a compiler which is set in the environment as VISAGE_HOME as well as run GRADLE so as to do all my build tasks.
I want to invoke all these commands from within Java Program.
Since it is a swing application I would like to invoke these commands on click of button or some other events.
My Problem is that I am not able to program this :( .
Neither do I know an API which would do this. I went through some sample codes but most of them have same kind of example codes of executing the shell commands or command prompt commands. None showed me to do the above stuff.
Have a look at ProcessBuilder. The Process object it returns has a waitFor method so you can wait for the process to finish. Then you can start your next process.
For example
Process p = new ProcessBuilder("runYourCommand").start();
InputStream in = process.getInputStream();
InputStreamReader inr = new InputStreamReader(in);
BufferedReader br = new BufferedReader(inr);
String inputLine;
while ((inputLine = br.readLine()) != null) {
System.out.println(inputLine);
}
p.waitFor();
Another interesting method on ProcessBuilder is environment(). This will return the environment variables that you can access. From the API docs
Map<String, String> env = pb.environment();
env.put("VAR1", "myValue");
Something like this:
String cmd = "gedit";
Runtime run = Runtime.getRuntime();
Process pr = run.exec(cmd);
Firstly, ProcessBuilder is your friend...
You could have a look at;
Getting started with Java’s ProcessBuilder (Linux focus)
Using ProcessBuilder to Make System Calls
Run processbuilder and get in and output
ProcessBuilder and how redirecting input and output from operating system's processes
Executing Operating System Commands from Java
Secondly, You will need to use System.getenv to find the value of the specified environment variable and substitute it yourself.
nb: Thanks to Guillaume Polet for pointing out that the Process will automatically include the path environment variable to find commands.
Also, remember, DO NOT EXECUTE ANY BLOCKING PROCESS ON THE EDT.
Executing external commands are inherently blocking actions, while not explicitly, taking into account needing to consume the output of the process or wanting to know about the processes termination, these would require you to perform some kind of blocking action. Don't do this on the EDT. It will cause you program to appear as if it's hung.
You can use the Runtime.exec methods to run commands from within Java. The system enviroment variables are normally not visible from within the jvm. You could use a launcher, that submits these system enviroment properties as jvm properties to your java application.
According to http://javarevisited.blogspot.de/2011/02/how-to-execute-native-shell-commands.html implementing the following into your code after including "java.lang.Runtime" should perfectly work:
try {
Process process = Runtime.getRuntime().exec("Command to be executed");
} catch (Exception e) {
e.printStackTrace(System.err);
}
It's not the first time I have tried to execute a system command from Java; but this time it turns out to be very hard. I have a script that executes just fine from the terminal. It reads input from a file (input.txt), it processes it and exports the result in another file (ouput.txt). The whole thing lasts no more than 1sec. But, when I try to execute it from Java, it gets stuck and never finishes. This is my code:
Process p = new ProcessBuilder("./runCalculator.sh").start();
p.waitFor();
I have also tried with Runtime.getRuntime().exec("./runCalculator.sh") but all the same. I've read both the InputStream and the ErrorStream of the process. The error stream returns nothing but a message like "Starting Calculation..."
Any ideas?
You need to use the following code:
ProcessBuilder pb = new ProcessBuilder();
pb.command("bash", "-c", "./runCalculator.sh");
Process process = pb.start();
int retValue = process.waitFor();
You likely need to invoke the unix command interpreter/processor for this to work. Please see: When Runtime.exec() won't.
Try this:
Process p = new ProcessBuilder("sh ./runCalculator.sh").start();
Another, simplier solution is that you can open program by entering the name of the program (this assumes that program is installed) instead of creating script and calling it.
Note that the name of the program isn't always what you see in Gnome's menu, for example Gnome's calculator is "gnome-calculator". Regarding this facts, you can run calculator by the folowing line:
Process p = Runtime.getRuntime().exec("gnome-calculator");
In that case you don't have a need for any sh scripts (in your case runCalculator.sh).