1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.bcel.util;
18
19 import java.io.ByteArrayInputStream;
20 import java.util.Hashtable;
21 import org.apache.bcel.Constants;
22 import org.apache.bcel.classfile.ClassParser;
23 import org.apache.bcel.classfile.ConstantClass;
24 import org.apache.bcel.classfile.ConstantPool;
25 import org.apache.bcel.classfile.ConstantUtf8;
26 import org.apache.bcel.classfile.JavaClass;
27 import org.apache.bcel.classfile.Utility;
28
29 /***
30 * <p>Drop in replacement for the standard class loader of the JVM. You can use it
31 * in conjunction with the JavaWrapper to dynamically modify/create classes
32 * as they're requested.</p>
33 *
34 * <p>This class loader recognizes special requests in a distinct
35 * format, i.e., when the name of the requested class contains with
36 * "$$BCEL$$" it calls the createClass() method with that name
37 * (everything bevor the $$BCEL$$ is considered to be the package
38 * name. You can subclass the class loader and override that
39 * method. "Normal" classes class can be modified by overriding the
40 * modifyClass() method which is called just before defineClass().</p>
41 *
42 * <p>There may be a number of packages where you have to use the
43 * default class loader (which may also be faster). You can define the
44 * set of packages where to use the system class loader in the
45 * constructor. The default value contains "java.", "sun.",
46 * "javax."</p>
47 *
48 * @version $Id: ClassLoader.java 386056 2006-03-15 11:31:56Z tcurdt $
49 * @author <A HREF="mailto:m.dahm@gmx.de">M. Dahm</A>
50 * @see JavaWrapper
51 * @see ClassPath
52 */
53 public class ClassLoader extends java.lang.ClassLoader {
54
55 public static final String[] DEFAULT_IGNORED_PACKAGES = {
56 "java.", "javax.", "sun."
57 };
58 private Hashtable classes = new Hashtable();
59 private String[] ignored_packages/package-summary.html">ong> String[] ignored_packages;
60 private Repository repository = SyntheticRepository.getInstance();
61
62
63 /*** Ignored packages are by default ( "java.", "sun.",
64 * "javax."), i.e. loaded by system class loader
65 */
66 public ClassLoader() {
67 this(DEFAULT_IGNORED_PACKAGES);
68 }
69
70
71 /*** @param deferTo delegate class loader to use for ignored packages
72 */
73 public ClassLoader(java.lang.ClassLoader deferTo) {
74 super(deferTo);
75 this.ignored_packages = DEFAULT_IGNORED_PACKAGES;
76 this.repository = new ClassLoaderRepository(deferTo);
77 }
78
79
80 /*** @param ignored_packages classes contained in these packages will be loaded
81 * with the system class loader
82 */
83 public ClassLoader(String[] ignored_packages) {/package-summary.html">ong> ClassLoader(String[] ignored_packages) {
84 this.ignored_packages = ignored_packages;
85 }
86
87
88 /*** @param ignored_packages classes contained in these packages will be loaded
89 * with the system class loader
90 * @param deferTo delegate class loader to use for ignored packages
91 */
92 public ClassLoader(java/lang/ClassLoader deferTo, String[] ignored_packages) {/package-summary.html">ong> ClassLoader(java.lang.ClassLoader deferTo, String[] ignored_packages) {
93 this(ignored_packages);
94 this.repository = new ClassLoaderRepository(deferTo);
95 }
96
97
98 protected Class loadClass( String class_name, boolean resolve ) throws ClassNotFoundException {
99 Class cl = null;
100
101
102 if ((cl = (Class) classes.get(class_name)) == null) {
103
104
105
106 for (int i = 0; i < ignored_packages.length; i++) {
107 rong>if (class_name.startsWith(ignored_packages[i])) {
108 cl = getParent().loadClass(class_name);
109 break;
110 }
111 }
112 if (cl == null) {
113 JavaClass clazz = null;
114
115
116 if (class_name.indexOf("$$BCEL$$") >= 0) {
117 clazz = createClass(class_name);
118 } else {
119 if ((clazz = repository.loadClass(class_name)) != null) {
120 clazz = modifyClass(clazz);
121 } else {
122 throw new ClassNotFoundException(class_name);
123 }
124 }
125 if (clazz != null) {
126 byte[] bytes = clazz.getBytes();
127 cl = defineClass(class_name, bytes, 0, bytes.length);
128 } else {
129 cl = Class.forName(class_name);
130 }
131 }
132 if (resolve) {
133 resolveClass(cl);
134 }
135 }
136 classes.put(class_name, cl);
137 return cl;
138 }
139
140
141 /*** Override this method if you want to alter a class before it gets actually
142 * loaded. Does nothing by default.
143 */
144 protected JavaClass modifyClass( JavaClass clazz ) {
145 return clazz;
146 }
147
148
149 /***
150 * Override this method to create you own classes on the fly. The
151 * name contains the special token $$BCEL$$. Everything before that
152 * token is consddered to be a package name. You can encode you own
153 * arguments into the subsequent string. You must regard however not
154 * to use any "illegal" characters, i.e., characters that may not
155 * appear in a Java class name too<br>
156 *
157 * The default implementation interprets the string as a encoded compressed
158 * Java class, unpacks and decodes it with the Utility.decode() method, and
159 * parses the resulting byte array and returns the resulting JavaClass object.
160 *
161 * @param class_name compressed byte code with "$$BCEL$$" in it
162 */
163 protected JavaClass createClass( String class_name ) {
164 int index = class_name.indexOf("$$BCEL$$");
165 String real_name = class_name.substring(index + 8);
166 JavaClass clazz = null;
167 try {
168 byte[] bytes = Utility.decode(real_name, true);
169 ClassParser parser = new ClassParser(new ByteArrayInputStream(bytes), "foo");
170 clazz = parser.parse();
171 } catch (Throwable e) {
172 e.printStackTrace();
173 return null;
174 }
175
176 ConstantPool cp = clazz.getConstantPool();
177 ConstantClass cl = (ConstantClass) cp.getConstant(clazz.getClassNameIndex(),
178 Constants.CONSTANT_Class);
179 ConstantUtf8 name = (ConstantUtf8) cp.getConstant(cl.getNameIndex(),
180 Constants.CONSTANT_Utf8);
181 name.setBytes(class_name.replace('.', '/'));
182 return clazz;
183 }
184 }