test/testlibrary/ClassFileInstaller.java

Tue, 03 Mar 2020 12:57:23 +0000

author
andrew
date
Tue, 03 Mar 2020 12:57:23 +0000
changeset 9896
1b8c45b8216a
parent 9421
6cfec782c42c
child 9448
73d689add964
permissions
-rw-r--r--

Merge

     1 /*
     2  * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4  *
     5  * This code is free software; you can redistribute it and/or modify it
     6  * under the terms of the GNU General Public License version 2 only, as
     7  * published by the Free Software Foundation.
     8  *
     9  * This code is distributed in the hope that it will be useful, but WITHOUT
    10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    12  * version 2 for more details (a copy is included in the LICENSE file that
    13  * accompanied this code).
    14  *
    15  * You should have received a copy of the GNU General Public License version
    16  * 2 along with this work; if not, write to the Free Software Foundation,
    17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    18  *
    19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    20  * or visit www.oracle.com if you need additional information or have any
    21  * questions.
    22  */
    24 import java.io.ByteArrayInputStream;
    25 import java.io.File;
    26 import java.io.FileInputStream;
    27 import java.io.FileOutputStream;
    28 import java.io.FileNotFoundException;
    29 import java.io.InputStream;
    30 import java.io.ByteArrayInputStream;
    31 import java.nio.file.Files;
    32 import java.nio.file.Path;
    33 import java.nio.file.Paths;
    34 import java.nio.file.StandardCopyOption;
    35 import java.util.zip.ZipEntry;
    36 import java.util.zip.ZipOutputStream;
    38 /**
    39  * Dump a class file for a class on the class path in the current directory, or
    40  * in the specified JAR file. This class is usually used when you build a class
    41  * from a test library, but want to use this class in a sub-process.
    42  *
    43  * For example, to build the following library class:
    44  * test/lib/sun/hotspot/WhiteBox.java
    45  *
    46  * You would use the following tags:
    47  *
    48  * @library /test/lib
    49  * @build sun.hotspot.WhiteBox
    50  *
    51  * JTREG would build the class file under
    52  * ${JTWork}/classes/test/lib/sun/hotspot/WhiteBox.class
    53  *
    54  * With you run your main test class using "@run main MyMainClass", JTREG would setup the
    55  * -classpath to include "${JTWork}/classes/test/lib/", so MyMainClass would be able to
    56  * load the WhiteBox class.
    57  *
    58  * However, if you run a sub process, and do not wish to use the exact same -classpath,
    59  * You can use ClassFileInstaller to ensure that WhiteBox is available in the current
    60  * directory of your test:
    61  *
    62  * @run main ClassFileInstaller sun.hotspot.WhiteBox
    63  *
    64  * Or, you can use the -jar option to store the class in the specified JAR file. If a relative
    65  * path name is given, the JAR file would be relative to the current directory of
    66  *
    67  * @run main ClassFileInstaller -jar myjar.jar sun.hotspot.WhiteBox
    68  */
    69 public class ClassFileInstaller {
    70     /**
    71      * You can enable debug tracing of ClassFileInstaller by running JTREG with
    72      * jtreg -DClassFileInstaller.debug=true ... <names of tests>
    73      */
    74     public static boolean DEBUG = Boolean.getBoolean("ClassFileInstaller.debug");
    76     /**
    77      * @param args The names of the classes to dump
    78      * @throws Exception
    79      */
    80     public static void main(String... args) throws Exception {
    81         if (args.length > 1 && args[0].equals("-jar")) {
    82             if (args.length < 2) {
    83                 throw new RuntimeException("Usage: ClassFileInstaller <options> <classes>\n" +
    84                                            "where possible options include:\n" +
    85                                            "  -jar <path>             Write to the JAR file <path>");
    86             }
    87             writeJar(args[1], null, args, 2, args.length);
    88         } else {
    89             if (DEBUG) {
    90                 System.out.println("ClassFileInstaller: Writing to " + System.getProperty("user.dir"));
    91             }
    92             for (String arg : args) {
    93                 writeClassToDisk(arg);
    94             }
    95         }
    96     }
    98     public static class Manifest {
    99         private InputStream in;
   101         private Manifest(InputStream in) {
   102             this.in = in;
   103         }
   105         static Manifest fromSourceFile(String fileName) throws Exception {
   106             String pathName = System.getProperty("test.src") + File.separator + fileName;
   107             return new Manifest(new FileInputStream(pathName));
   108         }
   110         // Example:
   111         //  String manifest = "Premain-Class: RedefineClassHelper\n" +
   112         //                "Can-Redefine-Classes: true\n";
   113         //  ClassFileInstaller.writeJar("redefineagent.jar",
   114         //    ClassFileInstaller.Manifest.fromString(manifest),
   115         //    "RedefineClassHelper");
   116         static Manifest fromString(String manifest) throws Exception {
   117             return new Manifest(new ByteArrayInputStream(manifest.getBytes()));
   118         }
   120         public InputStream getInputStream() {
   121             return in;
   122         }
   123     }
   125     private static void writeJar(String jarFile, Manifest manifest, String classes[], int from, int to) throws Exception {
   126         if (DEBUG) {
   127             System.out.println("ClassFileInstaller: Writing to " + getJarPath(jarFile));
   128         }
   130         (new File(jarFile)).delete();
   131         FileOutputStream fos = new FileOutputStream(jarFile);
   132         ZipOutputStream zos = new ZipOutputStream(fos);
   134         // The manifest must be the first or second entry. See comments in JarInputStream
   135         // constructor and JDK-5046178.
   136         if (manifest != null) {
   137             writeToDisk(zos, "META-INF/MANIFEST.MF", manifest.getInputStream());
   138         }
   140         for (int i=from; i<to; i++) {
   141             writeClassToDisk(zos, classes[i]);
   142         }
   144         zos.close();
   145         fos.close();
   146     }
   148     /*
   149      * You can call ClassFileInstaller.writeJar() from your main test class instead of
   150      * using "@run ClassFileInstaller -jar ...". E.g.,
   151      *
   152      * String jarPath = ClassFileInstaller.getJarPath("myjar.jar", "sun.hotspot.WhiteBox")
   153      *
   154      * If you call this API, make sure you build ClassFileInstaller with the following tags:
   155      *
   156      * @library testlibrary
   157      * @build ClassFileInstaller
   158      */
   159     public static String writeJar(String jarFile, String... classes) throws Exception {
   160         writeJar(jarFile, null, classes, 0, classes.length);
   161         return getJarPath(jarFile);
   162     }
   164     public static String writeJar(String jarFile, Manifest manifest, String... classes) throws Exception {
   165         writeJar(jarFile, manifest, classes, 0, classes.length);
   166         return getJarPath(jarFile);
   167     }
   169     /**
   170      * This returns the absolute path to the file specified in "@ClassFileInstaller -jar myjar.jar",
   171      * In your test program, instead of using the JAR file name directly:
   172      *
   173      * String jarPath = "myjar.jar";
   174      *
   175      * you should call this function, like:
   176      *
   177      * String jarPath = ClassFileInstaller.getJarPath("myjar.jar")
   178      *
   179      * The reasons are:
   180      * (1) Using absolute path makes it easy to cut-and-paste from the JTR file and rerun your
   181      *     test in any directory.
   182      * (2) In the future, we may make the JAR file name unique to avoid clobbering
   183      *     during parallel JTREG execution.
   184      *
   185      */
   186     public static String getJarPath(String jarFileName) {
   187         return new File(jarFileName).getAbsolutePath();
   188     }
   190     public static void writeClassToDisk(String className) throws Exception {
   191         writeClassToDisk((ZipOutputStream)null, className);
   192     }
   193     private static void writeClassToDisk(ZipOutputStream zos, String className) throws Exception {
   194         writeClassToDisk(zos, className, "");
   195     }
   197     public static void writeClassToDisk(String className, String prependPath) throws Exception {
   198         writeClassToDisk(null, className, prependPath);
   199     }
   200     private static void writeClassToDisk(ZipOutputStream zos, String className, String prependPath) throws Exception {
   201         ClassLoader cl = ClassFileInstaller.class.getClassLoader();
   203         // Convert dotted class name to a path to a class file
   204         String pathName = className.replace('.', '/').concat(".class");
   205         InputStream is = cl.getResourceAsStream(pathName);
   206         if (is == null) {
   207             throw new RuntimeException("Failed to find " + pathName);
   208         }
   209         if (prependPath.length() > 0) {
   210             pathName = prependPath + "/" + pathName;
   211         }
   212         writeToDisk(zos, pathName, is);
   213     }
   215     public static void writeClassToDisk(String className, byte[] bytecode) throws Exception {
   216         writeClassToDisk(null, className, bytecode);
   217     }
   218     private static void writeClassToDisk(ZipOutputStream zos, String className, byte[] bytecode) throws Exception {
   219         writeClassToDisk(zos, className, bytecode, "");
   220     }
   222     public static void writeClassToDisk(String className, byte[] bytecode, String prependPath) throws Exception {
   223         writeClassToDisk(null, className, bytecode, prependPath);
   224     }
   225     private static void writeClassToDisk(ZipOutputStream zos, String className, byte[] bytecode, String prependPath) throws Exception {
   226         // Convert dotted class name to a path to a class file
   227         String pathName = className.replace('.', '/').concat(".class");
   228         if (prependPath.length() > 0) {
   229             pathName = prependPath + "/" + pathName;
   230         }
   231         writeToDisk(zos, pathName, new ByteArrayInputStream(bytecode));
   232     }
   234     private static void writeToDisk(ZipOutputStream zos, String pathName, InputStream is) throws Exception {
   235         if (DEBUG) {
   236             System.out.println("ClassFileInstaller: Writing " + pathName);
   237         }
   238         if (zos != null) {
   239             ZipEntry ze = new ZipEntry(pathName);
   240             zos.putNextEntry(ze);
   241             byte[] buf = new byte[1024];
   242             int len;
   243             while ((len = is.read(buf))>0){
   244                 zos.write(buf, 0, len);
   245             }
   246         } else {
   247             // Create the class file's package directory
   248             Path p = Paths.get(pathName);
   249             if (pathName.contains("/")) {
   250                 Files.createDirectories(p.getParent());
   251             }
   252             // Create the class file
   253             Files.copy(is, p, StandardCopyOption.REPLACE_EXISTING);
   254         }
   255         is.close();
   256     }
   257 }

mercurial