JDK 7u25: Solutions to Issues caused by changes to Runtime.exec

Posted by Devika Gollapudi on Oracle Blogs See other posts from Oracle Blogs or by Devika Gollapudi
Published on Fri, 21 Jun 2013 23:06:59 +0000 Indexed on 2013/06/24 16:31 UTC
Read the original article Hit count: 395

Filed under:

The following examples were prepared by Java engineering for the benefit of Java developers who may have faced issues with Runtime.exec on the Windows platform.

Background

In JDK 7u21, the decoding of command strings specified to Runtime.exec(String), Runtime.exec(String,String[]) and Runtime.exec(String,String[],File) methods, has been made more strict. See JDK 7u21 Release Notes for more information.

This caused several issues for applications. The following section describes some of the problems faced by developers and their solutions.


Note: In JDK 7u25, the system property jdk.lang.Process.allowAmbigousCommands can be used to relax the checking process and helps as a workaround for some applications that cannot be changed. The workaround is only effective for applications that are run without a SecurityManager.
See JDK 7u25 Release Notes for more information.

Note: To understand the details of the Windows API CreateProcess call, see: http://msdn.microsoft.com/en-us/library/windows/desktop/ms682425%28v=vs.85%29.aspx


There are two forms of Runtime.exec calls:

  1. with the command as string: "Runtime.exec(String command[, ...])"
  2. with the command as string array: "Runtime.exec(String[] cmdarray [, ...] )"

The issues described in this section relate to the first form of call. With the first call form, developers expect the command to be passed "as is" to Windows where the command needs be split into its executable name and arguments parts first. But, in accordance with Java API, the command argument is split into executable name and arguments by spaces.

Problem 1: "The file path for the command includes spaces"

In the call:

Runtime.getRuntime().exec("c:\\Program Files\\do.exe")

the argument is split by spaces to an array of strings as:

c:\\Program, Files\\do.exe

The first element of parsed array is interpreted as the executable name, verified by SecurityManager (if present) and surrounded by quotations to avoid ambiguity in executable path.

This results in the wrong command:

"c:\\Program" "Files\\do.exe"

which will fail.

Solution:

Use the ProcessBuilder class, or the Runtime.exec(String[] cmdarray [, ...] ) call, or quote the executable path.

Where it is not possible to change the application code and where a SecurityManager is not used, the Java property jdk.lang.Process.allowAmbigousCommands could be used by setting its value to "true" from the command line:

-Djdk.lang.Process.allowAmbigousCommands=true

This will relax the checking process to allow ambiguous input.

Examples:

new ProcessBuilder("c:\\Program Files\\do.exe").start()
Runtime.getRuntime().exec(new String[]{"c:\\Program Files\\do.exe"})
Runtime.getRuntime().exec("\"c:\\Program Files\\do.exe\"")

Problem 2: "Shell command/.bat/.cmd IO redirection"

The following implicit cmd.exe calls:

Runtime.getRuntime().exec("dir > temp.txt")
new ProcessBuilder("foo.bat", ">", "temp.txt").start()
Runtime.getRuntime().exec(new String[]{"foo.cmd", ">", "temp.txt"})

lead to the wrong command:

"XXXX" ">" temp.txt

Solution:

To specify the command correctly, use the following options:

Runtime.getRuntime().exec("cmd /C \"dir > temp.txt\"")
new ProcessBuilder("cmd", "/C", "foo.bat > temp.txt").start()
Runtime.getRuntime().exec(new String[]{"cmd", "/C", "foo.cmd > temp.txt"})

or

Process p = new ProcessBuilder("cmd", "/C" "XXX").redirectOutput(new File("temp.txt")).start();

Problem 3: "Group execution of shell command and/or .bat/.cmd files"

Due to enforced verification procedure, arguments in the following calls create the wrong commands.:

Runtime.getRuntime().exec("first.bat && second.bat")
new ProcessBuilder("dir", "&&", "second.bat").start()
Runtime.getRuntime().exec(new String[]{"dir", "|", "more"})

Solution:

To specify the command correctly, use the following options:

Runtime.exec("cmd /C \"first.bat && second.bat\"")
new ProcessBuilder("cmd", "/C", "dir && second.bat").start()
Runtime.exec(new String[]{"cmd", "/C", "dir | more"})

The same scenario also works for the "&", "||", "^" operators of the cmd.exe shell.

Problem 4: ".bat/.cmd with special DOS chars in quoted params”

Due to enforced verification, arguments in the following calls will cause exceptions to be thrown.:

Runtime.getRuntime().exec("log.bat \">error<\"")
new ProcessBuilder("log.bat", ">error<").start()
Runtime.getRuntime().exec(new String[]{"log.bat", ">error<"})

Solution:

To specify the command correctly, use the following options:

Runtime.getRuntime().exec("cmd /C log.bat \">error<\"")
new ProcessBuilder("cmd", "/C", "log.bat", ">error<").start()
Runtime.getRuntime().exec(new String[]{"cmd", "/C", "log.bat", ">error<"})

Examples:

Complicated redirection for shell construction:

cmd /c dir /b C:\ > "my lovely spaces.txt"

becomes

Runtime.getRuntime().exec(new String[]{"cmd", "/C", "dir \b >\"my lovely spaces.txt\"" });

The Golden Rule:

In most cases, cmd.exe has two arguments: "/C" and the command for interpretation.

© Oracle Blogs or respective owner

Related posts about /Java