mgerdin@4637: /* vaibhav@9421: * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved. mgerdin@4637: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. mgerdin@4637: * mgerdin@4637: * This code is free software; you can redistribute it and/or modify it mgerdin@4637: * under the terms of the GNU General Public License version 2 only, as mgerdin@4637: * published by the Free Software Foundation. mgerdin@4637: * mgerdin@4637: * This code is distributed in the hope that it will be useful, but WITHOUT mgerdin@4637: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or mgerdin@4637: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License mgerdin@4637: * version 2 for more details (a copy is included in the LICENSE file that mgerdin@4637: * accompanied this code). mgerdin@4637: * mgerdin@4637: * You should have received a copy of the GNU General Public License version mgerdin@4637: * 2 along with this work; if not, write to the Free Software Foundation, mgerdin@4637: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. mgerdin@4637: * mgerdin@4637: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA mgerdin@4637: * or visit www.oracle.com if you need additional information or have any mgerdin@4637: * questions. mgerdin@4637: */ mgerdin@4637: vaibhav@9421: import java.io.ByteArrayInputStream; vaibhav@9421: import java.io.File; vaibhav@9421: import java.io.FileInputStream; vaibhav@9421: import java.io.FileOutputStream; vaibhav@9421: import java.io.FileNotFoundException; mgerdin@4637: import java.io.InputStream; vaibhav@9421: import java.io.ByteArrayInputStream; mgerdin@4637: import java.nio.file.Files; mgerdin@4637: import java.nio.file.Path; mgerdin@4637: import java.nio.file.Paths; mgerdin@4637: import java.nio.file.StandardCopyOption; vaibhav@9421: import java.util.zip.ZipEntry; vaibhav@9421: import java.util.zip.ZipOutputStream; mgerdin@4637: mgerdin@4637: /** vaibhav@9421: * Dump a class file for a class on the class path in the current directory, or vaibhav@9421: * in the specified JAR file. This class is usually used when you build a class vaibhav@9421: * from a test library, but want to use this class in a sub-process. vaibhav@9421: * vaibhav@9421: * For example, to build the following library class: vaibhav@9421: * test/lib/sun/hotspot/WhiteBox.java vaibhav@9421: * vaibhav@9421: * You would use the following tags: vaibhav@9421: * vaibhav@9421: * @library /test/lib vaibhav@9421: * @build sun.hotspot.WhiteBox vaibhav@9421: * vaibhav@9421: * JTREG would build the class file under vaibhav@9421: * ${JTWork}/classes/test/lib/sun/hotspot/WhiteBox.class vaibhav@9421: * vaibhav@9421: * With you run your main test class using "@run main MyMainClass", JTREG would setup the vaibhav@9421: * -classpath to include "${JTWork}/classes/test/lib/", so MyMainClass would be able to vaibhav@9421: * load the WhiteBox class. vaibhav@9421: * vaibhav@9421: * However, if you run a sub process, and do not wish to use the exact same -classpath, vaibhav@9421: * You can use ClassFileInstaller to ensure that WhiteBox is available in the current vaibhav@9421: * directory of your test: vaibhav@9421: * vaibhav@9421: * @run main ClassFileInstaller sun.hotspot.WhiteBox vaibhav@9421: * vaibhav@9421: * Or, you can use the -jar option to store the class in the specified JAR file. If a relative vaibhav@9421: * path name is given, the JAR file would be relative to the current directory of vaibhav@9421: * vaibhav@9421: * @run main ClassFileInstaller -jar myjar.jar sun.hotspot.WhiteBox mgerdin@4637: */ mgerdin@4637: public class ClassFileInstaller { mgerdin@4637: /** vaibhav@9421: * You can enable debug tracing of ClassFileInstaller by running JTREG with vaibhav@9421: * jtreg -DClassFileInstaller.debug=true ... vaibhav@9421: */ vaibhav@9421: public static boolean DEBUG = Boolean.getBoolean("ClassFileInstaller.debug"); vaibhav@9421: vaibhav@9421: /** mgerdin@4637: * @param args The names of the classes to dump mgerdin@4637: * @throws Exception mgerdin@4637: */ mgerdin@4637: public static void main(String... args) throws Exception { vaibhav@9421: if (args.length > 1 && args[0].equals("-jar")) { vaibhav@9421: if (args.length < 2) { vaibhav@9421: throw new RuntimeException("Usage: ClassFileInstaller \n" + vaibhav@9421: "where possible options include:\n" + vaibhav@9421: " -jar Write to the JAR file "); vaibhav@9421: } vaibhav@9421: writeJar(args[1], null, args, 2, args.length); vaibhav@9421: } else { vaibhav@9421: if (DEBUG) { vaibhav@9421: System.out.println("ClassFileInstaller: Writing to " + System.getProperty("user.dir")); vaibhav@9421: } vaibhav@9421: for (String arg : args) { vaibhav@9421: writeClassToDisk(arg); vaibhav@9421: } vaibhav@9421: } vaibhav@9421: } mgerdin@4637: vaibhav@9421: public static class Manifest { vaibhav@9421: private InputStream in; mgerdin@4637: vaibhav@9421: private Manifest(InputStream in) { vaibhav@9421: this.in = in; vaibhav@9421: } vaibhav@9421: vaibhav@9421: static Manifest fromSourceFile(String fileName) throws Exception { vaibhav@9421: String pathName = System.getProperty("test.src") + File.separator + fileName; vaibhav@9421: return new Manifest(new FileInputStream(pathName)); vaibhav@9421: } vaibhav@9421: vaibhav@9421: // Example: vaibhav@9421: // String manifest = "Premain-Class: RedefineClassHelper\n" + vaibhav@9421: // "Can-Redefine-Classes: true\n"; vaibhav@9421: // ClassFileInstaller.writeJar("redefineagent.jar", vaibhav@9421: // ClassFileInstaller.Manifest.fromString(manifest), vaibhav@9421: // "RedefineClassHelper"); vaibhav@9421: static Manifest fromString(String manifest) throws Exception { vaibhav@9421: return new Manifest(new ByteArrayInputStream(manifest.getBytes())); vaibhav@9421: } vaibhav@9421: vaibhav@9421: public InputStream getInputStream() { vaibhav@9421: return in; vaibhav@9421: } vaibhav@9421: } vaibhav@9421: vaibhav@9421: private static void writeJar(String jarFile, Manifest manifest, String classes[], int from, int to) throws Exception { vaibhav@9421: if (DEBUG) { vaibhav@9421: System.out.println("ClassFileInstaller: Writing to " + getJarPath(jarFile)); vaibhav@9421: } vaibhav@9421: vaibhav@9421: (new File(jarFile)).delete(); vaibhav@9421: FileOutputStream fos = new FileOutputStream(jarFile); vaibhav@9421: ZipOutputStream zos = new ZipOutputStream(fos); vaibhav@9421: vaibhav@9421: // The manifest must be the first or second entry. See comments in JarInputStream vaibhav@9421: // constructor and JDK-5046178. vaibhav@9421: if (manifest != null) { vaibhav@9421: writeToDisk(zos, "META-INF/MANIFEST.MF", manifest.getInputStream()); vaibhav@9421: } vaibhav@9421: vaibhav@9421: for (int i=from; i 0) { vaibhav@9421: pathName = prependPath + "/" + pathName; vaibhav@9421: } vaibhav@9421: writeToDisk(zos, pathName, is); vaibhav@9421: } vaibhav@9421: vaibhav@9421: public static void writeClassToDisk(String className, byte[] bytecode) throws Exception { vaibhav@9421: writeClassToDisk(null, className, bytecode); vaibhav@9421: } vaibhav@9421: private static void writeClassToDisk(ZipOutputStream zos, String className, byte[] bytecode) throws Exception { vaibhav@9421: writeClassToDisk(zos, className, bytecode, ""); vaibhav@9421: } vaibhav@9421: vaibhav@9421: public static void writeClassToDisk(String className, byte[] bytecode, String prependPath) throws Exception { vaibhav@9421: writeClassToDisk(null, className, bytecode, prependPath); vaibhav@9421: } vaibhav@9421: private static void writeClassToDisk(ZipOutputStream zos, String className, byte[] bytecode, String prependPath) throws Exception { vaibhav@9421: // Convert dotted class name to a path to a class file vaibhav@9421: String pathName = className.replace('.', '/').concat(".class"); vaibhav@9421: if (prependPath.length() > 0) { vaibhav@9421: pathName = prependPath + "/" + pathName; vaibhav@9421: } vaibhav@9421: writeToDisk(zos, pathName, new ByteArrayInputStream(bytecode)); vaibhav@9421: } vaibhav@9421: vaibhav@9421: private static void writeToDisk(ZipOutputStream zos, String pathName, InputStream is) throws Exception { vaibhav@9421: if (DEBUG) { vaibhav@9421: System.out.println("ClassFileInstaller: Writing " + pathName); vaibhav@9421: } vaibhav@9421: if (zos != null) { vaibhav@9421: ZipEntry ze = new ZipEntry(pathName); vaibhav@9421: zos.putNextEntry(ze); vaibhav@9421: byte[] buf = new byte[1024]; vaibhav@9421: int len; vaibhav@9421: while ((len = is.read(buf))>0){ vaibhav@9421: zos.write(buf, 0, len); vaibhav@9421: } vaibhav@9421: } else { mgerdin@4637: // Create the class file's package directory mgerdin@4637: Path p = Paths.get(pathName); coleenp@5100: if (pathName.contains("/")) { coleenp@5100: Files.createDirectories(p.getParent()); coleenp@5100: } mgerdin@4637: // Create the class file mgerdin@4637: Files.copy(is, p, StandardCopyOption.REPLACE_EXISTING); mgerdin@4637: } vaibhav@9421: is.close(); mgerdin@4637: } mgerdin@4637: }