Path: blob/aarch64-shenandoah-jdk8u272-b10/hotspot/test/testlibrary/ClassFileInstaller.java
32278 views
/*1* Copyright (c) 2013, 2018, 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.ByteArrayInputStream;24import java.io.File;25import java.io.FileInputStream;26import java.io.FileOutputStream;27import java.io.FileNotFoundException;28import java.io.InputStream;29import java.io.ByteArrayInputStream;30import java.nio.file.Files;31import java.nio.file.Path;32import java.nio.file.Paths;33import java.nio.file.StandardCopyOption;34import java.util.zip.ZipEntry;35import java.util.zip.ZipOutputStream;3637/**38* Dump a class file for a class on the class path in the current directory, or39* in the specified JAR file. This class is usually used when you build a class40* from a test library, but want to use this class in a sub-process.41*42* For example, to build the following library class:43* test/lib/sun/hotspot/WhiteBox.java44*45* You would use the following tags:46*47* @library /test/lib48* @build sun.hotspot.WhiteBox49*50* JTREG would build the class file under51* ${JTWork}/classes/test/lib/sun/hotspot/WhiteBox.class52*53* With you run your main test class using "@run main MyMainClass", JTREG would setup the54* -classpath to include "${JTWork}/classes/test/lib/", so MyMainClass would be able to55* load the WhiteBox class.56*57* However, if you run a sub process, and do not wish to use the exact same -classpath,58* You can use ClassFileInstaller to ensure that WhiteBox is available in the current59* directory of your test:60*61* @run main ClassFileInstaller sun.hotspot.WhiteBox62*63* Or, you can use the -jar option to store the class in the specified JAR file. If a relative64* path name is given, the JAR file would be relative to the current directory of65*66* @run main ClassFileInstaller -jar myjar.jar sun.hotspot.WhiteBox67*/68public class ClassFileInstaller {69/**70* You can enable debug tracing of ClassFileInstaller by running JTREG with71* jtreg -DClassFileInstaller.debug=true ... <names of tests>72*/73public static boolean DEBUG = Boolean.getBoolean("ClassFileInstaller.debug");7475/**76* @param args The names of the classes to dump77* @throws Exception78*/79public static void main(String... args) throws Exception {80if (args.length > 1 && args[0].equals("-jar")) {81if (args.length < 2) {82throw new RuntimeException("Usage: ClassFileInstaller <options> <classes>\n" +83"where possible options include:\n" +84" -jar <path> Write to the JAR file <path>");85}86writeJar(args[1], null, args, 2, args.length);87} else {88if (DEBUG) {89System.out.println("ClassFileInstaller: Writing to " + System.getProperty("user.dir"));90}91for (String arg : args) {92writeClassToDisk(arg);93}94}95}9697public static class Manifest {98private InputStream in;99100private Manifest(InputStream in) {101this.in = in;102}103104static Manifest fromSourceFile(String fileName) throws Exception {105String pathName = System.getProperty("test.src") + File.separator + fileName;106return new Manifest(new FileInputStream(pathName));107}108109// Example:110// String manifest = "Premain-Class: RedefineClassHelper\n" +111// "Can-Redefine-Classes: true\n";112// ClassFileInstaller.writeJar("redefineagent.jar",113// ClassFileInstaller.Manifest.fromString(manifest),114// "RedefineClassHelper");115static Manifest fromString(String manifest) throws Exception {116return new Manifest(new ByteArrayInputStream(manifest.getBytes()));117}118119public InputStream getInputStream() {120return in;121}122}123124private static void writeJar(String jarFile, Manifest manifest, String classes[], int from, int to) throws Exception {125if (DEBUG) {126System.out.println("ClassFileInstaller: Writing to " + getJarPath(jarFile));127}128129(new File(jarFile)).delete();130FileOutputStream fos = new FileOutputStream(jarFile);131ZipOutputStream zos = new ZipOutputStream(fos);132133// The manifest must be the first or second entry. See comments in JarInputStream134// constructor and JDK-5046178.135if (manifest != null) {136writeToDisk(zos, "META-INF/MANIFEST.MF", manifest.getInputStream());137}138139for (int i=from; i<to; i++) {140writeClassToDisk(zos, classes[i]);141}142143zos.close();144fos.close();145}146147/*148* You can call ClassFileInstaller.writeJar() from your main test class instead of149* using "@run ClassFileInstaller -jar ...". E.g.,150*151* String jarPath = ClassFileInstaller.getJarPath("myjar.jar", "sun.hotspot.WhiteBox")152*153* If you call this API, make sure you build ClassFileInstaller with the following tags:154*155* @library testlibrary156* @build ClassFileInstaller157*/158public static String writeJar(String jarFile, String... classes) throws Exception {159writeJar(jarFile, null, classes, 0, classes.length);160return getJarPath(jarFile);161}162163public static String writeJar(String jarFile, Manifest manifest, String... classes) throws Exception {164writeJar(jarFile, manifest, classes, 0, classes.length);165return getJarPath(jarFile);166}167168/**169* This returns the absolute path to the file specified in "@ClassFileInstaller -jar myjar.jar",170* In your test program, instead of using the JAR file name directly:171*172* String jarPath = "myjar.jar";173*174* you should call this function, like:175*176* String jarPath = ClassFileInstaller.getJarPath("myjar.jar")177*178* The reasons are:179* (1) Using absolute path makes it easy to cut-and-paste from the JTR file and rerun your180* test in any directory.181* (2) In the future, we may make the JAR file name unique to avoid clobbering182* during parallel JTREG execution.183*184*/185public static String getJarPath(String jarFileName) {186return new File(jarFileName).getAbsolutePath();187}188189public static void writeClassToDisk(String className) throws Exception {190writeClassToDisk((ZipOutputStream)null, className);191}192private static void writeClassToDisk(ZipOutputStream zos, String className) throws Exception {193writeClassToDisk(zos, className, "");194}195196public static void writeClassToDisk(String className, String prependPath) throws Exception {197writeClassToDisk(null, className, prependPath);198}199private static void writeClassToDisk(ZipOutputStream zos, String className, String prependPath) throws Exception {200ClassLoader cl = ClassFileInstaller.class.getClassLoader();201202// Convert dotted class name to a path to a class file203String pathName = className.replace('.', '/').concat(".class");204InputStream is = cl.getResourceAsStream(pathName);205if (is == null) {206throw new RuntimeException("Failed to find " + pathName);207}208if (prependPath.length() > 0) {209pathName = prependPath + "/" + pathName;210}211writeToDisk(zos, pathName, is);212}213214public static void writeClassToDisk(String className, byte[] bytecode) throws Exception {215writeClassToDisk(null, className, bytecode);216}217private static void writeClassToDisk(ZipOutputStream zos, String className, byte[] bytecode) throws Exception {218writeClassToDisk(zos, className, bytecode, "");219}220221public static void writeClassToDisk(String className, byte[] bytecode, String prependPath) throws Exception {222writeClassToDisk(null, className, bytecode, prependPath);223}224private static void writeClassToDisk(ZipOutputStream zos, String className, byte[] bytecode, String prependPath) throws Exception {225// Convert dotted class name to a path to a class file226String pathName = className.replace('.', '/').concat(".class");227if (prependPath.length() > 0) {228pathName = prependPath + "/" + pathName;229}230writeToDisk(zos, pathName, new ByteArrayInputStream(bytecode));231}232233private static void writeToDisk(ZipOutputStream zos, String pathName, InputStream is) throws Exception {234if (DEBUG) {235System.out.println("ClassFileInstaller: Writing " + pathName);236}237if (zos != null) {238ZipEntry ze = new ZipEntry(pathName);239zos.putNextEntry(ze);240byte[] buf = new byte[1024];241int len;242while ((len = is.read(buf))>0){243zos.write(buf, 0, len);244}245} else {246// Create the class file's package directory247Path p = Paths.get(pathName);248if (pathName.contains("/")) {249Files.createDirectories(p.getParent());250}251// Create the class file252Files.copy(is, p, StandardCopyOption.REPLACE_EXISTING);253}254is.close();255}256}257258259