views:

853

answers:

1

This is a followup question to my other question : http://stackoverflow.com/questions/2434125/run-bat-file-in-java-and-wait

The reason i am posting this as a separate question is that the one i already asked was answered correctly. From some research i did my problem is unique to my case so i decided to create a new question. Please go read that question before continuing with this one as they are closely related.

Running the proposed code blocks the program at the waitFor invocation. After some research i found that the waitFor method blocks if your process has output that needs to be proccessed so you should first empty the output stream and the error stream. I did those things but my method still blocks. I then found a suggestion to simply loop while waiting the exitValue method to return the exit value of the process and handle the exception thrown if it is not, pausing for a brief moment as well so as not to consume all the CPU. I did this:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class Test {

 public static void main(String[] args) {
  try {
   Process p = Runtime.getRuntime().exec(
     "cmd /k start SQLScriptsToRun.bat" + " -UuserName -Ppassword"
       + " projectName");
   final BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream()));
   final BufferedReader error = new BufferedReader(new InputStreamReader(p.getErrorStream()));
   new Thread(new Runnable() {

    @Override
    public void run() {
     try {
      while (input.readLine()!=null) {}
     } catch (IOException e) {
      e.printStackTrace();
     }
    }
   }).start();
   new Thread(new Runnable() {

    @Override
    public void run() {
     try {
      while (error.readLine()!=null) {}
     } catch (IOException e) {
      e.printStackTrace();
     }
    }
   }).start();
   int i = 0;
   boolean finished = false;
   while (!finished) {
    try { 
     i = p.exitValue();
     finished = true;
    } catch (IllegalThreadStateException e) {
     e.printStackTrace();
     try {
      Thread.sleep(500);
     } catch (InterruptedException e1) {
      e1.printStackTrace();
     }
    }
   }
   System.out.println(i);
  } catch (IOException e) {
   e.printStackTrace();
  }
 }
}

but my process will not end! I keep getting this error:

java.lang.IllegalThreadStateException: process has not exited

Any ideas as to why my process will not exit? Or do you have any libraries to suggest that handle executing batch files properly and wait until the execution is finished?

+2  A: 

Start cmd with the /c switch instead of /k and get rid of the start:

Process p = Runtime.getRuntime().exec( 
   "cmd /c SQLScriptsToRun.bat" + " -UuserName -Ppassword" 
   + " projectName");

/k tells cmd: “Run that command and then stay open”, while /c says “Run that command and then exit.”

/k is for interactive use where you want an initializing batch file and afterwards still use the console.

Your main problem here, however, is that you are creating yet another process by using start. To run a batch file this is totally unnecessary and prevents you from knowing when the batch was run completely, since Java has a reference to the original cmd process you started, not the one you spawned with start.

In principle, this now looks like the following:

  1. Java program starts
  2. Java program runs cmd and instructs it to run start foo.bat and stay open for interactive input (/k)
  3. Java memorizes the process ID (PID 42) to later reference that process

    • cmd (PID 42) starts
    • cmd (PID 42) runs start foo.bat
    • start foo.bat launches another instance of cmd, since that's what should happen to run batch files
      • cmd (PID 57005) starts
      • cmd (PID 57005) runs foo.bat
      • cmd (PID 57005) exits (This marks the event you'd like to know about)
    • cmd (PID 42) shows the prompt and obediently waits for input (unbeknownst to them the prompt is never seen by a user and no input will ever come ... but cmd (PID 42) waits ...)
  4. Java likes to know whether the process is finished and checks PID 42

  5. Yup, it's still there. Now what?

What you want (and what above change will do) is:

  1. Java program starts
  2. Java program runs cmd and instructs it to run foo.bat and close after running the command (/c)
  3. Java memorizes the process ID (PID 42) to later reference that process

    • cmd (PID 42) starts
    • cmd (PID 42) runs foo.bat
    • cmd (PID 42) exits
  4. Java likes to know whether the process is finished and checks PID 42

  5. Hooray, the process is gone, the batch file has been run.
Joey
this would cause the process to exit immediately. What i want is to wait until it is done.
Savvas Dalkitsis
@sav: Remove the `start` as well.
Joey
Yes well I thought of that but unfortunately if you try it yourself you will see that it won't execute the bat file... I tried all combinations. None works. Some will block forever, some will not bring up the cmd window at all...
Savvas Dalkitsis
@Sav: What exactly does that batch file do? If it prompts for input or spawns other processes asynchronously (like `start` does) you'll run into the same issues again.
Joey
@Savvas: I just tried it and using `/c` instead of `/k` without the `start` does indeed what it should do. Any other problems you have are not with Java but may be related to what exactly your batch file does.
Joey
hm you may be right...the bat file has these contents:FOR /F %%x in (%CD%\Scripts\List.txt) do sqlcmd -Slocalhost\SQL %1 %2 -d project_%3 -i %CD%\SQLScripts\%%xFOR /F %%x in (%CD%\Scripts\EXEC_List.txt) do sqlcmd -Slocalhost\SQL %1 %2 -d project_%3 -Q "EXEC %%x"exitso i am spawning new processes (sqlcmd.exe) but they block. so the process for the bat file should finish when every subprocess finished, am i right?
Savvas Dalkitsis
@Savvas: Yes, as long as `sqlcmd` terminates eventually. However, if it's waiting for input somewhere this won't be the case.
Joey
well it doesn't... And the thing is that if i call the command without the start, i get nothing... the java process object is running (i see that when i call the waitFor or the hack above) but i get no cmd window opening. It is frustrating...
Savvas Dalkitsis
and the problem with the /c option is when used in conjunction with "start". because of course using the start command returns immediately and using /c makes cmd return immediately :)
Savvas Dalkitsis
@Savvas: There shouldn't be any window opening. When launching th process from Java you don't get a window. You're capturing its output after all, so what's the point of a separate window? That's hardly surprising. The question is whether the batch will terminate eventually when you run it standalone.
Joey
hm ok i will try it again and wait for some minutes to see how it goes. as it is when run normally (from explorer) it takes some minutes to finish. Maybe i haven't waited long enough. thanks. i will post again if i have issues or accept the answer if it works :)
Savvas Dalkitsis
you sir are correct! thank you...
Savvas Dalkitsis