1 /*
   2  * Copyright (c) 2013, 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  *
  23  */
  24 
  25 package compiler.jsr292.methodHandleExceptions;
  26 
  27 import java.io.BufferedOutputStream;
  28 import java.io.FileNotFoundException;
  29 import java.io.FileOutputStream;
  30 import java.io.IOException;
  31 import java.net.URL;
  32 import java.net.URLClassLoader;
  33 import java.util.jar.JarEntry;
  34 import java.util.jar.JarOutputStream;
  35 
  36 /**
  37  * A ByteClassLoader is used to define classes from collections of bytes, as
  38  * well as loading classes in the usual way. It includes options to write the
  39  * classes to files in a jar, or to read the classes from jars in a later or
  40  * debugging run.
  41  *
  42  * If Boolean property byteclassloader.verbose is true, be chatty about jar
  43  * file operations.
  44  *
  45  */
  46 public class ByteClassLoader extends URLClassLoader {
  47 
  48     final static boolean verbose
  49             = Boolean.getBoolean("byteclassloader.verbose");
  50 
  51     final boolean read;
  52     final JarOutputStream jos;
  53     final String jar_name;
  54 
  55     /**
  56      * Make a new ByteClassLoader.
  57      *
  58      * @param jar_name  Basename of jar file to be read/written by this classloader.
  59      * @param read      If true, read classes from jar file instead of from parameter.
  60      * @param write     If true, write classes to jar files for offline study/use.
  61      *
  62      * @throws FileNotFoundException
  63      * @throws IOException
  64      */
  65     public ByteClassLoader(String jar_name, boolean read, boolean write)
  66             throws FileNotFoundException, IOException {
  67         super(read
  68                 ? new URL[]{new URL("file:" + jar_name + ".jar")}
  69                 : new URL[0]);
  70         this.read = read;
  71         this.jar_name = jar_name;
  72         this.jos = write
  73                 ? new JarOutputStream(
  74                 new BufferedOutputStream(
  75                 new FileOutputStream(jar_name + ".jar"))) : null;
  76         if (read && write) {
  77             throw new Error("At most one of read and write may be true.");
  78         }
  79     }
  80 
  81     private static void writeJarredFile(JarOutputStream jos, String file, String suffix, byte[] bytes) {
  82         String fileName = file.replace(".", "/") + "." + suffix;
  83         JarEntry ze = new JarEntry(fileName);
  84         try {
  85             ze.setSize(bytes.length);
  86             jos.putNextEntry(ze);
  87             jos.write(bytes);
  88             jos.closeEntry();
  89         } catch (IOException e) {
  90             throw new RuntimeException(e);
  91         }
  92     }
  93 
  94     /**
  95      * (pre)load class name using classData for the definition.
  96      *
  97      * @param name
  98      * @param classData
  99      * @return
 100      */
 101     public Class<?> loadBytes(String name, byte[] classData) throws ClassNotFoundException {
 102         if (jos != null) {
 103             if (verbose) {
 104                 System.out.println("ByteClassLoader: writing " + name);
 105             }
 106             writeJarredFile(jos, name, "class", classData);
 107         }
 108 
 109         Class<?> clazz = null;
 110         if (read) {
 111             if (verbose) {
 112                 System.out.println("ByteClassLoader: reading " + name + " from " + jar_name);
 113             }
 114             clazz = loadClass(name);
 115         } else {
 116             clazz = defineClass(name, classData, 0, classData.length);
 117             resolveClass(clazz);
 118         }
 119         return clazz;
 120     }
 121 
 122     public void close() {
 123         if (jos != null) {
 124             try {
 125                 if (verbose) {
 126                     System.out.println("ByteClassLoader: closing " + jar_name);
 127                 }
 128                 jos.close();
 129             } catch (IOException ex) {
 130             }
 131         }
 132     }
 133 }