/* * Copyright (c) 2014, 2017, 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 * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions.
*/
/** * Command driven subprocess with useful child functions.
*/ publicclass JavaChild extends Process {
privatestaticvolatileint commandSeq = 0; // Command sequence number privatestaticfinal ProcessHandle self = ProcessHandle.current(); privatestaticint finalStatus = 0; privatestaticfinal List<JavaChild> children = new ArrayList<>(); privatestaticfinal Set<JavaChild> completedChildren =
Collections.synchronizedSet(new HashSet<>());
privatefinal Process delegate; privatefinal PrintWriter inputWriter; privatefinal BufferedReader outputReader;
/** * Create a JavaChild control instance that delegates to the spawned process. * {@link #sendAction} is used to send commands via the processes stdin. * {@link #forEachOutputLine} can be used to process output from the child * @param delegate the process to delegate and send commands to and get responses from
*/ private JavaChild(ProcessBuilder pb) throws IOException {
allArgs = pb.command();
delegate = pb.start(); // Initialize PrintWriter with autoflush (on println)
inputWriter = new PrintWriter(delegate.getOutputStream(), true);
outputReader = new BufferedReader(new InputStreamReader(delegate.getInputStream()));
}
@Override public OutputStream getOutputStream() { return delegate.getOutputStream();
}
@Override public InputStream getInputStream() { return delegate.getInputStream();
}
@Override public InputStream getErrorStream() { return delegate.getErrorStream();
}
@Override public ProcessHandle toHandle() { return delegate.toHandle();
}
@Override public CompletableFuture<Process> onExit() { return delegate.onExit();
}
@Override public String toString() { return"delegate: " + delegate.toString();
}
public List<String> getArgs() { return allArgs;
}
public CompletableFuture<JavaChild> onJavaChildExit() { return onExit().thenApply(ph -> this);
}
/** * Send an action and arguments to the child via stdin. * @param action the action * @param args additional arguments * @throws IOException if something goes wrong writing to the child
*/ void sendAction(String action, Object... args) throws IOException {
StringBuilder sb = new StringBuilder();
sb.append(action); for (Object arg :args) {
sb.append(" ");
sb.append(arg);
}
String cmd = sb.toString(); synchronized (this) {
inputWriter.println(cmd);
}
}
public BufferedReader outputReader() { return outputReader;
}
/** * Asynchronously evaluate each line of output received back from the child process. * @param consumer a Consumer of each line read from the child * @return a CompletableFuture that is completed when the child closes System.out.
*/
CompletableFuture<String> forEachOutputLine(Consumer<String> consumer) { final CompletableFuture<String> future = new CompletableFuture<>();
String name = "OutputLineReader-" + pid(); Thread t = newThread(() -> { try (BufferedReader reader = outputReader()) {
String line; while ((line = reader.readLine()) != null) {
consumer.accept(line);
}
} catch (IOException | RuntimeException ex) {
consumer.accept("IOE (" + pid() + "):" + ex.getMessage());
future.completeExceptionally(ex);
}
future.complete("success");
}, name);
t.start(); return future;
}
/** * Spawn a JavaChild with the provided arguments. * Commands can be send to the child with {@link #sendAction}. * Output lines from the child can be processed with {@link #forEachOutputLine}. * System.err is set to inherit and is the unstructured async logging * output for all subprocesses. * @param args the command line arguments to JavaChild * @return the JavaChild that was started * @throws IOException thrown by ProcessBuilder.start
*/ static JavaChild spawnJavaChild(Object... args) throws IOException {
String[] stringArgs = new String[args.length]; for (int i = 0; i < args.length; i++) {
stringArgs[i] = args[i].toString();
}
ProcessBuilder pb = build(stringArgs);
pb.redirectError(ProcessBuilder.Redirect.INHERIT); returnnew JavaChild(pb);
}
/** * Spawn a JavaChild with the provided arguments. * Sets the process to inherit the I/O channels. * @param args the command line arguments to JavaChild * @return the Process that was started * @throws IOException thrown by ProcessBuilder.start
*/ static Process spawn(String... args) throws IOException {
ProcessBuilder pb = build(args);
pb.inheritIO(); return pb.start();
}
/** * Return a ProcessBuilder with the javaChildArgs and * any additional supplied args. * * @param args the command line arguments to JavaChild * @return the ProcessBuilder
*/ static ProcessBuilder build(String ... args) {
ProcessBuilder pb = new ProcessBuilder();
List<String> list = new ArrayList<>(javaChildArgs); for (String arg : args)
list.add(arg);
pb.command(list); return pb;
}
// Will hold the complete list of arguments which was given to Processbuilder.command() private List<String> allArgs;
privatestatic String absolutifyPath(String path) {
StringBuilder sb = new StringBuilder(); for (String file : path.split(File.pathSeparator)) { if (sb.length() != 0)
sb.append(File.pathSeparator);
sb.append(new File(file).getAbsolutePath());
} return sb.toString();
}
/** * Main program that interprets commands from the command line args or stdin. * Each command produces output to stdout confirming the command and * providing results. * System.err is used for unstructured information. * @param args an array of strings to be interpreted as commands; * each command uses additional arguments as needed
*/ publicstaticvoid main(String[] args) {
System.out.printf("args: %s %s%n", ProcessHandle.current(), Arrays.toString(args));
interpretCommands(args);
System.exit(finalStatus);
}
/** * Interpret an array of strings as a command line. * @param args an array of strings to be interpreted as commands; * each command uses additional arguments as needed
*/ privatestaticvoid interpretCommands(String[] args) { try { int nextArg = 0; while (nextArg < args.length) {
String action = args[nextArg++]; switch (action) { case"help":
sendResult(action, "");
help(); break; case"sleep": int millis = Integer.valueOf(args[nextArg++]); Thread.sleep(millis);
sendResult(action, Integer.toString(millis)); break; case"cpuloop": long cpuMillis = Long.valueOf(args[nextArg++]); long cpuTarget = getCpuTime() + cpuMillis * 1_000_000L; while (getCpuTime() < cpuTarget) { // burn the cpu until the time is up
}
sendResult(action, cpuMillis); break; case"cputime":
sendResult(action, getCpuTime()); break; case"out": case"err":
String value = args[nextArg++];
sendResult(action, value); if (action.equals("err")) {
System.err.println(value);
} break; case"stdin": // Read commands from stdin; at eof, close stdin of // children and wait for each to exit
sendResult(action, "start"); try (Reader reader = new InputStreamReader(System.in);
BufferedReader input = new BufferedReader(reader)) {
String line; while ((line = input.readLine()) != null) {
line = line.trim(); if (!line.isEmpty()) {
String[] split = line.split("\\s");
interpretCommands(split);
}
} // EOF on stdin, close stdin on all spawned processes for (JavaChild p : children) { try {
p.getOutputStream().close();
} catch (IOException ie) {
sendResult("stdin_closing", p.pid(), "exception", ie.getMessage());
}
}
for (JavaChild p : children) { do { try {
p.waitFor(); break;
} catch (InterruptedException e) { // retry
}
} while (true);
} // Wait for all children to be gone
Instant timeOut = Instant.now().plusSeconds(10L); while (!completedChildren.containsAll(children)) { if (Instant.now().isBefore(timeOut)) { Thread.sleep(100L);
} else {
System.err.printf("Timeout waiting for " + "children to terminate%n");
children.removeAll(completedChildren); for (JavaChild c : children) {
sendResult("stdin_noterm", c.pid());
System.err.printf(" Process not terminated: " + "pid: %d%n", c.pid());
}
System.exit(2);
}
}
}
sendResult(action, "done"); return; // normal exit from JavaChild Process case"parent":
sendResult(action, self.parent().toString()); break; case"pid":
sendResult(action, self.toString()); break; case"exit": int exitValue = (nextArg < args.length)
? Integer.valueOf(args[nextArg]) : 0;
sendResult(action, exitValue);
System.exit(exitValue); break; case"spawn": { if (args.length - nextArg < 2) { thrownew RuntimeException("not enough args for respawn: " +
(args.length - 2));
} // Spawn as many children as requested and // pass on rest of the arguments int ncount = Integer.valueOf(args[nextArg++]);
Object[] subargs = new String[args.length - nextArg];
System.arraycopy(args, nextArg, subargs, 0, subargs.length); for (int i = 0; i < ncount; i++) {
JavaChild p = spawnJavaChild(subargs);
sendResult(action, p.pid());
p.forEachOutputLine(JavaChild::sendRaw);
p.onJavaChildExit().thenAccept((p1) -> { int excode = p1.exitValue();
sendResult("child_exit", p1.pid(), excode);
completedChildren.add(p1);
});
children.add(p); // Add child to spawned list
}
nextArg = args.length; break;
} case"child": { // Send the command to all the live children; // ignoring those that are not alive int sentCount = 0;
Object[] result =
Arrays.copyOfRange(args, nextArg - 1, args.length);
Object[] subargs =
Arrays.copyOfRange(args, nextArg + 1, args.length); for (JavaChild p : children) { if (p.isAlive()) {
sentCount++; // overwrite with current pid
result[0] = Long.toString(p.pid());
sendResult(action, result);
p.sendAction(args[nextArg], subargs);
}
} if (sentCount == 0) {
sendResult(action, "n/a");
}
nextArg = args.length; break;
} case"child_eof" : // Close the InputStream of all the live children; // ignoring those that are not alive for (JavaChild p : children) { if (p.isAlive()) {
sendResult(action, p.pid());
p.getOutputStream().close();
}
} break; case"property":
String name = args[nextArg++];
sendResult(action, name, System.getProperty(name)); break; case"threaddump": Thread.dumpStack(); break; case"waitpid": long pid = Long.parseLong(args[nextArg++]);
Optional<String> s = ProcessHandle.of(pid).map(ph -> waitAlive(ph));
sendResult(action, s.orElse("pid not valid: " + pid)); break; default: thrownew Error("JavaChild action unknown: " + action);
}
}
} catch (Throwable t) {
t.printStackTrace(System.err);
System.exit(1);
}
}
/** * Create a String encoding the pid, seq, command, and results. * * @return a String formatted to send to the stream.
*/
String format() {
StringBuilder sb = new StringBuilder();
sb.append(pid);
sb.append(":");
sb.append(seq);
sb.append(" ");
sb.append(command); for (int i = 0; i < results.length; i++) {
sb.append(" ");
sb.append(results[i]);
} return sb.toString();
}
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.19Bemerkung:
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.