< prev index next >

test/lib/testlibrary/ClassFileInstaller.java

Print this page
rev 13657 : 8216401: Allow "file:" URLs in Class-Path of local JARs
Reviewed-by: alanb, mchung

*** 1,7 **** /* ! * Copyright (c) 2013, 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. --- 1,7 ---- /* ! * 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.
*** 19,56 **** * 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.InputStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; /** ! * Dump a class file for a class on the class path in the current directory */ public class ClassFileInstaller { /** * @param args The names of the classes to dump * @throws Exception */ public static void main(String... args) throws Exception { for (String arg : args) { ClassLoader cl = ClassFileInstaller.class.getClassLoader(); // Convert dotted class name to a path to a class file ! String pathName = arg.replace('.', '/').concat(".class"); InputStream is = cl.getResourceAsStream(pathName); // Create the class file's package directory Path p = Paths.get(pathName); ! Path parent = p.getParent(); ! if (parent != null) { ! Files.createDirectories(parent); } // Create the class file Files.copy(is, p, StandardCopyOption.REPLACE_EXISTING); } } } --- 19,257 ---- * 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 ... <names of tests> + */ + 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 <options> <classes>\n" + + "where possible options include:\n" + + " -jar <path> Write to the JAR file <path>"); + } + 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<to; i++) { + writeClassToDisk(zos, classes[i]); + } + + zos.close(); + fos.close(); + } + + /* + * You can call ClassFileInstaller.writeJar() from your main test class instead of + * using "@run ClassFileInstaller -jar ...". E.g., + * + * String jarPath = ClassFileInstaller.getJarPath("myjar.jar", "sun.hotspot.WhiteBox") + * + * If you call this API, make sure you build ClassFileInstaller with the following tags: + * + * @library testlibrary + * @build ClassFileInstaller + */ + public static String writeJar(String jarFile, String... classes) throws Exception { + writeJar(jarFile, null, classes, 0, classes.length); + return getJarPath(jarFile); + } + + public static String writeJar(String jarFile, Manifest manifest, String... classes) throws Exception { + writeJar(jarFile, manifest, classes, 0, classes.length); + return getJarPath(jarFile); + } + + /** + * This returns the absolute path to the file specified in "@ClassFileInstaller -jar myjar.jar", + * In your test program, instead of using the JAR file name directly: + * + * String jarPath = "myjar.jar"; + * + * you should call this function, like: + * + * String jarPath = ClassFileInstaller.getJarPath("myjar.jar") + * + * The reasons are: + * (1) Using absolute path makes it easy to cut-and-paste from the JTR file and rerun your + * test in any directory. + * (2) In the future, we may make the JAR file name unique to avoid clobbering + * during parallel JTREG execution. + * + */ + public static String getJarPath(String jarFileName) { + return new File(jarFileName).getAbsolutePath(); + } + + public static void writeClassToDisk(String className) throws Exception { + writeClassToDisk((ZipOutputStream)null, className); + } + private static void writeClassToDisk(ZipOutputStream zos, String className) throws Exception { + writeClassToDisk(zos, className, ""); + } + + public static void writeClassToDisk(String className, String prependPath) throws Exception { + writeClassToDisk(null, className, prependPath); + } + private static void writeClassToDisk(ZipOutputStream zos, String className, String prependPath) throws Exception { ClassLoader cl = ClassFileInstaller.class.getClassLoader(); // Convert dotted class name to a path to a class file ! String pathName = className.replace('.', '/').concat(".class"); InputStream is = cl.getResourceAsStream(pathName); + if (is == null) { + throw new RuntimeException("Failed to find " + pathName); + } + if (prependPath.length() > 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(); } }
< prev index next >