0

I am following a book on Java 'Java: The Complete Reference, 11th Edition" which has the following code

class ExecDemoFini {
    public static void main(String args[]) {
        Runtime r = Runtime.getRuntime();
        Process p = null;

        try {
            p = r.exec("notepad");
            p.waitFor();
        } catch(Exception e) {
            System.out.println("Error executing notepad.");
        }
        System.out.println("Notepad returned " + p.exitValue());
    }
}

According to the book, this program, when run in a Windows environment, is supposed to wait for notepad to terminate. But it does not. It returns an exit code of 0 immediately. I read on another post that had a similar question, that maybe the process that is starting (in this case, notepad) it creates another subprocess and then terminates itself. Is that the case here? Is that how notepad works? If not, I want to know why this example does not work in my computer. All the other examples have been accurate so far.

I use Windows 11 and openJDK version 11.0.0.2 .

8
  • 1
    Note that if you are facing something that might throw an InterruptedException, you probably don't want to silently handle it. This isn't a problem with your example but it might be worth considering in the future. Commented May 1 at 14:00
  • 1
    @dan1st catch(Exception e) is in over half of all SO questions, and putting that comment in each and every one of them strikes me as a really bad idea. Maybe not place drive-by irrelevant comments. Yes, 'catch everything and ignore it' is bad, but I don't think this is the way to fight that particular unfortunate aspect of java culture. Commented May 1 at 14:28
  • 2
    Did you already have a notepad running? If yes depending on all sorts of things, running notepad.exe might just do: "1. Tell already running notepad to open a new tab, 2. wait for it to acknowledge this, 3. exit yourself - the already running notepad did the thing". Also, this code is deplorable in many ways, I doubt the quality of that book of yours (bad exception handling, failure to use ProcessBuilder, failure to at least write 'notepad.exe', bizarre style to save Runtime to a separate variable first - all in a tiny snippet!). What happens if you make that "notepad.exe" instead? Commented May 1 at 14:42
  • 1
    Actually it looks like the book then goes on to doing the same thing with ProcessBuilder, that time using the file extension to the executable, though with a similarly lazy/obfuscatory approach to exceptions Commented May 1 at 15:24
  • 2
    Note well that there is a distinction between waiting for the child process to exit and waiting for the Notepad window to close. Behavior such as you seem to be observing often signals that the process you waited for is not the one that you observe still to be running. The one still running may be a child of that process, or even an altogether separate one. Commented May 1 at 19:43

1 Answer 1

1

The book is probably written + tested with Windows 10 or earlier. In earlier versions of Windows, notepad is a native executable with single document interface.

Note that the call r.exec("notepad"); relies on the use of system variable PATHEXT to resolve the binary. On my machine PATHEXT looks at the Path with these file extensions for "notepad".

PATHEXT=.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC

So the old sample launches notepad.exe as long as not finding another file extension in the path.

Windows 11

Windows 11 notepad is an app from Microsoft Store and has an application alias "Notepad.exe". It appears to launch subprocesses. The code sample you have works but it does not await the children to complete.

Here is example launch which uses ProcessBuilder and awaits child processes to exit:

public static void main(String... args) throws IOException, InterruptedException, ExecutionException {
    // Ideally use "notepad.exe" here that you don't accidentally run notepad.com if such exists 

    start("notepad");
}

public static int start(String ... cmd) throws IOException, InterruptedException, ExecutionException {

    System.out.println("start "+Arrays.toString(cmd));

    // Launch and wait:
    ProcessBuilder pb = new ProcessBuilder(cmd);
    pb.redirectErrorStream(true);   // No STDERR => merge to STDOUT
    Process p = pb.start();

    // No STDIN, just closes to signal end of input
    // (replace _ by a variable on older JDK)
    try(OutputStream _ = p.getOutputStream()) {/*EMPTY*/}

    // Process STDOUT
    p.getInputStream().transferTo(System.out);

    // Await process exit
    int rc = p.waitFor();

    // Show results + STDOUT+STDERR
    System.out.println("Exit PID "+p.pid()+" EXITCODE "+rc +' '+(rc == 0 ? "OK":"**** ERROR ****")+" command:"+Arrays.toString(cmd));

    // Await process descendants exit
    waitForDescendants(p.toHandle());

    // Optional: if (rc != 0) throw new RuntimeException("Process failed code: "+rc+" command: "+Arrays.toString(cmd));
    return rc;
}

/**
 * Pause current thread if there are any known running subprocesses of handle
 */
private static void waitForDescendants(ProcessHandle handle) throws InterruptedException, ExecutionException {
    var waiters = handle.descendants()
                        .map(ProcessHandle::onExit).toList();
    System.out.println("pid "+handle.pid() +" has "+waiters.size()+" descendants");
    for (CompletableFuture<ProcessHandle> cf : waiters) {
        ProcessHandle subp = cf.get();
        System.out.println("waitForDescendants "+handle.pid() +" child ended "+subp.pid());
        waitForDescendants(subp);
    }
}

The above code should work in Windows 10, and the call to waitForDescendants will do nothing. On Windows 11 it may print details of the subprocess, something like:

start [notepad]
Exit PID 59236 EXITCODE 0 OK command:[notepad]
pid 59236 has 1 descendants
waitForDescendants 59236 child ended 47908
pid 47908 has 0 descendants
Sign up to request clarification or add additional context in comments.

2 Comments

Hey man thank you so much. This was really helpful.
@MrinmoySadhukhan Great. It helps the SO community if you upvote or accept helpful answers, and leave comments if they don't.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.