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 package jdk.testlibrary; 25 26 import java.io.ByteArrayOutputStream; 27 import java.io.IOException; 28 import java.io.OutputStream; 29 import java.io.OutputStreamWriter; 30 import java.io.PrintStream; 31 import java.io.PrintWriter; 32 import java.lang.management.ManagementFactory; 33 import java.lang.management.RuntimeMXBean; 34 import java.lang.reflect.Field; 35 import java.lang.reflect.Method; 36 import java.util.ArrayList; 37 import java.util.Collections; 38 import java.util.concurrent.ExecutionException; 39 import java.util.concurrent.Future; 40 import java.util.concurrent.Phaser; 41 import java.util.concurrent.TimeUnit; 42 import java.util.concurrent.TimeoutException; 43 import java.util.function.Predicate; 44 45 import sun.management.VMManagement; 46 47 public final class ProcessTools { 48 private static final class LineForwarder extends StreamPumper.LinePump { 49 private final PrintStream ps; 50 private final String prefix; 51 LineForwarder(String prefix, PrintStream os) { 52 this.ps = os; 53 this.prefix = prefix; 54 } 55 @Override 56 protected void processLine(String line) { 57 ps.println("[" + prefix + "] " + line); 58 } 59 } 60 61 private ProcessTools() { 62 } 63 64 /** 65 * <p>Starts a process from its builder.</p> 66 * <span>The default redirects of STDOUT and STDERR are started</span> 67 * @param name The process name 68 * @param processBuilder The process builder 69 * @return Returns the initialized process 70 * @throws IOException 71 */ 72 public static Process startProcess(String name, 73 ProcessBuilder processBuilder) 74 throws IOException { 75 Process p = null; 76 try { 77 p = startProcess(name, processBuilder, null, -1, TimeUnit.NANOSECONDS); 78 } catch (InterruptedException | TimeoutException e) { 79 // can't ever happen 80 } 81 return p; 82 } 83 84 /** 85 * <p>Starts a process from its builder.</p> 86 * <span>The default redirects of STDOUT and STDERR are started</span> 87 * <p> 88 * It is possible to wait for the process to get to a warmed-up state 89 * via {@linkplain Predicate} condition on the STDOUT 90 * </p> 91 * @param name The process name 92 * @param processBuilder The process builder 93 * @param linePredicate The {@linkplain Predicate} to use on the STDOUT 94 * Used to determine the moment the target app is 95 * properly warmed-up. 96 * It can be null - in that case the warmup is skipped. 97 * @param timeout The timeout for the warmup waiting 98 * @param unit The timeout {@linkplain TimeUnit} 99 * @return Returns the initialized {@linkplain Process} 100 * @throws IOException 101 * @throws InterruptedException 102 * @throws TimeoutException 103 */ 104 public static Process startProcess(String name, 105 ProcessBuilder processBuilder, 106 final Predicate<String> linePredicate, 107 long timeout, 108 TimeUnit unit) 109 throws IOException, InterruptedException, TimeoutException { 110 Process p = processBuilder.start(); 111 StreamPumper stdout = new StreamPumper(p.getInputStream()); 112 StreamPumper stderr = new StreamPumper(p.getErrorStream()); 113 114 stdout.addPump(new LineForwarder(name, System.out)); 115 stderr.addPump(new LineForwarder(name, System.err)); 116 final Phaser phs = new Phaser(1); 117 if (linePredicate != null) { 118 stdout.addPump(new StreamPumper.LinePump() { 119 @Override 120 protected void processLine(String line) { 121 if (linePredicate.test(line)) { 122 if (phs.getRegisteredParties() > 0) { 123 phs.arriveAndDeregister(); 124 } 125 } 126 } 127 }); 128 } 129 Future<Void> stdoutTask = stdout.process(); 130 Future<Void> stderrTask = stderr.process(); 131 132 try { 133 if (timeout > -1) { 134 phs.awaitAdvanceInterruptibly(0, timeout, unit); 135 } 136 } catch (TimeoutException | InterruptedException e) { 137 stdoutTask.cancel(true); 138 stderrTask.cancel(true); 139 throw e; 140 } 141 142 return p; 143 } 144 145 /** 146 * Pumps stdout and stderr from running the process into a String. 147 * 148 * @param processBuilder 149 * ProcessHandler to run. 150 * @return Output from process. 151 * @throws IOException 152 * If an I/O error occurs. 153 */ 154 public static OutputBuffer getOutput(ProcessBuilder processBuilder) 155 throws IOException { 156 return getOutput(processBuilder.start()); 157 } 158 159 /** 160 * Pumps stdout and stderr the running process into a String. 161 * 162 * @param process 163 * Process to pump. 164 * @return Output from process. 165 * @throws IOException 166 * If an I/O error occurs. 167 */ 168 public static OutputBuffer getOutput(Process process) throws IOException { 169 ByteArrayOutputStream stderrBuffer = new ByteArrayOutputStream(); 170 ByteArrayOutputStream stdoutBuffer = new ByteArrayOutputStream(); 171 StreamPumper outPumper = new StreamPumper(process.getInputStream(), 172 stdoutBuffer); 173 StreamPumper errPumper = new StreamPumper(process.getErrorStream(), 174 stderrBuffer); 175 176 Future<Void> outTask = outPumper.process(); 177 Future<Void> errTask = errPumper.process(); 178 179 try { 180 process.waitFor(); 181 outTask.get(); 182 errTask.get(); 183 } catch (InterruptedException e) { 184 Thread.currentThread().interrupt(); 185 return null; 186 } catch (ExecutionException e) { 187 throw new IOException(e); 188 } 189 190 return new OutputBuffer(stdoutBuffer.toString(), 191 stderrBuffer.toString()); 192 } 193 194 /** 195 * Get the process id of the current running Java process 196 * 197 * @return Process id 198 */ 199 public static int getProcessId() throws Exception { 200 201 // Get the current process id using a reflection hack 202 RuntimeMXBean runtime = ManagementFactory.getRuntimeMXBean(); 203 Field jvm = runtime.getClass().getDeclaredField("jvm"); 204 205 jvm.setAccessible(true); 206 VMManagement mgmt = (sun.management.VMManagement) jvm.get(runtime); 207 208 Method pid_method = mgmt.getClass().getDeclaredMethod("getProcessId"); 209 210 pid_method.setAccessible(true); 211 212 int pid = (Integer) pid_method.invoke(mgmt); 213 214 return pid; 215 } 216 217 /** 218 * Get platform specific VM arguments (e.g. -d64 on 64bit Solaris) 219 * 220 * @return String[] with platform specific arguments, empty if there are 221 * none 222 */ 223 public static String[] getPlatformSpecificVMArgs() { 224 String osName = System.getProperty("os.name"); 225 String dataModel = System.getProperty("sun.arch.data.model"); 226 227 if (osName.equals("SunOS") && dataModel.equals("64")) { 228 return new String[] { "-d64" }; 229 } 230 231 return new String[] {}; 232 } 233 234 /** 235 * Create ProcessBuilder using the java launcher from the jdk to be tested 236 * and with any platform specific arguments prepended 237 */ 238 public static ProcessBuilder createJavaProcessBuilder(String... command) 239 throws Exception { 240 String javapath = JdkFinder.getJavaLauncher(false); 241 242 ArrayList<String> args = new ArrayList<>(); 243 args.add(javapath); 244 Collections.addAll(args, getPlatformSpecificVMArgs()); 245 Collections.addAll(args, command); 246 247 return new ProcessBuilder(args.toArray(new String[args.size()])); 248 249 } 250 251 }