aboutsummaryrefslogtreecommitdiff
path: root/test/lib/testlibrary/ClassFileInstaller.java
diff options
context:
space:
mode:
Diffstat (limited to 'test/lib/testlibrary/ClassFileInstaller.java')
-rw-r--r--test/lib/testlibrary/ClassFileInstaller.java216
1 files changed, 209 insertions, 7 deletions
diff --git a/test/lib/testlibrary/ClassFileInstaller.java b/test/lib/testlibrary/ClassFileInstaller.java
index dd8777b1ff..0f5b515a6c 100644
--- a/test/lib/testlibrary/ClassFileInstaller.java
+++ b/test/lib/testlibrary/ClassFileInstaller.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * 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
@@ -21,28 +21,229 @@
* 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
+ * 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 {
- for (String arg : args) {
- ClassLoader cl = ClassFileInstaller.class.getClassLoader();
+ 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);
+ }
- // Convert dotted class name to a path to a class file
- String pathName = arg.replace('.', '/').concat(".class");
- InputStream is = cl.getResourceAsStream(pathName);
+ 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);
Path parent = p.getParent();
@@ -52,5 +253,6 @@ public class ClassFileInstaller {
// Create the class file
Files.copy(is, p, StandardCopyOption.REPLACE_EXISTING);
}
+ is.close();
}
}