/* * Copyright (c) 2013, 2018, 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. */ import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.FileNotFoundException; import java.io.InputStream; import java.io.ByteArrayInputStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; /** * Dump a class file for a class on the class path in the current directory, or * in the specified JAR file. This class is usually used when you build a class * from a test library, but want to use this class in a sub-process. * * For example, to build the following library class: * test/lib/sun/hotspot/WhiteBox.java * * You would use the following tags: * * @library /test/lib * @build sun.hotspot.WhiteBox * * JTREG would build the class file under * ${JTWork}/classes/test/lib/sun/hotspot/WhiteBox.class * * With you run your main test class using "@run main MyMainClass", JTREG would setup the * -classpath to include "${JTWork}/classes/test/lib/", so MyMainClass would be able to * load the WhiteBox class. * * However, if you run a sub process, and do not wish to use the exact same -classpath, * You can use ClassFileInstaller to ensure that WhiteBox is available in the current * directory of your test: * * @run main ClassFileInstaller sun.hotspot.WhiteBox * * Or, you can use the -jar option to store the class in the specified JAR file. If a relative * path name is given, the JAR file would be relative to the current directory of * * @run main ClassFileInstaller -jar myjar.jar sun.hotspot.WhiteBox */ public class ClassFileInstaller { /** * You can enable debug tracing of ClassFileInstaller by running JTREG with * jtreg -DClassFileInstaller.debug=true ... */ public static boolean DEBUG = Boolean.getBoolean("ClassFileInstaller.debug"); /** * @param args The names of the classes to dump * @throws Exception */ public static void main(String... args) throws Exception { if (args.length > 1 && args[0].equals("-jar")) { if (args.length < 2) { throw new RuntimeException("Usage: ClassFileInstaller \n" + "where possible options include:\n" + " -jar Write to the JAR file "); } writeJar(args[1], null, args, 2, args.length); } else { if (DEBUG) { System.out.println("ClassFileInstaller: Writing to " + System.getProperty("user.dir")); } for (String arg : args) { writeClassToDisk(arg); } } } public static class Manifest { private InputStream in; private Manifest(InputStream in) { this.in = in; } static Manifest fromSourceFile(String fileName) throws Exception { String pathName = System.getProperty("test.src") + File.separator + fileName; return new Manifest(new FileInputStream(pathName)); } // Example: // String manifest = "Premain-Class: RedefineClassHelper\n" + // "Can-Redefine-Classes: true\n"; // ClassFileInstaller.writeJar("redefineagent.jar", // ClassFileInstaller.Manifest.fromString(manifest), // "RedefineClassHelper"); static Manifest fromString(String manifest) throws Exception { return new Manifest(new ByteArrayInputStream(manifest.getBytes())); } public InputStream getInputStream() { return in; } } private static void writeJar(String jarFile, Manifest manifest, String classes[], int from, int to) throws Exception { if (DEBUG) { System.out.println("ClassFileInstaller: Writing to " + getJarPath(jarFile)); } (new File(jarFile)).delete(); FileOutputStream fos = new FileOutputStream(jarFile); ZipOutputStream zos = new ZipOutputStream(fos); // The manifest must be the first or second entry. See comments in JarInputStream // constructor and JDK-5046178. if (manifest != null) { writeToDisk(zos, "META-INF/MANIFEST.MF", manifest.getInputStream()); } for (int i=from; i 0) { pathName = prependPath + "/" + pathName; } writeToDisk(zos, pathName, is); } public static void writeClassToDisk(String className, byte[] bytecode) throws Exception { writeClassToDisk(null, className, bytecode); } private static void writeClassToDisk(ZipOutputStream zos, String className, byte[] bytecode) throws Exception { writeClassToDisk(zos, className, bytecode, ""); } public static void writeClassToDisk(String className, byte[] bytecode, String prependPath) throws Exception { writeClassToDisk(null, className, bytecode, prependPath); } private static void writeClassToDisk(ZipOutputStream zos, String className, byte[] bytecode, String prependPath) throws Exception { // Convert dotted class name to a path to a class file String pathName = className.replace('.', '/').concat(".class"); if (prependPath.length() > 0) { pathName = prependPath + "/" + pathName; } writeToDisk(zos, pathName, new ByteArrayInputStream(bytecode)); } private static void writeToDisk(ZipOutputStream zos, String pathName, InputStream is) throws Exception { if (DEBUG) { System.out.println("ClassFileInstaller: Writing " + pathName); } if (zos != null) { ZipEntry ze = new ZipEntry(pathName); zos.putNextEntry(ze); byte[] buf = new byte[1024]; int len; while ((len = is.read(buf))>0){ zos.write(buf, 0, len); } } else { // Create the class file's package directory Path p = Paths.get(pathName); if (pathName.contains("/")) { Files.createDirectories(p.getParent()); } // Create the class file Files.copy(is, p, StandardCopyOption.REPLACE_EXISTING); } is.close(); } }