Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-4506936

RMID and Runtime.exec() fail under Win32 when java.home contains spaces

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Duplicate
    • Icon: P3 P3
    • None
    • 1.4.0
    • core-libs
    • x86
    • windows_98



      Name: bsT130419 Date: 09/25/2001


      java version "1.4.0-beta2"
      Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.0-beta2-b77)
      Java HotSpot(TM) Client VM (build 1.4.0-beta2-b77, mixed mode)


      The problem is (as far as I have investigated) Win32-specific. All testing was
      performed under Windows 98 (first edition). It involves an interaction between
      the code in the public class java.lang.Runtime (unchanged for JDK 1.4) and a
      change made in JDK 1.4 to the package-private class java.lang.Win32Process.

      In java.lang.Runtime, we are concerned with the difference between exec()
      functions that accept the command to execute as a single String and those which
      accept a String[]. Each of these functions eventually (in Win32 environments)
      result in calls to the Win32Process class. The difference we are concerned with
      has to do with the fact that the Runtime.exec() functions that accept a single
      command string first tokenize that string into an array of substrings, then
      they call the exec() function which accepts an array, in order to prevent the
      duplication of code. Nothing wrong with that, but it is this "pre-tokenizing"
      of commands specified as a single string rather than an array which is part of
      the interaction problem. The tokenizer employed is a standard StringTokenizer
      that uses the default delimiter set, which is " \t\n\r\f": the space character,
      the tab character, the newline character, the carriage-return character, and
      the form-feed character. The delimiter characters themselves are not treated as
      tokens. As a result of this pre-tokenizing operation, a command string such as
      the following:

          C:\PROGRAM FILES\JAVASOFT\JRE\1.4\bin\java

      would be converted into the argument array:

          element 0> C:\PROGRAM
          element 1> FILES\JAVASOFT\JRE\1.4\bin\java

      Note that the space between "PROGRAM" and "FILES" is lost. Quoting the command
      string, as in:

          "C:\PROGRAM FILES\JAVASOFT\JRE\1.4\bin\java"

      would result in the argument array:

          element 0> "C:\PROGRAM
          element 1> FILES\JAVASOFT\JRE\1.4\bin\java"

      Again, the intervening space is lost. Note that this behavior does not occur
      when calling the exec() functions that accept an argument array. With those
      functions, the original command string is not pre-tokenized, resulting in:

          element 0> C:\PROGRAM FILES\JAVASOFT\JRE\1.4\bin\java

      or:

          element 0> "C:\PROGRAM FILES\JAVASOFT\JRE\1.4\bin\java"

      The other part of the interaction problem occurs in java.lang.Win32Process. In
      the constructor of this object, any necessary quoting of arguments that contain
      spaces is performed. If no spaces are present, the command line is reassembled
      as is from the argument array, adding a space between each argument. So the
      following array:

          element 0> C:\PROGRAM
          element 1> FILES\JAVASOFT\JRE\1.4\bin\java

      would become:

          C:\PROGRAM FILES\JAVASOFT\JRE\1.4\bin\java

      and the quoted version:

          element 0> "C:\PROGRAM
          element 1> FILES\JAVASOFT\JRE\1.4\bin\java"

      would become:

          "C:\PROGRAM FILES\JAVASOFT\JRE\1.4\bin\java"

      thereby fixing the problem produced earlier and reconstituing the original
      command line. Quoted or not, the resulting string apparently works fine when it
      comes time to create a Win32 process. This is all great for arguments that do
      not contain spaces, and works the same in JDK 1.3.1 or JDK 1.4. The difference
      is what happens when arguments do contain spaces. Prior to JDK 1.4, such
      arguments were quoted in their entirety (unless they were already quoted), so
      that the array:

          element 0> C:\PROGRAM FILES\JAVASOFT\JRE\1.4\bin\java

      would become:

          "C:\PROGRAM FILES\JAVASOFT\JRE\1.4\bin\java"

      Arguments already quoted were left as is. In JDK 1.4, arguments already quoted
      are still left as is, but unquoted arguments that contain spaces are treated
      different than they were previously. Such arguments have each region of one or
      more spaces quoted individually, so that the array:

          element 0> C:\PROGRAM FILES\JAVASOFT\JRE\1.4\bin\java

      would become:

          C:\PROGRAM" "FILES\JAVASOFT\JRE\1.4\bin\java

      Unfortunately, a string of this type does not seen to make the Win32
      CreateProcess() API happy. When it fails, the exception that is eventually
      returned includes the API error code, but the returned error code is 0 (no
      error) because the native Win32 module (Win32Process_md.c) that makes the call
      does not save the value of the GetLastError() API before calling another API
      (CloseHandle()) which succeeds, thereby resetting the associated error value.
      Nevertheless, regardless of the actual error description, it is clearly the
      result of using this type of command string, as can be seen from examining and
      running the following test program:

      import java.io.*;

      public class ExecTest
      {
      public static void main(String[] args) {
      try {
      // Section 1 - Works in JDK 1.3.1 and 1.4
      // String command = System.getProperty("java.home");
      // command += File.separator + "bin" + File.separator
      + "java";

      // Section 2 - Works in JDK 1.3.1 but causes Runtime.exec() to *FAIL* in 1.4
      String[] command = new String[1];
      command[0] = System.getProperty("java.home");
      command[0] += File.separator + "bin" + File.separator
      + "java";

      // Section 3 - Works in JDK 1.3.1 and 1.4
      // String[] command = new String[1];
      // command[0] = "\"";
      // command[0] += System.getProperty("java.home");
      // command[0] += File.separator + "bin" + File.separator
      + "java";
      // command[0] += "\"";

      Process process = Runtime.getRuntime().exec(command);

      BufferedReader procOut = new BufferedReader(
      new InputStreamReader(process.getInputStream()));

      String strOut = procOut.readLine();

      while (strOut != null) {
      System.out.println(strOut);
      strOut = procOut.readLine();
      }

      process.waitFor();
      System.exit(0);
      } catch (Exception e) {
      e.printStackTrace(System.out);
      System.exit(1);
      }
      }
      }

      Compiling and running the test program under Windows using JDK 1.3.1 produces
      no error, just the standard output from a successfully launched JVM with no
      arguments. However, compiling and running the test program under JDK 1.4 (using
      a JRE path that includes spaces) results in an execption similar to the
      following:

          java.io.IOException: CreateProcess: C:\PROGRAM" "FILES\JAVASOFT\JRE\1.4
      \bin\java error=0
              at java.lang.Win32Process.create(Native Method)
              at java.lang.Win32Process.<init>(Win32Process.java:72)
              at java.lang.Runtime.execInternal(Native Method)
              at java.lang.Runtime.exec(Runtime.java:551)
              at java.lang.Runtime.exec(Runtime.java:477)
              at java.lang.Runtime.exec(Runtime.java:443)
              at ExecTest.main(ExecTest.java:26)

      Looking at the source of the test program, if you comment out Section 2 and
      uncomment Section 1 or Section 3, you can see that either using a single
      command string or quoting each array element in its entirety will avoid the
      problem that leads to the CreateProcess() failure. Also, using a JVM located in
      a path that does not contain spaces will also avoid the problem.

      SO, why is this problem such a big deal, you ask? Why can't we just either
      avoid the Runtime.exec() functions that accept an command string array, or make
      sure we quote our paths under Win32? Well, we can do those things in our own
      code, and in any code we can modify, but we can't reasonably fix RMID.
      Specifically, sun.rmi.server.Activation.initCommand(), which creates the
      command string array that will eventually be passed to Runtime.exec() to create
      the process for each RMI activation group. This command string array is not
      quoted (it uses the same technique as in Section 2 of the test program), so the
      CreateProcess() failure described above will occur under Win32 when the
      java.home property contains one or more spaces, as it virtually always will.

      This means we can't launch activatable services under Win32 unless the 1.4 JRE
      is installed to a path structure that does not contain spaces. Unfortunately,
      it always gets installed under "C:\Program Files\JavaSoft\JRE\1.4" by default.
      We can't reasonably modify java.lang.Runtime or java.lang.Win32Process to fix
      the problem either. Any other internal Sun code that uses a similar technique
      to launch a JVM (or any other JDK/JRE binary) also presents a problem. Needless
      to say, while we can still continue to develop our code, we cannot ship a JDK
      1.4 compatible version until this bug is fixed.

      The following 3 solutions are recommended (individually or collectively) for
      the next release:

          1) Restore the code in java.lang.Win32Process to JDK 1.3.1 status. However,
      I assume the change was made for some particular reason, perhaps compatibility
      with later versions of Windows, I don't know. If the change cannot be reversed,
      it should be implemented only for those Windows versions that require it.
      java.lang.Runtime.exec(String[]) specifically tested in those instances.
          2) Modify java.lang.Runtime to quote command string array elements in their
      entirety, if they contain spaces and prior to any tokenizing. Otherwise, move
      the tokenizing and integrate in with java.lang.Win32Process. Again, if there is
      a platform-specific reason this can't be done, at least do it for Win95/98/NT
      prior to Win2K. I can see the argument that the programmer should know that
      they need to do this, but on the other hand, you're already helping them out in
      a similar fashion in Win32Process.
          3) Modify sun.rmi.server.Activation.initCommand() to properly quote the
      path in java.home if it contains spaces, at least on the Win32 platforms
      mentioned.

      In addition, the following change is also recommended:

          1) Modify Win32Process_md.c to save the value returned from the GetLastError
      () API immediately after calling the CreateProcess() API, so that it can
      actually be returned in the generated exception, rather than being overwritten
      by the intervening CloseHandle() API call.

      Well, I've had my say. What do you folks think?
      (Review ID: 132285)
      ======================================================================

            kkladkosunw Konstantin Kladko (Inactive)
            bstrathesunw Bill Strathearn (Inactive)
            Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

              Created:
              Updated:
              Resolved:
              Imported:
              Indexed: