1 /*
   2  * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
   3  * Copyright (c) 2018 SAP SE. All rights reserved.
   4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   5  *
   6  * This code is free software; you can redistribute it and/or modify it
   7  * under the terms of the GNU General Public License version 2 only, as
   8  * published by the Free Software Foundation.
   9  *
  10  * This code is distributed in the hope that it will be useful, but WITHOUT
  11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  13  * version 2 for more details (a copy is included in the LICENSE file that
  14  * accompanied this code).
  15  *
  16  * You should have received a copy of the GNU General Public License version
  17  * 2 along with this work; if not, write to the Free Software Foundation,
  18  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  19  *
  20  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  21  * or visit www.oracle.com if you need additional information or have any
  22  * questions.
  23  */
  24 
  25 
  26 import java.io.IOException;
  27 import java.io.InputStream;
  28 import java.net.URL;
  29 import java.security.SecureClassLoader;
  30 
  31 /**
  32  * This is a class loader which can load the same classes as another class loader.
  33  */
  34 public class CloneClassLoader extends SecureClassLoader {
  35 
  36         /**
  37      * The class loaded to clone.
  38      */
  39     private final ClassLoader toClone;
  40 
  41     /**
  42      * Creates a class loader which can load the same classes as the loader which
  43      * loaded the <code>CloneClassLoader</code> class itself.
  44      *
  45      * @param toClone the class loader to mimic. The clone class loader will be able to
  46      *                load the same classes as the 'toClone' loader.
  47      */
  48     public CloneClassLoader(ClassLoader toClone) {
  49         super("Clone", null);
  50         this.toClone = toClone;
  51     }
  52 
  53     /**
  54      * @see java.lang.ClassLoader#findClass(java.lang.String)
  55      */
  56     @Override
  57     protected Class<?> findClass(String name) throws ClassNotFoundException {
  58         // We just ask the wrapper class loader to find the resource for us
  59         URL res = toClone.getResource(name.replace('.', '/') + ".class");
  60 
  61         if (res == null) {
  62             throw new ClassNotFoundException(name);
  63         }
  64 
  65         try {
  66             InputStream is = res.openStream();
  67             byte[] code = readStreamIntoBuffer(is, 8192);
  68             is.close();
  69             return defineClass(name, code, 0, code.length);
  70         } catch (IOException e) {
  71             throw new ClassNotFoundException(name, e);
  72         }
  73     }
  74 
  75     /**
  76      * Reads all data of a stream into a byte array. The method allocates as
  77      * much memory as necessary to put the whole data into that byte
  78      * array. The data is read in chunks of <code>chunkSize</code>
  79      * chunks.<br><br>
  80      * <b>Implementation Note: </b> The data is read in chunks of
  81      * <code>chunkSize</code> bytes. The data is copied to the result
  82      * array. The memory consumption at the end of the reading is
  83      * <code>2 x [size of resulting array] + chunkSize</code>.
  84      *
  85      * @param is the stream to read the data from
  86      * @param chunkSize the size of the chunks the data should be read in
  87      * @return the <b>whole</b> data of the stream read into an byte array
  88      * @throws IllegalArgumentException if chunkSize <= 0
  89      * @throws NullPointerException if is == null
  90      * @throws IOException thrown if the provided stream encounters IO problems
  91      */
  92     public static byte[] readStreamIntoBuffer(InputStream is, int chunkSize)
  93             throws IOException {
  94 
  95         // check preconditions
  96         if (chunkSize <= 0) {
  97             throw new IllegalArgumentException("chunkSize <= 0");
  98         }
  99         else if (is == null) {
 100             throw new NullPointerException("is is null");
 101         }
 102 
 103         // temporary buffer for read operations and result buffer
 104         byte[] tempBuffer = new byte[chunkSize];
 105         byte[] buffer     = new byte[0];
 106 
 107         int bytesRead = 0;  // bytes actual read
 108         int oldSize   = 0;  // size of the resulting buffer
 109 
 110         while ((bytesRead = is.read(tempBuffer)) > 0) {
 111 
 112             // temporary reference to the buffer for the copy operation
 113             byte[] oldBuffer = buffer;
 114 
 115             // create a new buffer with the size needed and copy data
 116             buffer = new byte[oldSize + bytesRead];
 117             System.arraycopy(oldBuffer,  0, buffer, 0, oldBuffer.length);
 118 
 119             // copy the new data
 120             System.arraycopy(tempBuffer, 0, buffer, oldSize, bytesRead);
 121             oldSize += bytesRead;
 122         }
 123 
 124         return buffer;
 125     }
 126 }