8132541: (process) ProcessBuilder support for redirection to discard output
authorrriggs
Wed, 23 Sep 2015 11:18:34 -0400
changeset 32763 c11c2b9b45a5
parent 32762 253adb0f4301
child 32764 1586fc6697da
8132541: (process) ProcessBuilder support for redirection to discard output Summary: Add redirect to NUL or /dev/null depending on the OS Reviewed-by: chegar, martin
jdk/src/java.base/share/classes/java/lang/ProcessBuilder.java
jdk/test/java/lang/ProcessBuilder/Basic.java
--- a/jdk/src/java.base/share/classes/java/lang/ProcessBuilder.java	Wed Sep 23 08:43:51 2015 +0200
+++ b/jdk/src/java.base/share/classes/java/lang/ProcessBuilder.java	Wed Sep 23 11:18:34 2015 -0400
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -33,7 +33,8 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
-
+import java.security.AccessController;
+import java.security.PrivilegedAction;
 /**
  * This class is used to create operating system processes.
  *
@@ -445,6 +446,7 @@
      * <ul>
      * <li>the special value {@link #PIPE Redirect.PIPE}
      * <li>the special value {@link #INHERIT Redirect.INHERIT}
+     * <li>the special value {@link #DISCARD Redirect.DISCARD}
      * <li>a redirection to read from a file, created by an invocation of
      *     {@link Redirect#from Redirect.from(File)}
      * <li>a redirection to write to a file,  created by an invocation of
@@ -459,6 +461,13 @@
      * @since 1.7
      */
     public abstract static class Redirect {
+        private static final File NULL_FILE = AccessController.doPrivileged(
+                (PrivilegedAction<File>) () -> {
+                    return new File((System.getProperty("os.name")
+                            .startsWith("Windows") ? "NUL" : "/dev/null"));
+                }
+        );
+
         /**
          * The type of a {@link Redirect}.
          */
@@ -529,6 +538,28 @@
                 public Type type() { return Type.INHERIT; }
                 public String toString() { return type().toString(); }};
 
+
+        /**
+         * Indicates that subprocess output will be discarded.
+         * A typical implementation discards the output by writing to
+         * an operating system specific "null file".
+         *
+         * <p>It will always be true that
+         * <pre> {@code
+         * Redirect.DISCARD.file() the filename appropriate for the operating system
+         * and may be null &&
+         * Redirect.DISCARD.type() == Redirect.Type.WRITE &&
+         * Redirect.DISCARD.append() == false
+         * }</pre>
+         * @since 9
+         */
+        public static final Redirect DISCARD = new Redirect() {
+                public Type type() { return Type.WRITE; }
+                public String toString() { return type().toString(); }
+                public File file() { return NULL_FILE; }
+                boolean append() { return false; }
+        };
+
         /**
          * Returns the {@link File} source or destination associated
          * with this redirect, or {@code null} if there is no such file.
--- a/jdk/test/java/lang/ProcessBuilder/Basic.java	Wed Sep 23 08:43:51 2015 +0200
+++ b/jdk/test/java/lang/ProcessBuilder/Basic.java	Wed Sep 23 11:18:34 2015 -0400
@@ -738,7 +738,7 @@
      * Remove it from the list of env variables
      */
     private static String removeAixExpectedVars(String vars) {
-        return vars.replace("AIXTHREAD_GUARDPAGES=0,","");
+        return vars.replace("AIXTHREAD_GUARDPAGES=0,", "");
     }
 
     private static String sortByLinesWindowsly(String text) {
@@ -785,8 +785,8 @@
                 equal(entry.getKey(), key);
                 equal(entry.getValue(), value);
             }
-            check(! kIter.hasNext() &&
-                  ! vIter.hasNext());
+            check(!kIter.hasNext() &&
+                    !vIter.hasNext());
 
         } catch (Throwable t) { unexpected(t); }
     }
@@ -815,9 +815,9 @@
 
     static void checkRedirects(ProcessBuilder pb,
                                Redirect in, Redirect out, Redirect err) {
-        equal(pb.redirectInput(),  in);
+        equal(pb.redirectInput(), in);
         equal(pb.redirectOutput(), out);
-        equal(pb.redirectError(),  err);
+        equal(pb.redirectError(), err);
     }
 
     static void redirectIO(ProcessBuilder pb,
@@ -862,6 +862,7 @@
         Redirect[] redirects =
             { PIPE,
               INHERIT,
+              DISCARD,
               Redirect.from(ifile),
               Redirect.to(ifile),
               Redirect.appendTo(ifile),
@@ -884,6 +885,10 @@
         equal(INHERIT.toString(), "INHERIT");
         equal(INHERIT.file(), null);
 
+        equal(DISCARD.type(), Redirect.Type.WRITE);
+        equal(DISCARD.toString(), "WRITE");
+        equal(DISCARD.file(), new File((Windows.is() ? "NUL" : "/dev/null")));
+
         equal(Redirect.from(ifile).type(), Redirect.Type.READ);
         equal(Redirect.from(ifile).toString(),
               "redirect to read from file \"ifile\"");
@@ -926,6 +931,12 @@
         checkRedirects(pb, INHERIT, INHERIT, INHERIT);
 
         //----------------------------------------------------------------
+        // Check DISCARD for stdout,stderr
+        //----------------------------------------------------------------
+        redirectIO(pb, INHERIT, DISCARD, DISCARD);
+        checkRedirects(pb, INHERIT, DISCARD, DISCARD);
+
+        //----------------------------------------------------------------
         // Check setters and getters agree
         //----------------------------------------------------------------
         pb.redirectInput(ifile);
@@ -943,7 +954,8 @@
         THROWS(IllegalArgumentException.class,
                () -> pb.redirectInput(Redirect.to(ofile)),
                () -> pb.redirectOutput(Redirect.from(ifile)),
-               () -> pb.redirectError(Redirect.from(ifile)));
+               () -> pb.redirectError(Redirect.from(ifile)),
+               () -> pb.redirectInput(DISCARD));
 
         THROWS(NullPointerException.class,
                 () -> pb.redirectInput((File)null),
@@ -980,7 +992,7 @@
             ProcessResults r = run(pb);
             equal(r.exitValue(), 0);
             equal(fileContents(ofile),
-                  "standard error" + "standard output");
+                    "standard error" + "standard output");
             equal(fileContents(efile), "");
             equal(r.out(), "");
             equal(r.err(), "");
@@ -1051,6 +1063,79 @@
         }
 
         //----------------------------------------------------------------
+        // DISCARDing output
+        //----------------------------------------------------------------
+        {
+            setFileContents(ifile, "standard input");
+            pb.redirectOutput(DISCARD);
+            pb.redirectError(DISCARD);
+            ProcessResults r = run(pb);
+            equal(r.exitValue(), 0);
+            equal(r.out(), "");
+            equal(r.err(), "");
+        }
+
+        //----------------------------------------------------------------
+        // DISCARDing output and redirecting error
+        //----------------------------------------------------------------
+        {
+            setFileContents(ifile, "standard input");
+            setFileContents(ofile, "ofile-contents");
+            setFileContents(efile, "efile-contents");
+            pb.redirectOutput(DISCARD);
+            pb.redirectError(efile);
+            ProcessResults r = run(pb);
+            equal(r.exitValue(), 0);
+            equal(fileContents(ofile), "ofile-contents");
+            equal(fileContents(efile), "standard error");
+            equal(r.out(), "");
+            equal(r.err(), "");
+            ofile.delete();
+            efile.delete();
+        }
+
+        //----------------------------------------------------------------
+        // DISCARDing error and redirecting output
+        //----------------------------------------------------------------
+        {
+            setFileContents(ifile, "standard input");
+            setFileContents(ofile, "ofile-contents");
+            setFileContents(efile, "efile-contents");
+            pb.redirectOutput(ofile);
+            pb.redirectError(DISCARD);
+            ProcessResults r = run(pb);
+            equal(r.exitValue(), 0);
+            equal(fileContents(ofile), "standard output");
+            equal(fileContents(efile), "efile-contents");
+            equal(r.out(), "");
+            equal(r.err(), "");
+            ofile.delete();
+            efile.delete();
+        }
+
+        //----------------------------------------------------------------
+        // DISCARDing output and merging error into output
+        //----------------------------------------------------------------
+        {
+            setFileContents(ifile, "standard input");
+            setFileContents(ofile, "ofile-contents");
+            setFileContents(efile, "efile-contents");
+            pb.redirectOutput(DISCARD);
+            pb.redirectErrorStream(true);
+            pb.redirectError(efile);
+            ProcessResults r = run(pb);
+            equal(r.exitValue(), 0);
+            equal(fileContents(ofile), "ofile-contents");   // untouched
+            equal(fileContents(efile), "");                 // empty
+            equal(r.out(), "");
+            equal(r.err(), "");
+            ifile.delete();
+            ofile.delete();
+            efile.delete();
+            pb.redirectErrorStream(false);                  // reset for next test
+        }
+
+        //----------------------------------------------------------------
         // Testing INHERIT is harder.
         // Note that this requires __FOUR__ nested JVMs involved in one test,
         // if you count the harness JVM.