Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/test/java/security/testlibrary/Proc.java
38812 views
/*1* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223import java.io.BufferedReader;24import java.io.File;25import java.io.IOException;26import java.io.InputStreamReader;27import java.net.URL;28import java.net.URLClassLoader;29import java.nio.file.Files;30import java.nio.file.Path;31import java.nio.file.Paths;32import java.security.Permission;33import java.util.ArrayList;34import java.util.Base64;35import java.util.HashMap;36import java.util.List;37import java.util.Map;38import java.util.Map.Entry;3940/**41* This is a test library that makes writing a Java test that spawns multiple42* Java processes easily.43*44* Usage:45*46* Proc.create("Clazz") // The class to launch47* .args("x") // with args48* .env("env", "value") // and an environment variable49* .prop("key","value") // and a system property50* .perm(perm) // with granted permissions51* .start(); // and start52*53* create/start must be called, args/env/prop/perm can be called zero or54* multiple times between create and start.55*56* The controller can call inheritIO to share its I/O to the process.57* Otherwise, it can send data into a proc's stdin with write/println, and58* read its stdout with readLine. stderr is always redirected to DFILE59* unless nodump() is called. A protocol is designed to make60* data exchange among the controller and the processes super easy, in which61* useful data are always printed with a special prefix ("PROCISFUN:").62* If the data is binary, make it BASE64.63*64* For example:65*66* - A producer Proc calls Proc.binOut() or Proc.textOut() to send out data.67* This method would prints to the stdout something like68*69* PROCISFUN:[raw text or base64 binary]70*71* - The controller calls producer.readData() to get the content. This method72* ignores all other output and only reads lines starting with "PROCISFUN:".73*74* - The controller does not care if the context is text or base64, it simply75* feeds the data to a consumer Proc by calling consumer.println(data).76* This will be printed into System.in of the consumer process.77*78* - The consumer Proc calls Proc.binIn() or Proc.textIn() to read the data.79* The first method de-base64 the input and return a byte[] block.80*81* Please note only plain ASCII is supported in raw text at the moment.82*83* As the Proc objects are hidden so deeply, two static methods, d(String) and84* d(Throwable) are provided to output info into stderr, where they will85* normally be appended messages to DFILE (unless nodump() is called).86* Developers can view the messages in real time by calling87*88* tail -f proc.debug89*90* TODO:91*92* . launch java tools, say, keytool93* . launch another version of java94* . start in another directory95* . start and finish using one method96*97* This is not a test, but is the core of98* JDK-8009977: A test library to launch multiple Java processes99*/100public class Proc {101private Process p;102private BufferedReader br; // the stdout of a process103private String launcher; // Optional: the java program104105private List<Permission> perms = new ArrayList<>();106private List<String> args = new ArrayList<>();107private Map<String,String> env = new HashMap<>();108private Map<String,String> prop = new HashMap();109private boolean inheritIO = false;110private boolean noDump = false;111112private String clazz; // Class to launch113private String debug; // debug flag, controller will show data114// transfer between procs115116final private static String PREFIX = "PROCISFUN:";117final private static String DFILE = "proc.debug";118119// The following methods are called by controllers120121// Creates a Proc by the Java class name, launcher is an optional122// argument to specify the java program123public static Proc create(String clazz, String... launcher) {124Proc pc = new Proc();125pc.clazz = clazz;126if (launcher.length > 0) {127pc.launcher = launcher[0];128}129return pc;130}131// Sets inheritIO flag to proc. If set, proc will same I/O channels as132// teh controller. Otherwise, its stdin/stdout is untouched, and its133// stderr is redirected to DFILE.134public Proc inheritIO() {135inheritIO = true;136return this;137}138// When called, stderr inherits parent stderr, otherwise, append to a file139public Proc nodump() {140noDump = true;141return this;142}143// Specifies some args. Can be called multiple times.144public Proc args(String... args) {145for (String c: args) {146this.args.add(c);147}148return this;149}150// Returns debug prefix151public String debug() {152return debug;153}154// Enables debug with prefix155public Proc debug(String title) {156debug = title;157return this;158}159// Specifies an env var. Can be called multiple times.160public Proc env(String a, String b) {161env.put(a, b);162return this;163}164// Specifies a Java system property. Can be called multiple times.165public Proc prop(String a, String b) {166prop.put(a, b);167return this;168}169// Adds a perm to policy. Can be called multiple times. In order to make it170// effective, please also call prop("java.security.manager", "").171public Proc perm(Permission p) {172perms.add(p);173return this;174}175// Starts the proc176public Proc start() throws IOException {177List<String> cmd = new ArrayList<>();178if (launcher != null) {179cmd.add(launcher);180} else {181cmd.add(new File(new File(System.getProperty("java.home"), "bin"),182"java").getPath());183}184cmd.add("-cp");185StringBuilder cp = new StringBuilder();186for (URL url: ((URLClassLoader)Proc.class.getClassLoader()).getURLs()) {187if (cp.length() != 0) {188cp.append(File.pathSeparatorChar);189}190cp.append(url.getFile());191}192cmd.add(cp.toString());193for (Entry<String,String> e: prop.entrySet()) {194cmd.add("-D" + e.getKey() + "=" + e.getValue());195}196if (!perms.isEmpty()) {197Path p = Files.createTempFile(198Paths.get(".").toAbsolutePath(), "policy", null);199StringBuilder sb = new StringBuilder();200sb.append("grant {\n");201for (Permission perm: perms) {202// Sometimes a permission has no name or actions.203// but it's safe to use an empty string.204String s = String.format("%s \"%s\", \"%s\"",205perm.getClass().getCanonicalName(),206perm.getName()207.replace("\\", "\\\\").replace("\"", "\\\""),208perm.getActions());209sb.append(" permission ").append(s).append(";\n");210}211sb.append("};\n");212Files.write(p, sb.toString().getBytes());213cmd.add("-Djava.security.policy=" + p.toString());214}215cmd.add(clazz);216for (String s: args) {217cmd.add(s);218}219if (debug != null) {220System.out.println("PROC: " + debug + " cmdline: " + cmd);221}222ProcessBuilder pb = new ProcessBuilder(cmd);223for (Entry<String,String> e: env.entrySet()) {224pb.environment().put(e.getKey(), e.getValue());225}226if (inheritIO) {227pb.inheritIO();228} else if (noDump) {229pb.redirectError(ProcessBuilder.Redirect.INHERIT);230} else {231pb.redirectError(ProcessBuilder.Redirect.appendTo(new File(DFILE)));232}233p = pb.start();234br = new BufferedReader(new InputStreamReader(p.getInputStream()));235return this;236}237String getId(String suffix) {238if (debug != null) {239return debug + "." + suffix;240} else {241return System.identityHashCode(this) + "." + suffix;242}243}244// Reads a line from stdout of proc245public String readLine() throws IOException {246String s = br.readLine();247if (debug != null) {248System.out.println("PROC: " + debug + " readline: " +249(s == null ? "<EOF>" : s));250}251return s;252}253// Reads a special line from stdout of proc254public String readData() throws Exception {255while (true) {256String s = readLine();257if (s == null) {258if (p.waitFor() != 0) {259throw new Exception("Proc abnormal end");260} else {261return s;262}263}264if (s.startsWith(PREFIX)) {265return s.substring(PREFIX.length());266}267}268}269// Writes text into stdin of proc270public void println(String s) throws IOException {271if (debug != null) {272System.out.println("PROC: " + debug + " println: " + s);273}274write((s + "\n").getBytes());275}276// Writes data into stdin of proc277public void write(byte[] b) throws IOException {278p.getOutputStream().write(b);279p.getOutputStream().flush();280}281// Reads all output and wait for process end282public int waitFor() throws Exception {283while (true) {284String s = readLine();285if (s == null) {286break;287}288}289return p.waitFor();290}291292// The following methods are used inside a proc293294// Writes out a BASE64 binary with a prefix295public static void binOut(byte[] data) {296System.out.println(PREFIX + Base64.getEncoder().encodeToString(data));297}298// Reads in a line of BASE64 binary299public static byte[] binIn() throws Exception {300return Base64.getDecoder().decode(textIn());301}302// Writes out a text with a prefix303public static void textOut(String data) {304System.out.println(PREFIX + data);305}306// Reads in a line of text307public static String textIn() throws Exception {308StringBuilder sb = new StringBuilder();309boolean isEmpty = true;310while (true) {311int i = System.in.read();312if (i == -1) {313break;314}315isEmpty = false;316if (i == '\n') {317break;318}319if (i != 13) {320// Force it to a char, so only simple ASCII works.321sb.append((char)i);322}323}324return isEmpty ? null : sb.toString();325}326// Sends string to stderr. If inheritIO is not called, they will327// be collected into DFILE328public static void d(String s) throws IOException {329System.err.println(s);330}331// Sends an exception to stderr332public static void d(Throwable e) throws IOException {333e.printStackTrace();334}335}336337338