1 /* 2 * Copyright (c) 2018 by SAP AG, Walldorf, Germany. 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 test; 26 27 import java.io.IOException; 28 import java.io.InputStream; 29 import java.net.URL; 30 import java.security.SecureClassLoader; 31 import java.util.Arrays; 32 import java.util.HashSet; 33 34 /** 35 * This is a class loader which can load the same classes as another class loader. 36 * <p> 37 * This is mainly useful for tests when you want to load a class, but do it with a class 38 * loader you can dispose. The clone loader just asks the loader to be cloned to get 39 * the bytecodes, but defines the class itself. 40 * <p> 41 * Additionally you can specify a set of classes the loader should not be able to load. 42 */ 43 public class IAE_Loader2 extends SecureClassLoader { 44 45 /** 46 * The class loaded to clone. 47 */ 48 private final ClassLoader toClone; 49 50 /** 51 * The strings we cannot load. 52 */ 53 private final HashSet<String> notLoadable; 54 55 /** 56 * The strings we just delegate. 57 */ 58 private final HashSet<String> simpleDelegate; 59 60 /** 61 * Creates a class loader which can load the same classes as the loader which 62 * loaded the <code>IAE_Loader2</code> class itself. 63 * <p> 64 * Only the classes which are loadable by the 'parent' loader are delegated to that 65 * loader (to make it possible mix classes). 66 * 67 * @param name the name of the class loader. 68 * @param parent the parent loader which is first asked to load a class. 69 * @param toClone the class loader to mimic. The clone class loader will be able to 70 * load the same classes as the 'toClone' loader. 71 * @param notLoadable The classes we should not be able to load via this loader. 72 * @param simpleDelegate The names of the classes for which we simply delegate. 73 */ 74 public IAE_Loader2(String name, ClassLoader parent, ClassLoader toClone, String[] notLoadable, 75 String[] simpleDelegate) { 76 super(name, parent); 77 78 this.toClone = toClone; 79 this.notLoadable = new HashSet<>(Arrays.asList(notLoadable)); 80 this.simpleDelegate = new HashSet<>(Arrays.asList(simpleDelegate)); 81 } 82 83 /** 84 * @see java.lang.ClassLoader#findClass(java.lang.String) 85 */ 86 @Override 87 protected Class<?> findClass(String name) throws ClassNotFoundException { 88 if (notLoadable.contains(name)) { 89 throw new ClassNotFoundException("The clone class loader explicitely " + 90 "didn't found the class"); 91 } 92 93 if (simpleDelegate.contains(name)) { 94 return toClone.loadClass(name); 95 } 96 97 // We just ask the wrapper class loader to find the resource for us 98 URL res = toClone.getResource(name.replace('.', '/') + ".class"); 99 100 if (res == null) { 101 throw new ClassNotFoundException(name); 102 } 103 104 try { 105 InputStream is = res.openStream(); 106 byte[] code = readStreamIntoBuffer(is, 8192); 107 is.close(); 108 return defineClass(name, code, 0, code.length); 109 } catch (IOException e) { 110 throw new ClassNotFoundException(name, e); 111 } 112 } 113 114 /** 115 * Reads all data of a stream into a byte array. The method allocates as 116 * much memory as necessary to put the whole data into that byte 117 * array. The data is read in chunks of <code>chunkSize</code> 118 * chunks.<br><br> 119 * <b>Implementation Note: </b> The data is read in chunks of 120 * <code>chunkSize</code> bytes. The data is copied to the result 121 * array. The memory consumption at the end of the reading is 122 * <code>2 x [size of resulting array] + chunkSize</code>. 123 * 124 * @param is the stream to read the data from 125 * @param chunkSize the size of the chunks the data should be read in 126 * @return the <b>whole</b> data of the stream read into an byte array 127 * @throws IllegalArgumentException if chunkSize <= 0 128 * @throws NullPointerException if is == null 129 * @throws IOException thrown if the provided stream encounters IO problems 130 */ 131 public static byte[] readStreamIntoBuffer(InputStream is, int chunkSize) 132 throws IOException { 133 134 // Check preconditions. 135 if (chunkSize <= 0) { 136 throw new IllegalArgumentException("chunkSize <= 0"); 137 } 138 else if (is == null) { 139 throw new NullPointerException("is is null"); 140 } 141 142 // Temporary buffer for read operations and result buffer. 143 byte[] tempBuffer = new byte[chunkSize]; 144 byte[] buffer = new byte[0]; 145 146 int bytesRead = 0; // bytes actual read 147 int oldSize = 0; // size of the resulting buffer 148 149 while ((bytesRead = is.read(tempBuffer)) > 0) { 150 151 // Temporary reference to the buffer for the copy operation. 152 byte[] oldBuffer = buffer; 153 154 // Create a new buffer with the size needed and copy data. 155 buffer = new byte[oldSize + bytesRead]; 156 System.arraycopy(oldBuffer, 0, buffer, 0, oldBuffer.length); 157 158 // Copy the new data. 159 System.arraycopy(tempBuffer, 0, buffer, oldSize, bytesRead); 160 oldSize += bytesRead; 161 } 162 163 return buffer; 164 } 165 }