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