I have a java application that downloads and runs another program. The problem I'm running into is that when it runs the program there is no visual; however, the process shows up in the Windows Task Manager.
Here's the relevant execution code:
String[] cmd = {System.getProperty("java.io.tmpdir") + PACKAGE_PATH + onePackage};
log.info("Package downloaded to: " + cmd[0]);
new ProcessBuilder(cmd[0]).start();
I've also used a Runtime.exec() and that produced the same results.
Here's a Commons Exec version that produces the same result:
String line = "cmd.exe start /c " + "\"" + cmd[0] + "\"";
CommandLine cmdLine = CommandLine.parse(line);
DefaultExecutor executor = new DefaultExecutor();
int exitValue = executor.execute(cmdLine);
One last bit of detail, it works fine on my Win7 desktop from Eclipse, but not on Windows Server 2008 R2.
You can do it with the following code:
import java.io.IOException;
public class TestClass {
public static void main(String[] args) throws IOException {
Runtime rt = Runtime.getRuntime();
Process pr = rt.exec("cmd.exe /c mspaint.exe");
}
}
This will result in a mspaint.exe getting started up in the foreground compared to the background. However since you do it through cmd.exe that process ends immediately after mspaint.exe starts up resulting in the java program to finish its execution regardless of the status of the mspaint.exe which may be ok for your situation.
If you need to wait for it to finish, I would recommend looking at Commons Exec
Related
Environment
Windows 10
Java 1.8
Process
I am running a 7zip's zip task.
The process takes 2 to 3 hours to complete.
Exception
java.lang.IllegalThreadStateException: process has not exited
at java.lang.ProcessImpl.exitValue(ProcessImpl.java:443)
at java.lang.ProcessImpl.waitFor(ProcessImpl.java:452at
My code
int exitValue = -1;
Process start = null;
try
{
ProcessBuilder processBuilder = new ProcessBuilder(commands);
start = processBuilder.start();
try(BufferedReader ipBuf = new BufferedReader(new InputStreamReader(start.getInputStream())))
{
String line = null;
while ((line = ipBuf.readLine()) != null)
{
LOGGER.info(line);
}
}
try(BufferedReader errBuf = new BufferedReader(new InputStreamReader(start.getErrorStream())))
{
String line;
while ((line = errBuf.readLine()) != null)
{
LOGGER.warning(line);
}
}
start.waitFor();
exitValue = start.exitValue();
}
finally
{
if (start != null)
{
start.destroy();
}
}
return exitValue;
I'm unable to find the root cause of this issue.
Note: I've tried this process with a similar demo instance on the same
machine and it works fine.
Please help me resolve this, Thanks.
There are two parts to your problem:
The JDK has a bug which causes an exception to be thrown when a Windows process returns an exit code of 259.
The command that you pass to ProcessBuilder exits with an exit code of 259 when it shouldn't.
Tackling each point in turn:
The bug in the JDK is caused by the following flawed logic in the Windows-specific implementation of Process.waitFor(): First, it waits until the process exits. Then, it calls exitValue() to get the exit value from the process. But unfortunately exitValue() gets the exit value and then checks if it's a special value that indicates the process hasn't exited. Since waitFor() knows that the process has exited, it should get the exit value directly instead of calling this method which does an unwanted check. Hopefully the JDK developers will fix this bug soon.
You should be using the command-line version of 7-zip, 7z.exe which exits with a set of well-defined exit values (so it never returns 259).
If your problem is caused by the Windows ProcessBuilder exit code 259 bug then there are workarounds available: all you need to do to make sure that your sub-process does not exit with status code 259 and Windows JRE won't report java.lang.IllegalThreadStateException.
You can easily reproduce this issue by executing the following command with Runtime.getRuntime().exec(cmd) or ProcessBuilder(cmd):
String[] cmd = {"cmd.exe /c exit /b 259"};
If you have written the code for the sub-process then just edit your code so that exit code is never set to 259.
If you have not written the code for the sub-process then a rather hacky workaround is to wrap your Java sub-process launch with a "CMD.EXE" and mini-script which adapts non-zero sub-process exit back to exit codes 0 or 1:
String[] fixed = new String[] { "cmd.exe", "/c",
"(call "+String.join(" ", cmd)+ ") || (echo !!! DETECTED ERROR!!! && exit 1)" };
Note: I'm no expert on CMD. The above fix definitely won't work for certain commands or punctuation (eg those with quotes / spaces etc), and because it runs under CMD.EXE environment settings the outcome might be different to direct launch from the calling JVM.
Here is an example class you could test with:
/** Examples to test with and without the fix:
java Status259 "cmd.exe /c exit /b 0"
java Status259 "cmd.exe /c exit /b 25"
java Status259 "cmd.exe /c exit /b 259"
java Status259 %JAVA_HOME%\bin\java -cp your.jar Status259$StatusXXX 0
java Status259 %JAVA_HOME%\bin\java -cp your.jar Status259$StatusXXX 33
java Status259 %JAVA_HOME%\bin\java -cp your.jar Status259$StatusXXX 259
*/
public class Status259 {
public static class StatusXXX {
public static void main(String ... args) {
int status = args.length > 0 ? Integer.parseInt(args[0]) : 0;
System.out.println("StatusXXX exit code: "+status);
System.exit(status);
}
}
public static int exec(String[] cmd) throws IOException, InterruptedException {
System.out.println("exec "+Arrays.toString(Objects.requireNonNull(cmd)));
ProcessBuilder pb = new ProcessBuilder(cmd);
// No STDERR => merge to STDOUT - or call redirectError(File)
pb.redirectErrorStream(true);
Process p = pb.start();
// send sub-process STDOUT to the Java stdout stream
try(var stdo = p.getInputStream()) {
stdo.transferTo(System.out);
}
int rc = p.waitFor();
System.out.println("exec() END pid="+p.pid()+" CODE "+rc +' '+(rc == 0 ? "OK":"**** ERROR ****"));
return rc;
}
public static void main(String ... args) throws IOException, InterruptedException {
// COMMENT OUT NEXT LINE TO SEE EFFECT OF DIRECT LAUNCH:
args = fixStatus259(args);
int rc = exec(args);
System.exit(rc);
}
private static String[] fixStatus259(String[] cmd) {
System.out.println("fixStatus259 "+Arrays.toString(cmd));
return new String[] {
"cmd.exe", "/c",
"(call "+String.join(" ", cmd)+ ") || (echo !!! DETECTED ERROR!!! && exit 1)"
};
}
}
Trying to build a basic launcher for a java game. I'm building the proper command to run the application. When the following command executes in the launcher, the launcher closes as expected, but the command doesn't appear to work - either it doesn't work or the game launches and immediately crashes.
When I print this same command to the console and copy/paste it into console and execute manually, it works perfectly.
/**
*
*/
protected void launch(){
currentStatusMsg = "Launching...";
String cmd = "java -jar";
cmd += " -Djava.library.path=\"" +nativesDirectory.getAbsolutePath() + "\"";
cmd += " \""+applicationJar.getAbsolutePath() + "\"";
System.out.println(cmd);
try {
Runtime rt = Runtime.getRuntime();
Process pr = rt.exec(cmd);
//closeLauncher();
BufferedReader input = new BufferedReader(new InputStreamReader(pr.getInputStream()));
String line=null;
while((line=input.readLine()) != null) {
System.out.println(line);
}
int exitVal = pr.waitFor();
System.out.println("Exited with error code "+exitVal);
} catch(Exception e) {
System.out.println(e.toString());
e.printStackTrace();
}
}
I tried adding something to read the output, but nothing is printed.
I was originally using the following format instead, but it has the same effect:
Process pr = Runtime.getRuntime().exec(new String[]{
"java",
"-Djava.library.path=\"" +nativesDirectory.getAbsolutePath() + "\"",
"-jar",
applicationJar.getAbsolutePath()});
Update I realized I was closing the launcher before allowing the debug code to run. The system only prints: "Exited with error code 1"
I finally was able to get the subprocess error to print. It states:
Exception in thread "main" java.lang.UnsatisfiedLinkError: no lwjgl in java.library.path
However, it should be available because the command I'm executing includes the library path, and when this exact command is run manually, it works fine.
the java command launcher is not a shell. don't use quoting and space separated commands because it won't end well. put each argument into a separate String without any extra quoting, and use the exec(String[]) method.
I have a java program in which i have this code,
JOptionPane.showConfirmDialog(null, "TEST");
String pathToJar = ClassRewriter.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath()+"ClassRewriter.class";
System.out.println(pathToJar);
ProcessBuilder pb = new ProcessBuilder("javaw "+pathToJar);
Process process = pb.start();
I have a java program in a class called classrewriter, and i am trying to start that program from within its main method but it doesnt seem to work.
public int runCommand(String command) throws Exception
{
Process s= Runtime.getRuntime().exec(command);
return s.exitValue();
}
So you write:
runCommand("java -jar "+pathToJar);
But it isn't recommend, because it isn't supported on all OSes (for example linux or mac).
I would like to be able to launch VI from within my Java program and wait for the user to quit VI before proceeding. Here's the code snippet that I have currently:
...
String previewFileName="test.txt"; // the file to edit
CommandLine cmdLine = new CommandLine("/usr/bin/vi");
cmdLine.addArgument(previewFileName);
cmdLine.addArgument(">/dev/tty");
cmdLine.addArgument("</dev/tty");
Executor executor = new DefaultExecutor();
try
{
DefaultExecuteResultHandler resultHandler = new ResetProcessResultHandler(cmdLine);
executor.execute(cmdLine, resultHandler);
} catch (IOException e)
{
throw new Error("Cannot execute command: /usr/bin/vi " + previewFileName, e);
}
log.info("waiting...");
cmdLine.wait();
log.info("...done");
...
private class ResetProcessResultHandler extends DefaultExecuteResultHandler
{
private final CommandLine mCommandLine;
public ResetProcessResultHandler(CommandLine pCommandLine)
{
mCommandLine = pCommandLine;
}
public void onProcessComplete(int exitValue)
{
log.info("Command complete rc(" + exitValue + ")");
if (exitValue != 0)
{
throw new RuntimeException("VI command error [rc=" + exitValue + "] " );
}
mCommandLine.notify();
}
public void onProcessFailed(ExecuteException e)
{
if (e.getExitValue() != 0)
{
log.error("launch VI error " + e.toString());
throw new RuntimeException("VI command failed [" + e.getCause() + "] ");
}
else
{
log.info("VI complete rc(" + e.getExitValue() + ")");
}
mCommandLine.notify();
}
}
I receive output:
Vim: output is not to a terminal
Vim: input is not from a terminal
But then I see the screen painted as if VI had started; and VI doesn't read characters I type.
So ... redirecting from /dev/tty isn't doing the trick.
Someone must have done this before - help!
Thanks,
Mark
However since Java 1.7 you can use the next example to transparently redirect and have full console functionality
System.out.println("STARTING VI");
ProcessBuilder processBuilder = new ProcessBuilder("/usr/bin/vi");
processBuilder.redirectOutput(ProcessBuilder.Redirect.INHERIT);
processBuilder.redirectError(ProcessBuilder.Redirect.INHERIT);
processBuilder.redirectInput(ProcessBuilder.Redirect.INHERIT);
Process p = processBuilder.start();
// wait for termination.
p.waitFor();
System.out.println("Exiting VI");
This will allow you to open VI transparently for JVM 1.7+.
When Java runs a program via Runtime.exec() (and this is what commons-exec does in the end), it connects the program's input, output and error streams to your Java app as input/output streams. Such a stream is certainly not a terminal, you can't for example move the text cursor in it (since it doesn't have any), change text colors, or detect if Shift key is pressed (since it's just a stream of bytes and not a physical keyborad). So, an interactive app like vi can't really function under such conditions like in a terminal.
By the way, I'm not sure if the command line args you supply are parsed by the shell or passed directly to the program. In the latter case your redirection to /dev/tty couldn't possibly work even if there was a way for Java to somehow allow the program to replace Java's connected streams with something else.
As an aside, it seems a bit strange why you would like to run vi from inside a Java program.
So I guess the best solution is to execute a terminal emulator like konsole or gnome-terminal or xterm and let it run vi by passing corresponding argument on its command line (e.g. konsole -e vi). In this case the terminal's window should pop up and vi could function inside it. Of course, it won't work if you're on a headless server, but then running vi can't be useful anyway.
I'm not sure how to do it with commons-exec,
But standard Java should be something along the lines of...
String[] command = {"/usr/bin/vi", "test.txt"};
Process vimProcess = Runtime.getRuntime().exec(command);
vimProcess.waitFor();
This will cause the current thread to wait for the process to complete. You can also use
vimProcess.getInputStream(), getOutputStream() and getErrorStream() to redirect those to log files or wherever you want it to go.
See here for more details.
http://docs.oracle.com/javase/6/docs/api/java/lang/Runtime.html
Hopefully this helps.
Could you please help me to resolve this issue.
I have a Java code which runs the rsync command using Runtime object.
I am running the below code at source machine, If there is any rsync connectivity problem during sync at target machine, the code should receive exit value, but that is not happening now.
String rsyncCommand = "rsync –abv <source> <remoteAddr:dest>"
Runtime rt = Runtime.getRuntime ();
rt.exec(rsyncCommand);
To give you more details:
When I run the rsync command directly(not through java code) in source machine and if I kill the rsync process at target machine using kill -9 option during sync, the rsync process at source will exit with exit message.
But if I run the rsync through my java code and if I kill the process during the sync at target, it is not receiving any exit message. The java and rsync process are still in running mode. But not doing any tasks.
What is the difference in running the command through java and directly through command prompt?
Any one has similar kind of problem with rsync, do we have any other options to run the rsync through java, I tried with “ProcessBuilder” as well.
Please provide me some pointers to solve this issue.
Thanks for the response, i gave only sample code, below is the complete code which i am using in my java.
Runtime rt = Runtime.getRuntime();
Process proc = null;
try {
proc = rt.exec(rsyncCommand);
InputStream stderr = proc.getErrorStream();
InputStreamReader isrErr = new InputStreamReader(stderr);
BufferedReader brErr = new BufferedReader(isrErr);
InputStream stdout = proc.getInputStream();
InputStreamReader isrStd = new InputStreamReader(stdout);
BufferedReader brStd = new BufferedReader(isrStd);
String val = null;
while ((val = brStd.readLine()) != null) {
System.out.println(val);
}
while ((val = brErr.readLine()) != null) {
System.out.println(val);
}
int exitVal = proc.waitFor();
} catch (Exception e) {
e.printStackTrace();
}
if you do this and the process is not finished yet you will not receive exit value
Process process = rt.exec(rsyncCommand);
int exitValue = process.exitValue();
instead you should use
int exitValue = process.waitFor()
then the thread will wait until the process returns exit value
Your invocation of exec() is incorrect, it should specify the parameters directly, something like:
Runtime rt = Runtime.getRuntime ();
rt.exec(new String[]{"rsync", "-abv", "<source>", "<remoteAddr:dest>"});
exec doesn't do any parsing of the command line, so it's trying to exec a command called "rsync –abv " (as a single string)