Skip to main content

Can Java execute batch file outside of current JVM in separate process tree

15 replies [Last post]
cknight19
Offline
Joined: 2005-04-04
Points: 0

Hi,

Does anyone know how to run programs from Java as separate processes that will not die when the spawning java program exits (JVM exits).

The problem I have with using Runtime.exec is it spawns only child processes under the current running JVM, thus when the origonal program that called Runtime.exec ends so does all child processes.

Basically I want to start a DOS batch file from my Java application, my Java application will then immediately exit (calling System.exit(0) ). The batch program will continue to run, its does some file clean up, create's some new files and deletes the old jar (containing the main app), it then rebuilds the main app jar and and executes the main class and then exits itself.

I've also tried the apache.tomcat.jni.Proc :-

long pool = Pool.create( new Long(0).longValue() );
long pa = Procattr.create( pool );
Procattr.dirSet( pa, "c:\\temp\\updater\\");
Procattr.cmdtypeSet( pa, Proc.APR_SHELLCM );
Procattr.detachSet( pa, 1 );
long proc = Proc.alloc( pool );
Proc.create( proc, "test.bat", new String[]{"test.bat"}, null, pa, pool );
System.exit(0);

The detach option doesn't work, if I take it off then the bat file runs and stops the JVM exiting, if I leave it in the batch file never gets called.

Is this possible in Java. Can java start master process on Windows XP JDK1.5+?

Cheers
Chris.

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
billwright
Offline
Joined: 2007-04-13
Points: 0

Actually, I'm having the "usual complaint". I can start up a separate window and run my server program in it, using the techniques already shown in this forum, but I can't kill the process. I use the following:

Process p = Runtime.getRuntime().exec("cmd /c start c:\\myprocess.exe");

This does what I want, but when I execute this:

p.destroy();

It has no effect. Even when my Java program ends, this process and its command window remaining running. How can I kill this process? Is there a better way to be starting and stopping other windows programs?

Thanks for any help,
Bill

fred34
Offline
Joined: 2004-06-19
Points: 0

Hi, it looks like a similar problem to the one already discussed here. The reason your process.exe isn't closing is because its not the one that was started by java. When you write "cmd /c whatever.exe" you start TWO processes, the cmd.exe and whatever.exe. From java's point of view it started cmd, so the Process p is a handle on this process, so when u call p.destroy(), this is what gets destroyed, the second process is left running. The easiest way around this is to start myProcess directly from the exec command. If your process neither requires or produces console output then this is the way to go. p.destroy() will work properly.

However, there is one problem with this technique in that the process will be given the same input and output streams as the java process and as explained elsewhere on this page, those buffers are limited in size, and if the process' output exceeds a certain size and java is NOT draining the buffer, it can crash or freeze the other process. If you can redirect the output and input streams to our server process, do so (through command line redirection >c:\somefile.txt). Otherwise, one possible option is to make a adhoc console window for it in Java with Swing. This is not that complex, and would mostly require a JTextArea rigged to display the OutputStream from the process so that the outputstream buffer doesn't block. Again if you don't care about what it writes to the console, then you don't need to do this and can write a 10 line piece of java code that periodically checks for data in the outputstream and just drain it.

Since it is a server application, is there any way to adapt it to accept shutdown commands through a socket? If so then this maybe the easiest solution as you could then easily write a shutdown hook (see the Runtime documentation) that would kill the server process over TCP/UDP when System.exit() is called.

regwhitton
Offline
Joined: 2006-09-06
Points: 0

It's true that Windows doesn't have a traditional concept of child processes, but it does have one - but only for "console" programs. Kill the console and you kill the program running in it.

Console program are special - they have a stdio, stderr and stdin, and they have Control-C and Control-Break handers. Normal Windows programs don't. Java is a console program, javaw is not.

If you start a console program from another console program then they normally both use the same console window. Start a console program from a non console program and a fresh console will be started for it. You also can get a new console by using the "start" command from "cmd". This is a bit like starting a new process group.

I think that from a console java program (not javaw) you will need to use "cmd /c start ..." to get a batch program running in a separate console.

fred34
Offline
Joined: 2004-06-19
Points: 0

Okay, I've solved it:

mrthornton is right in that the trouble is with output/input stream handles, not with child processes. I've got a fix here which I've tested on JDK1.4.2 and on JDK6_u1 so it's definitely backward compatible. This fix also avoids creating "dummy" cmd windows, as far as the user is concerned, they see nothing of the batch file running unless they are watching Task Manager, which will list the cmd.exe process. Also this solution does away with all the "child of a child of a child" nonsense. If the same streams are passed to each process, then it doesn't matter if the actual batch file is nested 10 processes deep, as soon as the root process terminates, all the others will cascade terminate.

The key is that when a process is launched, as mrthornton said, it is passed all the handles from the starting process, i.e. java. If this dies, then the current command (e.g.ping ) continues to operate until termination, but when that happens the command interpreter (cmd.exe) finds the streams are closed and terminates. It doesn't have anything to do with child processes.

Using my solution I was able to write a short java program that started a batch file, terminated. The batch file used the ping command to pause for a few seconds and then executed the same java class, creating an infinite loop, i.e. proving that you can indeed achieve a solution that is 1) Neat: no clumsy cmd.exe windows lurking on screen and 2) very short.

Here we go:

[code]
public class Main {
public static void main(String[] args) throws Exception {
Process p=Runtime.getRuntime().exec("cmd /c c:\\test.bat");
System.out.println("done");
System.out.println("quitting");
System.exit(0);
}
}
[/code]

Save/compile this as Main.java somewhere obvious, like the root of c: in this case.

Now create c:\test.bat and stick this lot in it:

@echo off
PING 1.1.1.1 -n 1 -w 5000 >nul
java -cp "c:\ " Main

That’s it. No start commands or other strange stuff. The two important parts are the /c option for cmd.exe and the @echo off in the script, take either out and it won’t work. This will create and infinite loop of Java processes starting a batch and terminating which in turn pauses and starts Java. Its silly, but it demonstrates the point. Kill it from Task Manager. Also note that with this scenario, there is no console for the batch file, or the new java process. If you need one, then change

[code]
Process p=Runtime.getRuntime().exec("cmd /c c:\\test.bat");
[/code]
to:

[code]
Process p=Runtime.getRuntime().exec("cmd /c start c:\\test.bat");
[/code]

and stick an “exit” command at the end of the batch file. This will cause a cmd.exe console window to appear so the new java process has a console to read/write to, but it will still disappear at the end of the script. Hope this helps.

cknight19
Offline
Joined: 2005-04-04
Points: 0

Thanks for all the posts, I'm getting closer to a solution. Indeed I thought Fred34 had it cracked....until I tried the code in my main app instead of the tester app I wrote.

Fred34 your code works really well for small batch jobs. The java program exits fine and the batch continues with the ping statement and then reruns the origonal Java. I must have been missing the @echo off line (probably for testing). I've created a very simple cut down of the code I'm writing and using your suggestions managed to get it working.

By the way DON'T run this from an IDE (at least not Intellj) as it doesn't work the IDE doesn't allow the java to exit until the batch has exited. Must be the stream hooks again. So the main class MUST be run from either command prompt or jar file.

I guess this is a partial fix and a good post for others having similar problems as it works fine for small batch processes. However my batch file does a lot of stuff, ping, changes dir, moves files, deletes files and executes a jar.

It doesn't work for my real program it always falls over on the "move files" part of the batch....some files get moved then the cmd ends and its always different files that it manages to move.

I'm convinced that the JVM when it exited tried to kill the CMD but because its running the PING command it takes Windows time to kill the process (just like when u task manage a running process).....as soon as the PING finishes Windows can kill the batch, so the batch gets a chance to change dir and move some files but not all.

Very bizare, but when I run the code in my main app using
Runtime.getRuntime().exec( "cmd /c c:\\temp\\updater\\pre.bat c:\\temp\\updater\\" );
It doesn't work (the CMD dies on the move), however in my tester app it works fine.

Now in my main app if I use
Runtime.getRuntime().exec( "cmd /c start c:\\temp\\updater\\pre.bat c:\\temp\\updater\\" );
it works all the time..........although I get a console window, I dont really mind as it exits when finished even though it looks a little unprofessional......however because I still think Windows is trying to kill my batch I've lost all confidence in using this method commercially (dont want clients ringing me up).

I'm in the process of writing a C++ JNI dll to do the job instead.......but Java's still trying to kill that too.......mighty impressive garbage collection.

Below is the contents of my batch file if anyone thinks they can spot the problem :-

@echo off

PING 1.1.1.1 -n 1 -w 10000 >nul

cd %1

del /F /Q *.*

cd new
copy /Y /V *.* ..\
cd..

RD /Q /S new

PING 1.1.1.1 -n 1 -w 1000 >nul

start RelatisSysTray.bat

exit

Ok so the code goes like this. I JAR up the Main Class and put it in "C:\temp" along with jdic.dll, tray.dll, IeEmbed.exe, a properties file and another batch file which runs the Main JAR again...it contains this code :-

start /min C:/jdk1.5.0_03/bin/javaw -classpath C:/relatis_systray/rs.jar; system.RelatisSysTray
exit

In "C:\temp\updater" sits my batch file", now for testing purposes I've created a "C\temp\new" folder which contains a copy of all the files in "C:\temp" (but in the main app they come from a server as a zip....which gets unzipped by java before it exits).

I start the whole thing off using the second batch file (in the real app this is copied to the windows startup folder).

If you can replicate that setup you'll find the whole things falls over at the move statements.

I'll post my C++ JNI dll code when I've ironed out the wrinkles as it seems to do the Job flawlessly.....however I'd much rather have a JDK solution as maintaining C++ is a pain as I don't use it every day.

fred34
Offline
Joined: 2004-06-19
Points: 0

I had similar problems running out of Netbeans when I first started hacking round with getting calc.exe to execute. You are correct with stream hooks being the problem here. Netbeans starts java as an external process, with the Stdout, stdin and stderr redirected to netbeans' own output window. I was getting a small class to start calc and then quit which worked, but netbeans wouldn't close until calc was killed too, since it would have held the streams open. I didn't consider this a problem as the program would not normally be run from a development environment.

I'm still convinced that Java does not actively try to kill processes when it terminates. They terminate of their own accord when any necessary streams are closed. I saw no evidence of Java trying to kill cmd.exe or calculator; I believe the explanation is still that as under my example, the streams may not be valid once Java terminates, any command in the batch file which requires them terminate thus terminating the batch file with any errors being dropped on the floor.

If it works every time using the extra "start" to ensure a console window then go with it, you can use start /min to minimise the thing and keep clients from toying with it, especially if you have Java display a progress bar/splash screen/anything to keep people entertained (I think Romain Guy has some blog entries about this kind of stuff somewhere).

I haven't had chance to try the code you posted out, but I would suspect its something to do with some part of the script failing and not having anywhere to write an error or display messages. When running under "start" with the console displayed, does anything actually appear in it? the echo off should stop commands appearing, so anything else will be generated by one part of the script.

Ok, so to cut this short, here are my suggestions, in order of implementation cost:

1) Ensure the batch file doesn't try to write stuff to the screen. If nothing is written and no input/errors are required/produced, then your problems *should* go away.

2) Redirect the streams from within Java, the Javadoc for java.lang.Process is actually very informative here:

"The methods that create processes may not work well for special processes on certain native platforms, such as native windowing processes, daemon processes, Win16/DOS processes on Microsoft Windows,[b] or shell scripts. The created subprocess does not have its own terminal or console. All its standard io (i.e. stdin, stdout, stderr) operations will be redirected to the parent process through three streams (getOutputStream(), getInputStream(), getErrorStream())[/b]. The parent process uses these streams to feed input to and get output from the subprocess. Because some native platforms only provide limited buffer size for standard input and output streams, failure to promptly write the input stream or read the output stream of the subprocess may cause the subprocess to block, and even deadlock." (emphasis added)

Especially important are the bits on limited buffer size, etc,etc. Lets say the Java process exits, but the output stream given to the batch file is limited to a certain size, this won't get emptied so (I haven't tested this, its mostly conjecture) if you're batch is writing a lot of stuff (Im looking at you Mr.copy command) then it could freeze and fail when the stream buffer is full. This could also explain why the copy command is terminating at different points.

Ensure the batch is silent, and mute any commands that aren't either through specific "quiet" switches or by redirecting to nul as in the case of ping.

cknight19
Offline
Joined: 2005-04-04
Points: 0

Turns out even though quiet mode was on and "echo off" the move/copy command was still producing some output!!! As you suggested I've changed my batch file to redirect all output (I might have gone overboard) :-

@echo off

PING 1.1.1.1 -n 1 -w 10000 >nul

cd %1 >nul

del /F /Q *.* >nul

move /Y new\*.* >nul

RD /Q /S new >nul

PING 1.1.1.1 -n 1 -w 1000 >nul

start RelatisSysTray.bat

exit

[b]And now the whole process works every time whether I use start or not. Now my users can have a self updating system tray app without any ugle DOS windows.[/b]

Can't thank you enough for all the help and guidance Fred, and everyone else that posted. I'd have been well stumped ages ago. Now I can go bin a dll....

cknight19
Offline
Joined: 2005-04-04
Points: 0

Just had an after thought......None of my user will appreciate all the effort and help from this forum that went in to providing them with a seamless auto updating system tray app....damn users!!! Still I can't fix them (shame) but I've decided to rename the batch file to :-

fred.bat

So from call centre's across the country (UK) and the odd lucky household there will be a small testiment sitting someone on their PC, silently working some magic.

fred34
Offline
Joined: 2004-06-19
Points: 0

no problem, I'm also UK based so one up for the home team!

Final point to note..careful if you ever change this batch file or whatever, because if something new tries to write to the console, it'll screw up again.

Glad I helped.

himanshukapil
Offline
Joined: 2008-08-12
Points: 0

Hello Fred34 / cknight
I am having a similar problem. I hope you can help me out.
I have a requirement in which I am running a 2 tier client-server application over socket communication.
Now based on some message I need to run a batch script on server. This batch script will in turn call a java program. Now the issue is that I need to kill that java program after some time when a message to close that comes in.
I tried following this..
***************
Process p = Runtime.getRuntime().exec("C:\\TradingEdge\\BondLink\\6.1.0\\LoadTestCases\\DevlForwardInquiryReqResTest.bat garot1 10 3 \"UBS,ABN\"" );

try{
Thread.currentThread().sleep(60000);
}catch(InterruptedException ie){
}
p.destroy();

**************

But this doesnt kill the underlying running java program. I can understand from the discussion on this thread that two are separate processes but only if we use some thing like "cmd /c start xxx.bat".

Can you please help how can I achive to shut down that running java program ?

Thanks

fred34
Offline
Joined: 2004-06-19
Points: 0

I tried executing a separate process on Windows XP under JDK 5 and 6 and couldn't get the behaviour you're talking about, in both cases, I was able to start a new process and have the Java process quit using System.exit(0) and leave the spawned process executing. I can't reproduce the problem, I was using ProcessBuilder to do it, which is exactly what Runtime.exec() does anyway.

regwhitton
Offline
Joined: 2006-09-06
Points: 0

Try:

[code]Runtime.getRuntime().exec("cmd /c start test.bat");[/code]

or if you don't want to see a window open

[code]Runtime.getRuntime().exec("cmd /c start /MIN test.bat");[/code]

For some reason I have to put an "exit" at the end of the batch file.

For details type the following at the DOS prompt: [code]cmd /?[/code] or [code]start /?[/code]

cknight19
Offline
Joined: 2005-04-04
Points: 0

Hi,

Thanks for the reply.

Runtime.exec is no good as it can only ever spawn child processes. If the main java class exits then all child processes get killed even if they are still doing something.

You can try it yourself

public static void main(String[] args)
{
Runtime.getRuntime().exec("cmd /c start c:\\temp\\test.bat" );
System.out.println("main should exit but doesn't, until batch is finished, if you try and force main to exit then bat exits too.");
}

In test.bat put the following code :-

ping -n 10 -w 1000 17.0.0.2

this causes a 10 second delay before the bat file finishes.

I want the Java class (JVM) to exit and leave the batch file running......

I'm begining to think that Java can't do this but if anyone knows a guru i could ask that would be great.

mthornton
Offline
Joined: 2003-06-10
Points: 0

I'm a bit bemused as to what is going on here because Windows doesn't really have a concept of child process (unlike Unix). In fact the usual complaint in the bug parade is that killing the 'parent' doesn't kill any of its children on Windows.
Are you using java.exe or javaw.exe?
Later ...
I think the explanation is this:

Java always creates processes with stdin, stdout, stderr redirected to the creating process. If the child process doesn't use those standard streams then the child process can continue after the parent dies.
On the other hand if the child uses stdout, for example, then the parent must remain to collect that output. If the parent dies then either the child will block on filling the output buffer or it may get an exception to indicate the loss of the other end of the pipe.
I suspect that the Windows command processor is terminating when it notices that its output pipe is dead.
To get around this, use the command shell to run the batch script detached. In this case your batch script is a 'child' of the child of your java application.
Look at the documentation for the Windows "start" command (type start /? at a prompt).

Message was edited by: mthornton

cknight19
Offline
Joined: 2005-04-04
Points: 0

Thanks for the reply "mthornton", it sounds like you know what I'm trying to do.

I thought the child of a child would work too but it doesn't as they all become connected. Here's my current setup

Java TestClass calls
[code]Runtime.getRuntime().exec("cmd /c start /min c:\\temp\\one.bat" );
System.exit(0);
[/code]

one.bat is :-
start /min c:\\temp\\two.bat
exit

two.bat is (simple version, actually does much more). The ping line is a 10sec sleep :-
ping -n 10 -w 1000 17.0.0.2
start /min C:/jdk1.5.0_03/bin/javaw -classpath C:/rs.jar; AnotherClass
exit

This all works fine and AnotherClass is started and running. However, TestClass will never exit, until AnotherClass exits, if I kill TestClass then AnotherClass exits. And regardless of which state the above event sequence is in they are all linked together (if one of them exits then they all exit). This leads me to believe that they are all child processes of each other and all will be cleaned up when the JVM that started it all exits, as a kind of garbage collection.

Please try the above code sequence, its faily easy to setup and for me it proves that Runtime.exec can't be used for this task.

Its possible you are right about the stdin, stout and stderr pipes being shared between all the children thus linking them together, but I've no idea how to stop this?

I've also tried javaw and java when starting TestClass but both do the same.

Any further thoughts would be grately appreciated.