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