51 public class JavaChild extends Process {
52
53 private static volatile int commandSeq = 0; // Command sequence number
54 private static final ProcessHandle self = ProcessHandle.current();
55 private static int finalStatus = 0;
56 private static final List<JavaChild> children = new ArrayList<>();
57 private static final Set<JavaChild> completedChildren =
58 Collections.synchronizedSet(new HashSet<>());
59
60 private final Process delegate;
61 private final PrintWriter inputWriter;
62 private final BufferedReader outputReader;
63
64
65 /**
66 * Create a JavaChild control instance that delegates to the spawned process.
67 * {@link #sendAction} is used to send commands via the processes stdin.
68 * {@link #forEachOutputLine} can be used to process output from the child
69 * @param delegate the process to delegate and send commands to and get responses from
70 */
71 private JavaChild(Process delegate) {
72 this.delegate = delegate;
73 // Initialize PrintWriter with autoflush (on println)
74 inputWriter = new PrintWriter(delegate.getOutputStream(), true);
75 outputReader = new BufferedReader(new InputStreamReader(delegate.getInputStream()));
76 }
77
78 @Override
79 public void destroy() {
80 delegate.destroy();
81 }
82
83 @Override
84 public int exitValue() {
85 return delegate.exitValue();
86 }
87
88 @Override
89 public int waitFor() throws InterruptedException {
90 return delegate.waitFor();
91 }
92
102
103 @Override
104 public InputStream getErrorStream() {
105 return delegate.getErrorStream();
106 }
107
108 @Override
109 public ProcessHandle toHandle() {
110 return delegate.toHandle();
111 }
112
113 @Override
114 public CompletableFuture<Process> onExit() {
115 return delegate.onExit();
116 }
117 @Override
118 public String toString() {
119 return "delegate: " + delegate.toString();
120 }
121
122 public CompletableFuture<JavaChild> onJavaChildExit() {
123 return onExit().thenApply(ph -> this);
124 }
125
126 /**
127 * Send an action and arguments to the child via stdin.
128 * @param action the action
129 * @param args additional arguments
130 * @throws IOException if something goes wrong writing to the child
131 */
132 void sendAction(String action, Object... args) throws IOException {
133 StringBuilder sb = new StringBuilder();
134 sb.append(action);
135 for (Object arg :args) {
136 sb.append(" ");
137 sb.append(arg);
138 }
139 String cmd = sb.toString();
140 synchronized (this) {
141 inputWriter.println(cmd);
170 return future;
171 }
172
173 /**
174 * Spawn a JavaChild with the provided arguments.
175 * Commands can be send to the child with {@link #sendAction}.
176 * Output lines from the child can be processed with {@link #forEachOutputLine}.
177 * System.err is set to inherit and is the unstructured async logging
178 * output for all subprocesses.
179 * @param args the command line arguments to JavaChild
180 * @return the JavaChild that was started
181 * @throws IOException thrown by ProcessBuilder.start
182 */
183 static JavaChild spawnJavaChild(Object... args) throws IOException {
184 String[] stringArgs = new String[args.length];
185 for (int i = 0; i < args.length; i++) {
186 stringArgs[i] = args[i].toString();
187 }
188 ProcessBuilder pb = build(stringArgs);
189 pb.redirectError(ProcessBuilder.Redirect.INHERIT);
190 return new JavaChild(pb.start());
191 }
192
193 /**
194 * Spawn a JavaChild with the provided arguments.
195 * Sets the process to inherit the I/O channels.
196 * @param args the command line arguments to JavaChild
197 * @return the Process that was started
198 * @throws IOException thrown by ProcessBuilder.start
199 */
200 static Process spawn(String... args) throws IOException {
201 ProcessBuilder pb = build(args);
202 pb.inheritIO();
203 return pb.start();
204 }
205
206 /**
207 * Return a ProcessBuilder with the javaChildArgs and
208 * any additional supplied args.
209 *
210 * @param args the command line arguments to JavaChild
218 pb.command(list);
219 return pb;
220 }
221
222 static final String javaHome = (System.getProperty("test.jdk") != null)
223 ? System.getProperty("test.jdk")
224 : System.getProperty("java.home");
225
226 static final String javaExe =
227 javaHome + File.separator + "bin" + File.separator + "java";
228
229 static final String classpath =
230 System.getProperty("java.class.path");
231
232 static final List<String> javaChildArgs =
233 Arrays.asList(javaExe,
234 "-XX:+DisplayVMOutputToStderr",
235 "-Dtest.jdk=" + javaHome,
236 "-classpath", absolutifyPath(classpath),
237 "JavaChild");
238
239 private static String absolutifyPath(String path) {
240 StringBuilder sb = new StringBuilder();
241 for (String file : path.split(File.pathSeparator)) {
242 if (sb.length() != 0)
243 sb.append(File.pathSeparator);
244 sb.append(new File(file).getAbsolutePath());
245 }
246 return sb.toString();
247 }
248
249 /**
250 * Main program that interprets commands from the command line args or stdin.
251 * Each command produces output to stdout confirming the command and
252 * providing results.
253 * System.err is used for unstructured information.
254 * @param args an array of strings to be interpreted as commands;
255 * each command uses additional arguments as needed
256 */
257 public static void main(String[] args) {
|
51 public class JavaChild extends Process {
52
53 private static volatile int commandSeq = 0; // Command sequence number
54 private static final ProcessHandle self = ProcessHandle.current();
55 private static int finalStatus = 0;
56 private static final List<JavaChild> children = new ArrayList<>();
57 private static final Set<JavaChild> completedChildren =
58 Collections.synchronizedSet(new HashSet<>());
59
60 private final Process delegate;
61 private final PrintWriter inputWriter;
62 private final BufferedReader outputReader;
63
64
65 /**
66 * Create a JavaChild control instance that delegates to the spawned process.
67 * {@link #sendAction} is used to send commands via the processes stdin.
68 * {@link #forEachOutputLine} can be used to process output from the child
69 * @param delegate the process to delegate and send commands to and get responses from
70 */
71 private JavaChild(ProcessBuilder pb) throws IOException {
72 allArgs = pb.command();
73 delegate = pb.start();
74 // Initialize PrintWriter with autoflush (on println)
75 inputWriter = new PrintWriter(delegate.getOutputStream(), true);
76 outputReader = new BufferedReader(new InputStreamReader(delegate.getInputStream()));
77 }
78
79 @Override
80 public void destroy() {
81 delegate.destroy();
82 }
83
84 @Override
85 public int exitValue() {
86 return delegate.exitValue();
87 }
88
89 @Override
90 public int waitFor() throws InterruptedException {
91 return delegate.waitFor();
92 }
93
103
104 @Override
105 public InputStream getErrorStream() {
106 return delegate.getErrorStream();
107 }
108
109 @Override
110 public ProcessHandle toHandle() {
111 return delegate.toHandle();
112 }
113
114 @Override
115 public CompletableFuture<Process> onExit() {
116 return delegate.onExit();
117 }
118 @Override
119 public String toString() {
120 return "delegate: " + delegate.toString();
121 }
122
123 public List<String> getArgs() {
124 return allArgs;
125 }
126
127 public CompletableFuture<JavaChild> onJavaChildExit() {
128 return onExit().thenApply(ph -> this);
129 }
130
131 /**
132 * Send an action and arguments to the child via stdin.
133 * @param action the action
134 * @param args additional arguments
135 * @throws IOException if something goes wrong writing to the child
136 */
137 void sendAction(String action, Object... args) throws IOException {
138 StringBuilder sb = new StringBuilder();
139 sb.append(action);
140 for (Object arg :args) {
141 sb.append(" ");
142 sb.append(arg);
143 }
144 String cmd = sb.toString();
145 synchronized (this) {
146 inputWriter.println(cmd);
175 return future;
176 }
177
178 /**
179 * Spawn a JavaChild with the provided arguments.
180 * Commands can be send to the child with {@link #sendAction}.
181 * Output lines from the child can be processed with {@link #forEachOutputLine}.
182 * System.err is set to inherit and is the unstructured async logging
183 * output for all subprocesses.
184 * @param args the command line arguments to JavaChild
185 * @return the JavaChild that was started
186 * @throws IOException thrown by ProcessBuilder.start
187 */
188 static JavaChild spawnJavaChild(Object... args) throws IOException {
189 String[] stringArgs = new String[args.length];
190 for (int i = 0; i < args.length; i++) {
191 stringArgs[i] = args[i].toString();
192 }
193 ProcessBuilder pb = build(stringArgs);
194 pb.redirectError(ProcessBuilder.Redirect.INHERIT);
195 return new JavaChild(pb);
196 }
197
198 /**
199 * Spawn a JavaChild with the provided arguments.
200 * Sets the process to inherit the I/O channels.
201 * @param args the command line arguments to JavaChild
202 * @return the Process that was started
203 * @throws IOException thrown by ProcessBuilder.start
204 */
205 static Process spawn(String... args) throws IOException {
206 ProcessBuilder pb = build(args);
207 pb.inheritIO();
208 return pb.start();
209 }
210
211 /**
212 * Return a ProcessBuilder with the javaChildArgs and
213 * any additional supplied args.
214 *
215 * @param args the command line arguments to JavaChild
223 pb.command(list);
224 return pb;
225 }
226
227 static final String javaHome = (System.getProperty("test.jdk") != null)
228 ? System.getProperty("test.jdk")
229 : System.getProperty("java.home");
230
231 static final String javaExe =
232 javaHome + File.separator + "bin" + File.separator + "java";
233
234 static final String classpath =
235 System.getProperty("java.class.path");
236
237 static final List<String> javaChildArgs =
238 Arrays.asList(javaExe,
239 "-XX:+DisplayVMOutputToStderr",
240 "-Dtest.jdk=" + javaHome,
241 "-classpath", absolutifyPath(classpath),
242 "JavaChild");
243
244 // Will hold the complete list of arguments which was given to Processbuilder.command()
245 private List<String> allArgs;
246
247 private static String absolutifyPath(String path) {
248 StringBuilder sb = new StringBuilder();
249 for (String file : path.split(File.pathSeparator)) {
250 if (sb.length() != 0)
251 sb.append(File.pathSeparator);
252 sb.append(new File(file).getAbsolutePath());
253 }
254 return sb.toString();
255 }
256
257 /**
258 * Main program that interprets commands from the command line args or stdin.
259 * Each command produces output to stdout confirming the command and
260 * providing results.
261 * System.err is used for unstructured information.
262 * @param args an array of strings to be interpreted as commands;
263 * each command uses additional arguments as needed
264 */
265 public static void main(String[] args) {
|