1 /* 2 * Copyright (c) 2015, 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 package utils; 24 25 import java.io.*; 26 import java.util.*; 27 28 /** 29 * Classloader that generates classes on the fly. 30 * 31 * This classloader can load classes with name starting with 'Class'. It will 32 * use TemplateClass as template and will replace class name in the bytecode of 33 * template class. It can be used for example to detect memory leaks in class 34 * loading or to quickly fill PermGen. 35 */ 36 class GeneratingClassLoader extends ClassLoader { 37 38 public synchronized Class loadClass(String name) throws ClassNotFoundException { 39 return loadClass(name, false); 40 } 41 42 public synchronized Class loadClass(String name, boolean resolve) 43 throws ClassNotFoundException { 44 Class c = findLoadedClass(name); 45 if (c != null) { 46 return c; 47 } 48 if (!name.startsWith(PREFIX)) { 49 return super.loadClass(name, resolve); 50 } 51 if (name.length() != templateClassName.length()) { 52 throw new ClassNotFoundException("Only can load classes with name.length() = " + getNameLength() + " got: '" + name + "' length: " + name.length()); 53 } 54 byte[] bytecode = getPatchedByteCode(name); 55 c = defineClass(name, bytecode, 0, bytecode.length); 56 if (resolve) { 57 resolveClass(c); 58 } 59 return c; 60 } 61 62 /** 63 * Create generating class loader that will use class file for given class 64 * from classpath as template. 65 */ 66 GeneratingClassLoader(String templateClassName) { 67 this.templateClassName = templateClassName; 68 classPath = System.getProperty("java.class.path").split(File.pathSeparator); 69 try { 70 templateClassNameBytes = templateClassName.getBytes(encoding); 71 } catch (UnsupportedEncodingException e) { 72 throw new RuntimeException(e); 73 } 74 } 75 76 /** 77 * Create generating class loader that will use class file for 78 * nsk.share.classload.TemplateClass as template. 79 */ 80 GeneratingClassLoader() { 81 this(TemplateClass.class.getName()); 82 } 83 84 int getNameLength() { 85 return templateClassName.length(); 86 } 87 88 String getPrefix() { 89 return PREFIX; 90 } 91 92 String getClassName(int number) { 93 StringBuffer sb = new StringBuffer(); 94 sb.append(PREFIX); 95 sb.append(number); 96 int n = templateClassName.length() - sb.length(); 97 for (int i = 0; i < n; ++i) { 98 sb.append("_"); 99 } 100 return sb.toString(); 101 } 102 103 private byte[] getPatchedByteCode(String name) throws ClassNotFoundException { 104 try { 105 byte[] bytecode = getByteCode(); 106 String fname = name.replace(".", File.separator); 107 byte[] replaceBytes = fname.getBytes(encoding); 108 for (int offset : offsets) { 109 for (int i = 0; i < replaceBytes.length; ++i) { 110 bytecode[offset + i] = replaceBytes[i]; 111 } 112 } 113 return bytecode; 114 } catch (UnsupportedEncodingException e) { 115 throw new RuntimeException(e); 116 } 117 } 118 119 private byte[] getByteCode() throws ClassNotFoundException { 120 if (bytecode == null) { 121 readByteCode(); 122 } 123 if (offsets == null) { 124 getOffsets(bytecode); 125 if (offsets == null) { 126 throw new RuntimeException("Class name not found in template class file"); 127 } 128 } 129 return (byte[]) bytecode.clone(); 130 } 131 132 private void readByteCode() throws ClassNotFoundException { 133 String fname = templateClassName.replace(".", File.separator) + ".class"; 134 File target = null; 135 for (int i = 0; i < classPath.length; ++i) { 136 target = new File(classPath[i] + File.separator + fname); 137 if (target.exists()) { 138 break; 139 } 140 } 141 142 if (target == null || !target.exists()) { 143 throw new ClassNotFoundException("File not found: " + target); 144 } 145 try { 146 bytecode = ClassLoadUtils.readFile(target); 147 } catch (IOException e) { 148 throw new ClassNotFoundException(templateClassName, e); 149 } 150 } 151 152 private void getOffsets(byte[] bytecode) { 153 List<Integer> offsets = new ArrayList<Integer>(); 154 if (this.offsets == null) { 155 String pname = templateClassName.replace(".", "/"); 156 try { 157 byte[] pnameb = pname.getBytes(encoding); 158 int i = 0; 159 while (true) { 160 while (i < bytecode.length) { 161 int j = 0; 162 while (j < pnameb.length && bytecode[i + j] == pnameb[j]) { 163 ++j; 164 } 165 if (j == pnameb.length) { 166 break; 167 } 168 i++; 169 } 170 if (i == bytecode.length) { 171 break; 172 } 173 offsets.add(new Integer(i)); 174 i++; 175 } 176 } catch (UnsupportedEncodingException e) { 177 throw new RuntimeException(e); 178 } 179 this.offsets = new int[offsets.size()]; 180 for (int i = 0; i < offsets.size(); ++i) { 181 this.offsets[i] = offsets.get(i).intValue(); 182 } 183 } 184 } 185 186 static final String DEFAULT_CLASSNAME = TemplateClass.class.getName(); 187 static final String PREFIX = "Class"; 188 189 private final String[] classPath; 190 private byte[] bytecode; 191 private int[] offsets; 192 private final String encoding = "UTF8"; 193 private final String templateClassName; 194 private final byte[] templateClassNameBytes; 195 }