1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 package org.microemu.app.classloader;
23
24 import java.io.File;
25 import java.io.IOException;
26 import java.io.InputStream;
27 import java.net.MalformedURLException;
28 import java.net.URL;
29 import java.net.URLClassLoader;
30 import java.security.AccessControlContext;
31 import java.security.AccessController;
32 import java.security.PrivilegedActionException;
33 import java.security.PrivilegedExceptionAction;
34 import java.util.HashSet;
35 import java.util.Iterator;
36 import java.util.Set;
37 import java.util.StringTokenizer;
38
39 import org.microemu.app.util.IOUtils;
40 import org.microemu.log.Logger;
41
42
43
44
45
46
47
48
49
50
51 public class MIDletClassLoader extends URLClassLoader {
52
53
54
55 public static boolean instrumentMIDletClasses = true;
56
57 public static boolean traceClassLoading = false;
58
59 public static boolean traceSystemClassLoading = false;
60
61 public static boolean enhanceCatchBlock = false;
62
63 private final static boolean debug = false;
64
65 private boolean delegatingToParent = false;
66
67 private InstrumentationConfig config;
68
69 private Set noPreporcessingNames;
70
71
72 private AccessControlContext acc;
73
74 private static class LoadClassByParentException extends ClassNotFoundException {
75
76 public LoadClassByParentException(String name) {
77 super(name);
78 }
79
80 private static final long serialVersionUID = 1L;
81
82 }
83
84 public MIDletClassLoader(ClassLoader parent) {
85 super(new URL[] {}, parent);
86 noPreporcessingNames = new HashSet();
87 acc = AccessController.getContext();
88 config = new InstrumentationConfig();
89 config.setEnhanceCatchBlock(enhanceCatchBlock);
90 config.setEnhanceThreadCreation(true);
91 }
92
93
94
95
96
97
98 public void configure(MIDletClassLoaderConfig clConfig) throws MalformedURLException {
99 for (Iterator iter = clConfig.appclasspath.iterator(); iter.hasNext();) {
100 String path = (String) iter.next();
101 StringTokenizer st = new StringTokenizer(path, File.pathSeparator);
102 while (st.hasMoreTokens()) {
103 this.addURL(new URL(IOUtils.getCanonicalFileClassLoaderURL(new File(st.nextToken()))));
104 }
105 }
106 for (Iterator iter = clConfig.appclasses.iterator(); iter.hasNext();) {
107 this.addClassURL((String) iter.next());
108 }
109 this.delegatingToParent = (clConfig.delegationType == MIDletClassLoaderConfig.DELEGATION_DELEGATING);
110 }
111
112
113
114
115
116
117
118
119 public void addClassURL(String className) throws MalformedURLException {
120 String resource = getClassResourceName(className);
121 URL url = getParent().getResource(resource);
122 if (url == null) {
123 url = this.getResource(resource);
124 }
125 if (url == null) {
126 throw new MalformedURLException("Unable to find class " + className + " URL");
127 }
128 String path = url.toExternalForm();
129 if (debug) {
130 Logger.debug("addClassURL ", path);
131 }
132 addURL(new URL(path.substring(0, path.length() - resource.length())));
133 }
134
135 static URL getClassURL(ClassLoader parent, String className) throws MalformedURLException {
136 String resource = getClassResourceName(className);
137 URL url = parent.getResource(resource);
138 if (url == null) {
139 throw new MalformedURLException("Unable to find class " + className + " URL");
140 }
141 String path = url.toExternalForm();
142 return new URL(path.substring(0, path.length() - resource.length()));
143 }
144
145 public void addURL(URL url) {
146 if (debug) {
147 Logger.debug("addURL ", url.toString());
148 }
149 super.addURL(url);
150 }
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190 protected synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException {
191 if (debug) {
192 Logger.debug("loadClass", name);
193 }
194
195 Class result = findLoadedClass(name);
196 if (result == null) {
197 try {
198 result = findClass(name);
199 if (debug && (result == null)) {
200 Logger.debug("loadClass not found", name);
201 }
202 } catch (ClassNotFoundException e) {
203
204 if ((e instanceof LoadClassByParentException) || this.delegatingToParent) {
205 if (traceSystemClassLoading) {
206 Logger.info("Load system class", name);
207 }
208
209
210 result = super.loadClass(name, false);
211 if (result == null) {
212 throw new ClassNotFoundException(name);
213 }
214 }
215 }
216 }
217 if (resolve) {
218 resolveClass(result);
219 }
220 return result;
221 }
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251 public URL getResource(final String name) {
252 try {
253 return (URL) AccessController.doPrivileged(new PrivilegedExceptionAction() {
254 public Object run() {
255 URL url = findResource(name);
256 if (delegatingToParent && (getParent() != null)) {
257 url = getParent().getResource(name);
258 }
259 return url;
260 }
261 }, acc);
262 } catch (PrivilegedActionException e) {
263 if (debug) {
264 Logger.error("Unable to find resource " + name + " ", e);
265 }
266 return null;
267 }
268 }
269
270
271
272
273 public InputStream getResourceAsStream(String name) {
274 final URL url = getResource(name);
275 if (url == null) {
276 return null;
277 }
278
279 try {
280 return (InputStream) AccessController.doPrivileged(new PrivilegedExceptionAction() {
281 public Object run() throws IOException {
282 return url.openStream();
283 }
284 }, acc);
285 } catch (PrivilegedActionException e) {
286 if (debug) {
287 Logger.debug("Unable to find resource for class " + name + " ", e);
288 }
289 return null;
290 }
291
292 }
293
294 public boolean classLoadByParent(String className) {
295
296 if (className.startsWith("java.")) {
297 return true;
298 }
299
300
301
302
303 if (className.startsWith("sun.reflect.")) {
304 return true;
305 }
306
307 if (className.startsWith("javax.microedition.")) {
308 return true;
309 }
310 if (className.startsWith("javax.")) {
311 return true;
312 }
313 if (noPreporcessingNames.contains(className)) {
314 return true;
315 }
316 return false;
317 }
318
319
320
321
322
323
324 public void disableClassPreporcessing(Class klass) {
325 disableClassPreporcessing(klass.getName());
326 }
327
328 public void disableClassPreporcessing(String className) {
329 noPreporcessingNames.add(className);
330 }
331
332 public static String getClassResourceName(String className) {
333 return className.replace('.', '/').concat(".class");
334 }
335
336 protected Class findClass(final String name) throws ClassNotFoundException {
337 if (debug) {
338 Logger.debug("findClass", name);
339 }
340 if (classLoadByParent(name)) {
341 throw new LoadClassByParentException(name);
342 }
343 InputStream is;
344 try {
345 is = (InputStream) AccessController.doPrivileged(new PrivilegedExceptionAction() {
346 public Object run() throws ClassNotFoundException {
347 return getResourceAsStream(getClassResourceName(name));
348 }
349 }, acc);
350 } catch (PrivilegedActionException e) {
351 if (debug) {
352 Logger.debug("Unable to find resource for class " + name + " ", e);
353 }
354 throw new ClassNotFoundException(name, e.getCause());
355 }
356
357 if (is == null) {
358 if (debug) {
359 Logger.debug("Unable to find resource for class", name);
360 }
361 throw new ClassNotFoundException(name);
362 }
363 byte[] byteCode;
364 int byteCodeLength;
365 try {
366 if (traceClassLoading) {
367 Logger.info("Load MIDlet class", name);
368 }
369 if (instrumentMIDletClasses) {
370 byteCode = ClassPreprocessor.instrument(is, config);
371 byteCodeLength = byteCode.length;
372 } else {
373 final int chunkSize = 1024 * 2;
374
375 final int maxClassSizeSize = 1024 * 16;
376 byteCode = new byte[chunkSize];
377 byteCodeLength = 0;
378 do {
379 int retrived;
380 try {
381 retrived = is.read(byteCode, byteCodeLength, byteCode.length - byteCodeLength);
382 } catch (IOException e) {
383 throw new ClassNotFoundException(name, e);
384 }
385 if (retrived == -1) {
386 break;
387 }
388 if (byteCode.length + chunkSize > maxClassSizeSize) {
389 throw new ClassNotFoundException(name, new ClassFormatError(
390 "Class object is bigger than 16 Kilobyte"));
391 }
392 byteCodeLength += retrived;
393 if (byteCode.length == byteCodeLength) {
394 byte[] newData = new byte[byteCode.length + chunkSize];
395 System.arraycopy(byteCode, 0, newData, 0, byteCode.length);
396 byteCode = newData;
397 } else if (byteCode.length < byteCodeLength) {
398 throw new ClassNotFoundException(name, new ClassFormatError("Internal read error"));
399 }
400 } while (true);
401 }
402 } finally {
403 try {
404 is.close();
405 } catch (IOException ignore) {
406 }
407 }
408 if ((debug) && (instrumentMIDletClasses)) {
409 Logger.debug("instrumented ", name);
410 }
411 return defineClass(name, byteCode, 0, byteCodeLength);
412 }
413 }