In "C", I can run a long blocking process in the background (AND HAVE IT CONTINUE TO RUN) after the starting process has exited.
void main(void)
{
system("some_long_blocking_process &");
exit();
}
// "some_long_blocking_process" is still running here (DESIRED BEHAVIOR)
Java's getRuntime().exec() DOESN'T have this behavior. Instead, "some_long_blocking_process" ends immediately when the Java process ends.
Anyone know how I can recapture this behavior in Java?
I am using Java 1.4 (No process builder)
I specifically am looking to start the long blocking process and to exit immediately (no "waitFor(), etc.)
Things I have already tried (the process runs correctly, but I still get the same undesired behavior)
adding "nohup" and run in foreground ("nohup some_long_process")
adding "nohup" and running in background ("nohup some_long_process &")
run in foreground ("some_long_process")
run in background ("some_long_process &")
THANKS!
Thanks to all the suggestions... I've decided to use jtahlborn's answer (it worked for me)
try this:
String[] cmd = {"/bin/sh", "-c", "yourcommand args"};
Process p = Runtime.getRuntime().exec(cmd);
when redirect stream to /dev/null:
String[] cmd = {"/bin/sh", "-c", "yourcommand args > /dev/null 2>&1 &"};
Process p = Runtime.getRuntime().exec(cmd);
the only way we were able to achieve this with java was to add another layer of script. you need a simple wrapper script which invokes the app you actually want to run, e.g.:
runner.sh:
#!/bin/sh
nohup "$#" > /dev/null 2>&1 &
then invoke "/bin/sh runner.sh the real command" from your java program.
EDIT:
Have your tried this?
Runtime.getRuntime().exec("/bin/sh -c /usr/X11/bin/xterm &")
This worked for me on MacOS.
Previous answer (JDK 1.5, apologies for not reading the question correctly):
To execute a process without waiting you can use the ProcessBuilder
ProcessBuilder pb = new ProcessBuilder("/usr/X11/bin/xterm");
pb.start();
Your problem is probably due to the trailing &. Try removing it.
getRuntime().exec() is more similar to fork() and exec() than system().
system() passes the command to the shell, and it's Bash that understands that the trailing ampersand means to run the process in the background.
getRuntime().exec() parses the command using a StringTokenizer to parse the command, and doesn't do anything with the trailing ampersand. That's simply passed as the first argument to your some_long_blocking_process, which may exit out immediately on the unknown error.
Have you tried spawning a new Thread to run the executable? Try:
new Thread(new Runnable() {
#Override
public void run() {
try {
Runtime.getRuntime().exec(<your exec>);
} catch (IOException e) {
e.printStackTrace();
}
}
}).run();
This way, the main process won't shutdown until the exec has finished running (and the thread has finished).
Also, don't manually call exit() in your application unless you have some overwhelming reason to--the JVM does a good job of detecting when the application has finished on its own. This way, you won't force close threads that are running in the background.
Related
I'm experiencing a weird behaviour of java's ProcessBuilder.
What I try is to stop a running screen using a shell, delete a few folders and after that restart the screen using another shell script.
The first step, killing the running screen, runs perfectly using:
ProcessBuilder pb0 = new ProcessBuilder(System.getProperty("user.dir") + "/generator/stop.sh");
In this stop.sh shell I simply run
screen -X -S generator kill
which works as it should.
After that I delete my directorys using org.apache.commons.io.FileUtils and then I want to start the screen again. Currently I'm doing it like that:
System.out.println("Restarting the generator");
ProcessBuilder pb1 = new ProcessBuilder();
pb1.directory(new File(System.getProperty("user.dir") + "/generator"));
pb1.command("./start.sh");
try {
Process process = pb1.start();
System.out.printf("Started the generator with %d", process.waitFor());
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
This gives out "Started the generator with 1" which indicates to me that the screen runs which is simply not the case when checking with screen -ls.
No errors, no clue on how to move forward from here
Inside start.sh:
screen -S generator java -Xms2G -Xmx2G -jar generator.jar
PS: I'm using Debian 10.
Maybe anyone can help me out here?
Greets!
You should never ignore process output, because it's buffer has limited length, and if you don't consume it, it will hang. Im not sure if this is causing your issue, but this is definitely something you should do.
It is also possible that process throws some error that you can't see because you are ignoring it's output (in which case, this will help you to investigate the issue).
Try this:
new ProcessBuilder()
.redirectOutput(ProcessBuilder.Redirect.INHERIT)
.redirectError(ProcessBuilder.Redirect.INHERIT)
...
This will redirect process output stream and input stream to it's parent (which is your application).
This has got to be one of the strangest things I have ever observed. Consider the following Java program:
import java.io.IOException;
public class StrangeError {
public static void main(String[] args) {
try {
Process process = new ProcessBuilder(
"cmd",
"/c",
"\"C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\vcvarsall.bat\" amd64 && set"
).start();
process.waitFor();
} catch (IOException|InterruptedException e) {
System.out.println(e.getMessage());
}
}
}
I compiled it with javac StrangeError.java, copied it to my server running Windows Server 2012 R2, and ran it with java StrangeError.
Here's where things start to get weird. The program hangs, waiting for the process it spawned to finish. This is not the expected behavior, since the vcvarsall.bat script should complete immediately as well as set.
So I started playing around and discovered the following:
Removing set causes vcvarsall.bat to terminate
Removing vcvarsall.bat causes set to terminate
Replacing && with || causes everything to terminate correctly
Copying vcvarsall.bat to a location on the desktop and changing the path causes everything to terminate correctly
A nearly equivalent program works fine in Go using the same commands
I get this output if I run everything in WinDbg and interrupt the process after it hangs
This does not appear to be reproducible with vcvarsall.bat from MSVC2013 but is also reproducible with MSVC2015 on Windows 10
What on earth is wrong with the original program? If I copy and paste the entire command (cmd /c "C:\...) into Start->Run, it immediately launches cmd and terminates, as expected.
Is this a bug with Java? Is this a bug with Windows?
Is this a bug with Java? Is this a bug with Windows?
It's a bug in your code. :-)
By default, a child process created using a ProcessBuilder object has output redirected to a pipe, the parent end of which can be obtained using Process.getInputStream() and which is not automatically drained if your code does not make use of it.
Since your code simply calls .waitFor without making any provision to drain the pipe, it will deadlock as soon as the pipe's buffer overflows. I believe the default buffer size is 4,096 bytes. On my machine, the output of the command you're running is 5,192 bytes, but this will vary depending on the original contents of the environment block. (From the sounds of it, the output length in your environment is borderline, only just above the limit, so that even small changes like changing the version of VS make a difference.)
One of the many possible solutions, depending on what you're actually trying to do, is to tell Java not to pipe the child's output:
import java.io.IOException;
public class StrangeError {
public static void main(String[] args) {
try {
ProcessBuilder processb = new ProcessBuilder(
"cmd",
"/c",
"\"C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\vcvarsall.bat\" amd64 && set"
);
processb.redirectOutput(ProcessBuilder.Redirect.INHERIT);
Process process = processb.start();
process.waitFor();
} catch (IOException|InterruptedException e) {
System.out.println(e.getMessage());
}
}
}
Not possible to read standard input and output error inside the same ProcessBuilder.
So you need to create two ProcessBuilder
Process process1 = new ProcessBuilder(
"cmd",
"/c",
"\"C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\vcvarsall.bat\",
"amd64");
Process process2 = new ProcessBuilder(
"cmd",
"/c",
"set");
process1.start();
if (process1.waitFor() == 0) {
process2.start();
if (process2.waitFor() == 0) {
// Successfull execution
}
}
And one thing : I don't think it is a good practice to do shell/batch launches with Java (or another language). Maybe you should use a script (shell, batch, python, perl...) to control standard input/output streams.
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 launching a process with Runtime.getRuntime().exec()
However, once Java reaches the end of the main() loop, java will not close, unless I destroy() the process first.
Problem with that is, I need the process to keep running after Java is closed.
I pretty much want to do this
public static void main(String args[]) {
Runtime.getRuntime().exec("file.bat");
// now I want java to close, and I want file.bat to keep running
}
I tried System.exit(), it will stop my main() loop, however I think a thread or something that was started by exec() keeps running, preventing java from closing. I can't even end it in Eclipse without first exiting file.bat
Why isn't it closing? And how would I fix it?
I couldn't find anything online, and I've been experimenting for a while, so I decided to ask you guys.
Thank you,
-Alex Benoit
Figured it out. I'll share my code in case anyone else has the same question. I brought it down to 1 line.
Desktop.getDesktop().open(new File("C:\\Folder\\File"));
This is a system dependent question. I am not sure if in Windows the parent process can terminate before the child. I believe this is true in linux.
As a guideline, you should call waitFor() on the process. However, on some systems, just doing so might not be enough. As pointed out in the javadoc, the out/err stream need to be properly purged (using the streams returned by getOutputStream() and getErrorStream()) because they could keep your process from completing. To do so, I found it most appropriate to use two separate threads, one purging the err stream, the other the out stream. The calling thread (main in your case), has to do the following:
start the external process;
start a thread to purge the out stream;
start a thread to purge the err stream;
call waitFor() on the calling thread (main).
I found the above approach very robust and easy to implement (if you are familiar with threads). Please use an ExecutorService for the threads.
Launch the file with cmd for Windows Only
Older windows will probably have to use command.com instead
Runtime.getRuntime().exec("cmd /c start file.bat");
Using the start with /wait parameter waits until bat is finished without /wait it should work.
public class Command {
public static void main(String[] args) throws java.io.IOException, InterruptedException {
String path = "C:\\DOCUME~1\\\USER\\DESKTOP";
Process p = Runtime.getRuntime().exec("cmd /c start /wait " + path + "\\test.bat");
System.out.println("Waiting for batch file ...");
p.waitFor();
System.out.println("Batch file done.");
}
}
test.bat
#echo off
cls
:start
echo This is a loop
goto start
Use the command.com file to get short path name, since long path names don't get processed too well in start program.
Here is a sample I made download link below:
https://www.mediafire.com/?mu7vht3e6tto698
Your problem may be that you are not on a Windows Administrator account you could try, But this requires you to type in your Administrator password which is very stupid.
Process p = Runtime.getRuntime().exec("runas /profile /user:Administrator \"cmd.exe /c start test.bat\"");
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.