1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 package org.apache.commons.rng.examples.stress; 18 19 import java.io.File; 20 import java.io.IOException; 21 import java.util.ArrayList; 22 import java.util.List; 23 24 /** 25 * Utility methods for a {@link Process}. 26 */ 27 final class ProcessUtils { 28 /** The timeout to wait for the process exit value in milliseconds. */ 29 private static final long DEFAULT_TIMEOUT_MILLIS = 1000L; 30 31 /** No public construction. */ 32 private ProcessUtils() {} 33 34 /** 35 * Check the executable exists and has execute permissions. 36 * 37 * @param executable The executable. 38 * @throws ApplicationException If the executable is invalid. 39 */ 40 static void checkExecutable(File executable) { 41 if (!executable.exists() || 42 !executable.canExecute()) { 43 throw new ApplicationException("Program is not executable: " + executable); 44 } 45 } 46 47 /** 48 * Check the output directory exists and has write permissions. 49 * 50 * @param fileOutputPrefix The file output prefix. 51 * @throws ApplicationException If the output directory is invalid. 52 */ 53 static void checkOutputDirectory(File fileOutputPrefix) { 54 final File reportDir = fileOutputPrefix.getAbsoluteFile().getParentFile(); 55 if (!reportDir.exists() || 56 !reportDir.isDirectory() || 57 !reportDir.canWrite()) { 58 throw new ApplicationException("Invalid output directory: " + reportDir); 59 } 60 } 61 62 /** 63 * Builds the command for the sub-process. 64 * 65 * @param executable The executable file. 66 * @param executableArguments The executable arguments. 67 * @return the command 68 * @throws ApplicationException If the executable path cannot be resolved 69 */ 70 static List<String> buildSubProcessCommand(File executable, 71 List<String> executableArguments) { 72 final ArrayList<String> command = new ArrayList<>(); 73 try { 74 command.add(executable.getCanonicalPath()); 75 } catch (final IOException ex) { 76 // Not expected to happen as the file has been tested to exist 77 throw new ApplicationException("Cannot resolve executable path: " + ex.getMessage(), ex); 78 } 79 command.addAll(executableArguments); 80 return command; 81 } 82 83 /** 84 * Get the exit value from the process, waiting at most for 1 second, otherwise kill the process 85 * and return {@code null}. 86 * 87 * <p>This should be used when it is expected the process has completed.</p> 88 * 89 * @param process The process. 90 * @return The exit value. 91 * @see Process#destroy() 92 */ 93 static Integer getExitValue(Process process) { 94 return getExitValue(process, DEFAULT_TIMEOUT_MILLIS); 95 } 96 97 /** 98 * Get the exit value from the process, waiting at most for the given time, otherwise 99 * kill the process and return {@code null}. 100 * 101 * <p>This should be used when it is expected the process has completed. If the timeout 102 * expires an error message is logged before the process is killed.</p> 103 * 104 * @param process The process. 105 * @param timeoutMillis The timeout (in milliseconds). 106 * @return The exit value. 107 * @see Process#destroy() 108 */ 109 static Integer getExitValue(Process process, long timeoutMillis) { 110 final long startTime = System.currentTimeMillis(); 111 long remaining = timeoutMillis; 112 113 while (remaining > 0) { 114 try { 115 return process.exitValue(); 116 } catch (final IllegalThreadStateException ex) { 117 try { 118 Thread.sleep(Math.min(remaining + 1, 100)); 119 } catch (final InterruptedException e) { 120 // Reset interrupted status and stop waiting 121 Thread.currentThread().interrupt(); 122 break; 123 } 124 } 125 remaining = timeoutMillis - (System.currentTimeMillis() - startTime); 126 } 127 128 LogUtils.error("Failed to obtain exit value after %d ms, forcing termination", timeoutMillis); 129 130 // Not finished so kill it 131 process.destroy(); 132 133 return null; 134 } 135 }