1 /*
   2  * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 /**
  25  * @test
  26  * @bug 6921885
  27  * @run main/othervm SiblingIOEHandle
  28  * @summary inherit IOE handles and MS CreateProcess limitations (kb315939)
  29  */
  30 
  31 import java.io.BufferedReader;
  32 import java.io.File;
  33 import java.io.IOException;
  34 import java.io.InputStreamReader;
  35 import java.util.concurrent.BrokenBarrierException;
  36 import java.util.concurrent.CyclicBarrier;
  37 
  38 public class SiblingIOEHandle {
  39     private static enum APP {
  40         B, C;
  41     }
  42     private static File stopC = new File(".\\StopCs.txt");
  43     private static String SIGNAL = "B child reported.";
  44     private static String JAVA_EXE = System.getProperty("java.home")
  45         + File.separator + "bin"
  46         + File.separator + "java";
  47 
  48     private static String[] getCommandArray(String processName) {
  49         String[] cmdArray = {
  50             JAVA_EXE,
  51             "-cp",
  52             System.getProperty("java.class.path"),
  53             SiblingIOEHandle.class.getName(),
  54             processName
  55         };
  56         return cmdArray;
  57     }
  58 
  59     public static void main(String[] args) {
  60         if (!System.getProperty("os.name").startsWith("Windows")) {
  61             return;
  62         }
  63 
  64         if (args.length > 0) {
  65             APP app = APP.valueOf(args[0]);
  66             switch (app) {
  67             case B:
  68                 performB();
  69                 break;
  70             case C:
  71                 performC();
  72                 break;
  73             }
  74             return;
  75         }
  76         performA(true);
  77         performA(false);
  78     }
  79 
  80     static boolean procClaunched = false;
  81 
  82     private static void waitAbit() {
  83         try {
  84             Thread.sleep(0);
  85         } catch (InterruptedException ex) {
  86             // that was long enough
  87         }
  88     }
  89     private static boolean waitBarrier(CyclicBarrier barrier) {
  90         while (true) try {
  91             barrier.await();
  92             return true;
  93         } catch (InterruptedException ex) {
  94             continue;
  95         } catch (BrokenBarrierException ex) {
  96             ex.printStackTrace();
  97             return false;
  98         }
  99     }
 100 
 101     private static void performA(boolean fileOut) {
 102         try {
 103             stopC.delete();
 104             ProcessBuilder builderB = new ProcessBuilder(
 105                     getCommandArray(APP.B.name()));
 106 
 107             File outB = null;
 108             if (fileOut) {
 109                 outB = new File("outB.txt");
 110                 builderB.redirectOutput(outB);
 111             }
 112             builderB.redirectErrorStream(true);
 113 
 114             final CyclicBarrier barrier = new CyclicBarrier(2);
 115             Thread procCRunner = new Thread(new Runnable() {
 116                 @Override public void run() {
 117                     try {
 118                         if (waitBarrier(barrier)) {
 119                             waitAbit();
 120                             // Run process C next to B ASAP to make an attempt
 121                             // to capture the B-process IOE handles in C process.
 122                             Runtime.getRuntime().exec(getCommandArray(APP.C.name()));
 123                             procClaunched = true;
 124                         }
 125                     } catch (IOException ex) {
 126                         ex.printStackTrace();
 127                     }
 128                 }
 129             });
 130             procCRunner.start();
 131 
 132 
 133             if (!waitBarrier(barrier)) {
 134                 throw new Error("Catastrophe in process A! Synchronization failed.");
 135             }
 136             // Run process B first.
 137             Process processB = builderB.start();
 138 
 139             while (true) try {
 140                 procCRunner.join();
 141                 break;
 142             } catch (InterruptedException ex) {
 143                 continue;
 144             }
 145 
 146             if (!procClaunched) {
 147                 throw new Error("Catastrophe in process A! C was not launched.");
 148             }
 149 
 150             processB.getOutputStream().close();
 151             processB.getErrorStream().close();
 152 
 153             if (fileOut) {
 154                 try {
 155                     processB.waitFor();
 156                 } catch (InterruptedException ex) {
 157                     throw new Error("Catastrophe in process B! B hung up.");
 158                 }
 159                 System.err.println("Trying to delete [outB.txt].");
 160                 if (!outB.delete()) {
 161                     throw new Error("Greedy brother C deadlock! File share.");
 162                 }
 163                 System.err.println("Succeeded in delete [outB.txt].");
 164             } else {
 165                 System.err.println("Read stream start.");
 166                 try (BufferedReader in = new BufferedReader( new InputStreamReader(
 167                              processB.getInputStream(), "utf-8")))
 168                 {
 169                     String result;
 170                     while ((result = in.readLine()) != null) {
 171                         if (!SIGNAL.equals(result)) {
 172                             throw new Error("Catastrophe in process B! Bad output.");
 173                         }
 174                     }
 175                 }
 176                 System.err.println("Read stream finished.");
 177             }
 178             // If JDK-6921885 is not fixed that point is unreachable.
 179             // Test timeout exception.
 180 
 181             // write signal file to stop C process.
 182             stopC.createNewFile();
 183         } catch (IOException ex) {
 184             throw new Error("Catastrophe in process A!", ex);
 185         }
 186     }
 187 
 188     private static void performB() {
 189         System.out.println(SIGNAL);
 190     }
 191 
 192     private static void performC() {
 193         // If JDK-7147084 is not fixed the loop is 5min long.
 194         for (int i = 0; i < 5*60; ++i) {
 195             try {
 196                 Thread.sleep(1000);
 197                 // check for sucess
 198                 if (stopC.exists())
 199                     break;
 200             } catch (InterruptedException ex) {
 201                 // that is ok. Longer sleep - better effect.
 202             }
 203         }
 204     }
 205 }