This is a spinoff of this thread: Killing a JFrame/Java when process finishes
I know this question has been asked various times here on SO but none of the answers seem to solve my problem. The issue I am having is this: I have a java program that calls a batch file. I need the batch file to finish before the Java program continues. For this I use the waitFor() method, which should wait until the process finishes. However, it doesn't. I found this thread, which suggests that the process waitFor() is actually waiting for is simply cmd.exe, which returns before the actual process does.
Runtime.exec().waitFor() doesn't wait until process is done
That thread recommends using the /wait command, but when I do it makes no difference. My code is very in-depth with several classes that all rely on one another, but here is an attempted SSCCE:
public class BatchFileRun(){
public static void main(String[] args){
ArrayList<String> list = new ArrayList<String>();
list.add("cmd.exe");
list.add("/C");
list.add("Start");
list.add("/wait");
list.add(("C:\\.....\\scripts\\myBatchFile.bat"));
list.add("batchVariable");
String[] commands = new String[list.size()];
commands = list.toArray(commands);
Process p = Runtime.getRuntime().exec(commands);
p.waitFor();
closeWindow();
}
void closeWindow(){
WindowEvent close = new WindowEvent(this, WindowEvent.WINDOW_CLOSING);
Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(close);
System.exit(0);
}
}
The batch file simply operates on the variable passed into it as a parameter (batchVariable). All prior commands should be used to simply run the batch file. The problem is that the closeWindow() method is called before the process in the batch file is finished. Therefore, the Java process that appears in the Windows task manager remains running, even though it should close when closeWindow() is called (if I am using it correctly). I really need said process to close when the process running from the batch file is complete. I have tried different ways of running the batch file (using ProcessBuilder, for example), but no matter what I try I have the same problem.
You can modify the batch file so it only finishes when the process it launches finishes.
Related
This is a very unusual problem I've come across and I'm hoping someone might have some insight on it. I'm on macOS Mojave (10.14.6), using Amazon's JRE Corretto-11.0.9.12.1 (build 11.0.9.1+12-LTS)
I have a program I've written that is something of a scripting engine that we use to do our routine bulk data processing. It takes an xml "script" file which contains the processing directions to execute and a list of input files as arguments. I'll refer to this as the "engine" from this point on. This engine is the backbone of a large portion of our automation. It shows a progress bar while processing to let users know that it is working.
There are 2 programs that use this engine:
One is a thin UI written in Swing, which we use to manually process data; it generates an xml file from the user input and passes it along with the input files and launches the engine in a separate process; the UI itself doesn't process any data.
The other watches a folder on our file server and processes incoming data from our clients daily when a folder is created inside of it so we can rip the data into our database. I'll call this the "importer".
Recently, a problem has come up where the engine becomes stuck while processing. Older versions of the engine did not have this problem, and I'm trying to figure out what exactly changed that caused this to happen, but while I've been trying to do this, our UI and importer programs have been using and older version of the engine. There are new features that we need to use in the new version of the engine, but we can't use it until this problem is solved.
The programs that uses the engine launch it in a process then waits for the result before continuing:
// example command generated from external program
String commandString = "java -jar engine.jar script.xml input_file1.txt input_file2.txt input_file3.txt";
String[] command = {"bash", "-c", commandString};
// I can grab the command from here for debugging
System.out.println(Arrays.toString(command));
ProcessBuilder pb = new ProcessBuilder(command);
// wait for the process to complete before continuing
Process p = pb.start();
p.waitFor();
int result = p.exitValue();
try (BufferedReader e = new BufferedReader(new InputStreamReader(proc.getErrorStream()));
BufferedReader i = new BufferedReader(new InputStreamReader(proc.getInputStream()))) {
String line;
while ((line = e.readLine()) != null) {
System.out.println(line);
}
while ((line = i.readLine()) != null) {
System.out.println(line);
}
}
p.destroy();
// do other stuff
When launched in this way, there is a specific operation that causes the engine to hang. But if I take the command and launch it directly from the command line, the engine runs just fine! This is making it difficult to pin down where exactly the problem is; is it in the engine, or in the other programs? I've spent a couple of days looking for answers and come up with nothing. It's even more frustrating that this problem has appeared seemingly out of nowhere when it was working perfectly before, using the exact code above, for a quite a long time.
The operation where the engine hangs sorts files into folders based on their file names. When I watch my activity monitor while it runs, it's not taxing my resources at all, and disk space isn't an issue. It isn't a file permission issue, as the engine is creating files and folders all the time and in every step leading up to the step where it hangs. And as I said, if I run the command directly from the command line, it creates the folders and sorts the files without issue, to my extreme confusion.
The importer and UI run locally on a station, but the engine jar file lives on our file server, so that it is accessible to every station without individually downloading it everywhere each time there is an update. I thought at first that the issue might lie in the fact that it is being accessed over the network, but the problem occurs even when I use a local copy of the engine on my dev machine, so I have ruled that out. I've also ruled out that it's the JRE, even though we switched to it recently, since the older version of the engine still perform as expected.
There might of course be any reason why your 'engine' program may hang ;-) but certainly it will hang you don't read the its output, and in the right way:
The parent process needs to read the standard output and standard error streams of the child process, given that the child process does generate any substantial amount of output on any of these two channels. This must be done in two separate background threads. If the parent does not read the child's output, then the child process will block as soon as the (small) buffer between the processes is filled up.
The threads should be started as soon as the child process is started, and before the parent calls process.waitFor().
The simplest way to do this is the following:
Process process = processBuilder.start();
InputStream stdout = process.getInputStream();
InputStream stderr = process.getErrorStream();
Thread stdoutThread = new Thread() {
#Override
public void run() {
// read stdout here, e.g.
try {
int c;
while (-1 != (c = stdout.read())) {
// do whatever with c
}
} catch (IOException ioex) {
// ...
}
}
};
Thread stderrThread = new Thread() {
// ... same as for stdout
};
stdoutThread.start();
stderrThread.start();
}
Only after both threads have been started you may wait for the child process and join the threads:
int exitValue = process.waitFor();
stdoutThread.join();
stderrThread.join();
There might be more sophisticated ways to work with background threads using the Concurrency Framework introduced in Java 5, but this basic code gives the idea.
I am trying to search when GTA5.exe ends. It launches with "steam://rungameid" protocol, so I can't use Process#waitFor(); method.
The method below checks if (currently) the process is running
public static boolean isProcessRunning(String processName) throws IOException {
ProcessBuilder processBuilder = new ProcessBuilder("tasklist.exe");
Process process = processBuilder.start();
String tasksList = toString(process.getInputStream());
return tasksList.contains(processName);
}
But what I want is something like
waitUntilProcessEnds(String processname) {
}
It could be
while (isProcessRunning) {
Thread.sleep(1000);
}
And, as expected, my JavaFX app freezes.
If I try to run the while method in another Thread, my JavaFX App gives this error:
java.lang.IllegalStateException: Not on FX application thread; currentThread = Thread-6
at com.sun.javafx.tk.Toolkit.checkFxUserThread(Toolkit.java:279)
at com.sun.javafx.tk.quantum.QuantumToolkit.checkFxUserThread(QuantumToolkit.java:444)
at javafx.scene.control.Dialog.show(Dialog.java:294)
at com.thunderstorm.gta5.mod_installer.app.AppController$1$1.run(AppController.java:101)
at java.lang.Thread.run(Thread.java:748)
I also tried this
Platform.runLater();
and this
Platform.setImplicitExit(false);
But nothing different...
How to fix this
This question is edited
There is an API, but it requires Java 9 or newer. Then, you can do it smoothly:
String name = "GTA5.exe";
ProcessHandle.allProcesses()
.filter(ph -> ph.info().command()
.filter(s -> s.regionMatches(true,s.length()-name.length(),name,0,name.length()))
.isPresent())
.findAny()
.ifPresentOrElse(ph -> {
System.out.println("Waiting for "+ph.pid()+" "+ph.info().command().get());
ph.onExit().thenRunAsync(() -> {
// your actual action
}, Platform::runLater);
}, () -> System.out.println("No matching process found"));
ProcessHandle.onExit() returns a CompletableFuture which allows to chain dependent actions, to be performed on the process’ completion. So you don’t need to poll the process yourself.
Further note that Platform.runLater(Runnable) matches the functional signature of the Executor interface which allows us to use thenRunAsync(…, Platform::runLater) to mandate that the dependent action (Runnable) should be executed in the JavaFX Application Thread, once the process has exited.
I think your design is ok, but you're a bit confused by the threads. First when you start your wait method, go ahead and start a new Thread.
new Thread( ()->{
waitUntilProcessEnds(processname);
Platform.runLater( ()-> callBackOnPlatformThread() );
}).start();
Then you need.
public void callBackOnPlatformThread(){
System.out.println("go ahead and modify fx components.");
}
This will start waiting for the process to end on a new thread, so your javafx UI will continue to be responsive. When it is finished, it calls back on the javafx Platform thread, so you can modify components.
This doesn't check if the thread has already been started, so if you have a button that starts the thread, you could start a bunch of them.
What are you trying to achieve? If you want to execute some code when your application ends then you can add a shutdown hook:
Runtime.getRuntime().addShutdownHook(() -> { ... });
If you want to wait until the external process ends, then you can use:
process.waitFor()
This will block your current thread until the external process exits.
Not if your program didn't create the process in the first place.
Java does not provide operating-system level facilities that are not provided in the underlying OS in the first place. In Unix-like systems, for example, "wait for process exit" can only be executed by an ancestor of the process in question. In Windows, for another example, you have to possess a handle on the process, which you would have obtained from creating the process.
You'll have to roll your own. Create a thread which periodically checks for the existence of the process you are monitoring, by whatever means is suitable to that process and your operating environment. Worst-case you'll have to fork a 'ps' command (or similar) and parse the output.
I can't advise on the JavaFX issue; not my area.
Environment: Windows 7
I'm launching an external process with ProcessBuilder. This external program can be called with an argument defining how many CPU cores to use. It then launches as many processes to perform the calculation.
The issue seems to be that the initial called process then immediately terminates. So process.waitFor() does not actually wait for the calculation to complete.
How can I make it wait for the child-process it spwaned? Or how can i wait til all process of a specific name have been terminated?
EDIT due to comments:
ProcessBuilder pb = new ProcessBuilder("myExe", "-i", inputFile, "-o", outputFile, "-np", "4");
pb.directory(new File(tempPath));
Process myProcess = pb.start();
myProcess.waitFor();
Process is a 3-rd party exe file (no GUI).
EDIT 2 Possible workaround (which has another issue):
As a workaround I tried a solution found with google:
while(wait) {
Process p = Runtime.getRuntime().exec(System.getenv("windir") +"\\system32\\"+"tasklist.exe /fi \"imagename eq myExe.exe\"");
BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream(), "US-ASCII"));
while ((line = input.readLine()) != null) {
if (line.startsWith("INFO: No tasks are running")) {
wait = false;
}
}
if (wait) {
Thread.sleep(2000);
}
input.close();
}
This should return a list of all processes with the given name. Problem is the list is always empty when called from Java but works correctly when called from the cli manually. Any ideas why?
Java-like dirty solution
Currently java.lang.Process is rather poor and is getting enriched.
For example, here you can find the feature requests. In java 8,
you already have methods like isAlive and destroyForcibly So
you can try something like the following dirty solution:
Get the id of the main process by reflection.
Get the child processes - specific for Windows
Wait for the found child processes
Steps 2 and 3 can be implemented as one script and executed as
java.lang.Process.waitFor
Work with the executable
I suppose, that the executable is some kind of MPI program. If you have access to the sources, i'd better change it to work correctly and waiting for all threads.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
java/shellscript code to find out if a jar file is already running on current machine
I would love to get a cross-platform solution for this, but even if it's unix only- that would be fine.
The simple solution would be to do this from the shell (Pseudocode)(if ps -aux | grep myJar | print {awk 2}.contains myPID, don't run myProgram.
Now unfortunately our linux team doesn't want a script like that running in production since it can (admittedly) have undesired behaviors.
So what I need is to be able to have a file run, and when it runs see if another program is running. If the program is already running and it's below that time limit, it should prevent the program from running.
A bit of an example:
Myprog.jar -- timeout 5 min
Myprog.jar is in a cron that gets called every 4 minutes,
the first time it's called it launches, the second time it's called it's still running, but since it's not over the timeout, it's fine.
If it's still running when the third check comes through (at 8 minutes into execution) it's killed, and its process is replaced by itself afterwards.
If someone can help me understand how to do this (We've been trying to set up a lock file with limited success)
Thanks!
You could make your program open a dummy file for writing with a FileWriter when your program starts, and keep the file open until the program is finished.
When you now start a second instance of your program, it will also try to open this file for writing, which will throw an IOException, because only one process can have a write handle to a file at the same time.
You could use a port as a semaphore. See this question for more info on that. I think a port would be a good cross-platform solution
You can create a temporary file on a fixed location.
private static final File LOCK_FILE = new File("app.lock");
public static boolean checkIfAlreadyRunning()
{
return LOCK_FILE.exists();
}
public static void createLockFile()
{
LOCK_FILE.createNewFile();
Runnable shutDown = new Runnable()
{
public void run()
{
try
{
LOCK_FILE.delete();
} catch (Exception e) { /* Sad but true */ }
}
};
Runtime.getRuntime().addShutdownHook(new Thread(shutDown));
Thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler()
{
public void uncaughtException(Thread t, Exception e)
{
shutDown.run();
System.exit(-1);
}
});
}
I had exactly the same problem, and it can be pretty tricky to solve. Both File and Socket based approaches can be made to work, but it gets really tricky on some OS's (think of Windows with multiple users in multiple terminal server sessions etc.).
First, determine the scope where you want only one instance. Then decide on a solution.
The ServerSocket method with a fixed port number will allow you one instance per machine (maybe not exactly what you want).
The locking file approach can be tailored to create the locking file in the users temp directoy, so it gives one instance per session/user.
I personally use a combined approach where the locking file specifies a random port and a second instance connects to that port to pass command line parameter to the running instance.
I'm working on a Java program that incorporates Process and Runtime to run several shell scripts for automated testing. All but one of these scripts runs fine, which causes an issue with script calls following it. Ex:
process = runtime.exec("A.sh");
process = runtime.exec("B.sh");
process = runtime.exec("C.sh");
A.sh runs fine and only takes a few seconds to run. B.sh, however, takes a couple minutes to run, and I think this is causing a problem with running C.sh, since they both interact with the same MySQL table and the overlap causes a Communications Link Failure.
Without overloading you with unnecessary information, my question is, how can I wait to ensure a run shell script has been run to completion/termination before moving on to the next exec() call?
What I've tried:
process.waitFor()
This doesn't work, I don't think it waits until the script is completely done
process.wait(long time_period)
This doesn't work since it causes the current thread to wait which results in the remaining shell script calls to get skipped and the next test case to begin prematurely.
The shell script I call that causes the problem is not a simple script, but I didn't write it myself and have little understanding of what it does behind the scenes. The only relevant information I have about it is that it directly connects to the MySQL database in question whereas my program uses java.sql.* to (I believe) remotely connect (although it is a local database on a remote machine).
Edit:
After following a suggestion, I've looked into the Apache Commons Exec and tried a new strategy, unsuccessfully.
ExecuteWatchdog watchdog = new ExecuteWatchdog(300000); //For five minutes
CommandLine cmdline = CommandLine.parse("./directory/shell.sh");
DefaultExecutor executor = setExitValue(0);
executor.setWatchdog(watchdog);
int exitVal = executor.execute(cmdLine);
//A line to log the exit val in another file
My log gives no implication that the shell script was actually run, as the time between a logged statement saying "shell.sh begins" and "test 2 starts" are essentially the same instant, which means the ~2 minute process that shell.sh runs never happens. Where did I go wrong?
I use Apache Commons Exec. Have synchronous and asynchronous execution support. Execution timeout can be set.
First paragraph from their tutorial page:
At this point we can safely assume that you would like to start some
subprocesses from within your Java application and you spent some time
here to do it properly. You look at Commons Exec and think "Wow -
calling Runtime.exec() is easy and the Apache folks are wasting their
and my time with tons of code". Well, we learned it the hard way (in
my case more than once) that using plain Runtime.exec() can be a
painful experience. Therefore you are invited to delve into
commons-exec and have a look at the hard lessons the easy way ...
Advanced usage example (some code is missing like BusinessException and "StreamUtil.closeQuietly", but it could be easily replaced):
ExecuteWatchdog watchdog = new ExecuteWatchdog(EXECUTION_TIMEOUT_IN_MS);
DefaultExecutor executor = new DefaultExecutor();
executor.setWatchdog(watchdog);
executor.setExitValue(0);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ByteArrayOutputStream errorStream = new ByteArrayOutputStream();
executor.setStreamHandler(new PumpStreamHandler(outputStream, errorStream));
try {
log.info(commandLine.toString());
int exitCode = executor.execute(commandLine, (Map<?, ?>) null);
if (exitCode != 0)
throw new BusinessException("Process exited with non-zero exit code.");
return outputStream.toString();
} catch (ExecuteException e) {
String errorStreamStr = null;
if (errorStream.size() != 0)
errorStreamStr = errorStream.toString();
StringBuilder errorMessageBuilder = new StringBuilder();
errorMessageBuilder.append("main.error").append(":\n").append(
e.getMessage()).append("\n\n");
if (errorStreamStr != null) {
errorMessageBuilder.append("additional.error").append(":\n").append(errorStreamStr).append("\n\n");
}
errorMessageBuilder.append("command.line").append(":\n").append(commandLine.toString());
if (log.isDebugEnabled())
log.debug(errorMessageBuilder.toString());
throw new BusinessException(errorMessageBuilder.toString());
} catch (IOException e) {
throw new IllegalStateException(e);
} finally {
StreamUtil.closeQuietly(outputStream, errorStream);
}