View Javadoc

1   /**
2    *  MicroEmulator
3    *  Copyright (C) 2001-2003 Bartek Teodorczyk <barteo@barteo.net>
4    *
5    *  This library is free software; you can redistribute it and/or
6    *  modify it under the terms of the GNU Lesser General Public
7    *  License as published by the Free Software Foundation; either
8    *  version 2.1 of the License, or (at your option) any later version.
9    *
10   *  This library is distributed in the hope that it will be useful,
11   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13   *  Lesser General Public License for more details.
14   *
15   *  You should have received a copy of the GNU Lesser General Public
16   *  License along with this library; if not, write to the Free Software
17   *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18   *
19   *  @version $Id: Common.java 1662 2008-03-24 19:57:30Z vlads $
20   */
21  package org.microemu.app;
22  
23  import java.io.File;
24  import java.io.FileNotFoundException;
25  import java.io.IOException;
26  import java.io.InputStream;
27  import java.io.Serializable;
28  import java.lang.reflect.Constructor;
29  import java.lang.reflect.InvocationTargetException;
30  import java.lang.reflect.Method;
31  import java.lang.reflect.Modifier;
32  import java.net.MalformedURLException;
33  import java.net.URL;
34  import java.net.URLConnection;
35  import java.util.Enumeration;
36  import java.util.HashMap;
37  import java.util.Iterator;
38  import java.util.List;
39  import java.util.Locale;
40  import java.util.Map;
41  import java.util.Vector;
42  import java.util.jar.JarEntry;
43  import java.util.jar.JarInputStream;
44  import java.util.zip.ZipException;
45  
46  import javax.microedition.midlet.MIDlet;
47  import javax.microedition.midlet.MIDletStateChangeException;
48  
49  import org.microemu.EmulatorContext;
50  import org.microemu.Injected;
51  import org.microemu.MIDletAccess;
52  import org.microemu.MIDletBridge;
53  import org.microemu.MIDletContext;
54  import org.microemu.MIDletEntry;
55  import org.microemu.MicroEmulator;
56  import org.microemu.RecordStoreManager;
57  import org.microemu.app.classloader.ExtensionsClassLoader;
58  import org.microemu.app.classloader.MIDletClassLoader;
59  import org.microemu.app.classloader.MIDletClassLoaderConfig;
60  import org.microemu.app.launcher.Launcher;
61  import org.microemu.app.ui.Message;
62  import org.microemu.app.ui.ResponseInterfaceListener;
63  import org.microemu.app.ui.StatusBarListener;
64  import org.microemu.app.util.DeviceEntry;
65  import org.microemu.app.util.FileRecordStoreManager;
66  import org.microemu.app.util.IOUtils;
67  import org.microemu.app.util.MIDletResourceLoader;
68  import org.microemu.app.util.MIDletSystemProperties;
69  import org.microemu.app.util.MIDletThread;
70  import org.microemu.app.util.MIDletTimer;
71  import org.microemu.app.util.MidletURLReference;
72  import org.microemu.device.Device;
73  import org.microemu.device.DeviceFactory;
74  import org.microemu.device.impl.DeviceImpl;
75  import org.microemu.log.Logger;
76  import org.microemu.microedition.ImplFactory;
77  import org.microemu.microedition.ImplementationInitialization;
78  import org.microemu.microedition.io.ConnectorImpl;
79  import org.microemu.util.Base64Coder;
80  import org.microemu.util.JadMidletEntry;
81  import org.microemu.util.JadProperties;
82  import org.microemu.util.MemoryRecordStoreManager;
83  
84  public class Common implements MicroEmulator, CommonInterface {
85  
86  	protected EmulatorContext emulatorContext;
87  
88  	protected JadProperties jad = new JadProperties();
89  
90  	private static Common instance;
91  
92  	private static Launcher launcher;
93  
94  	private static StatusBarListener statusBarListener = null;
95  
96  	private JadProperties manifest = new JadProperties();
97  
98  	private RecordStoreManager recordStoreManager;
99  
100 	private ResponseInterfaceListener responseInterfaceListener = null;
101 
102 	private ExtensionsClassLoader extensionsClassLoader;
103 
104 	private Vector extensions = new Vector();
105 
106 	private MIDletClassLoaderConfig mIDletClassLoaderConfig;
107 
108 	private boolean useSystemClassLoader = false;
109 
110 	private boolean autoTests = false;
111 
112 	private String propertiesJad = null;
113 
114 	private String midletClassOrJad = null;
115 
116 	private String jadURL = null;
117 
118 	private Object destroyNotify = new Object();
119 
120 	public Common(EmulatorContext context) {
121 		instance = this;
122 		this.emulatorContext = context;
123 
124         try {
125 		    launcher = new Launcher(this);
126 		    launcher.setCurrentMIDlet(launcher);
127 		} finally {
128 		    MIDletBridge.setThreadMIDletContext(null);
129 		}
130 
131 		/*
132 		 * Initialize secutity context for implemenations, May be there are
133 		 * better place for this call
134 		 */
135 		ImplFactory.instance();
136 		MIDletSystemProperties.initContext();
137 		// TODO integrate with ImplementationInitialization
138 		ImplFactory.registerGCF(ImplFactory.DEFAULT, new ConnectorImpl());
139 
140 		MIDletBridge.setMicroEmulator(this);
141 	}
142 
143 	public RecordStoreManager getRecordStoreManager() {
144 		return recordStoreManager;
145 	}
146 
147 	public void setRecordStoreManager(RecordStoreManager manager) {
148 		this.recordStoreManager = manager;
149 	}
150 
151 	public String getAppProperty(String key) {
152 		if (key.equals("microedition.platform")) {
153 			return "MicroEmulator";
154 		} else if (key.equals("microedition.profiles")) {
155 			return "MIDP-2.0";
156 		} else if (key.equals("microedition.configuration")) {
157 			return "CLDC-1.0";
158 		} else if (key.equals("microedition.locale")) {
159 			return Locale.getDefault().getLanguage();
160 		} else if (key.equals("microedition.encoding")) {
161 			return System.getProperty("file.encoding");
162 		}
163 
164 		String result = jad.getProperty(key);
165 		if (result == null) {
166 			result = manifest.getProperty(key);
167 		}
168 
169 		return result;
170 	}
171 
172 	public InputStream getResourceAsStream(String name) {
173 		return emulatorContext.getResourceAsStream(name);
174 	}
175 
176 	public void notifyDestroyed(MIDletContext midletContext) {
177 		Logger.debug("notifyDestroyed");
178 		notifyImplementationMIDletDestroyed();
179 		startLauncher(midletContext);
180 	}
181 
182 	public void destroyMIDletContext(MIDletContext midletContext) {
183 		if ((midletContext != null) && (MIDletBridge.getMIDletContext() == midletContext)
184 				&& !midletContext.isLauncher()) {
185 		      Logger.debug("destroyMIDletContext");
186 		}
187 		MIDletThread.contextDestroyed(midletContext);
188 		synchronized (destroyNotify) {
189 			destroyNotify.notifyAll();
190 		}
191 	}
192 
193 	public Launcher getLauncher() {
194 		return launcher;
195 	}
196 
197 	public static void dispose() {
198 		try {
199 			MIDletAccess midletAccess = MIDletBridge.getMIDletAccess();
200 			if (midletAccess != null) {
201 				midletAccess.destroyApp(true);
202 			}
203 		} catch (MIDletStateChangeException ex) {
204 			Logger.error(ex);
205 		}
206 		// TODO to be removed when event dispatcher will run input method task
207 		DeviceFactory.getDevice().getInputMethod().dispose();
208 	}
209 
210 	public static boolean isJadExtension(String nameString) {
211 		if (nameString == null) {
212 			return false;
213 		}
214 		int end = nameString.lastIndexOf('.');
215 		if (end == -1) {
216 			return false;
217 		}
218 		return nameString.substring(end + 1, nameString.length()).toLowerCase(Locale.ENGLISH).equals("jad");
219 	}
220 
221 	/**
222 	 * TODO add proper Error handling and display in this function.
223 	 */
224 	public static void openJadUrlSafe(String urlString) {
225 		try {
226 			getInstance().openJadUrl(urlString);
227 		} catch (IOException e) {
228 			Message.error("Unable to open jad " + urlString, e);
229 		}
230 	}
231 
232 	protected void openJadUrl(String urlString) throws IOException {
233 		midletClassOrJad = urlString;
234 		if (!autoTests) {
235 			openJadUrl(urlString, createMIDletClassLoader());
236 		} else {
237 			runAutoTests(urlString, false);
238 		}
239 	}
240 
241 	private void runAutoTests(final String urlString, final boolean exitAtTheEnd) {
242 		final Common common = getInstance();
243 		Thread t = new Thread("AutoTestsThread") {
244 			public void run() {
245 				boolean firstJad = true;
246 				do {
247 					common.jad.clear();
248 					Logger.debug("AutoTests open jad", urlString);
249 					try {
250 						common.jad = loadJadProperties(urlString);
251 					} catch (IOException e) {
252 						if (firstJad) {
253 							Logger.debug(e);
254 						} else {
255 							Logger.debug("AutoTests no more tests");
256 						}
257 						break;
258 					}
259 					firstJad = false;
260 
261 					JadMidletEntry jadMidletEntry;
262 					Iterator it = common.jad.getMidletEntries().iterator();
263 					if (!it.hasNext()) {
264 						Message.error("MIDlet Suite has no entries");
265 						break;
266 					}
267 					jadMidletEntry = (JadMidletEntry) it.next();
268 					String midletClassName = jadMidletEntry.getClassName();
269 
270 					boolean firstJar = true;
271 					do {
272 						MIDletClassLoader midletClassLoader = createMIDletClassLoader();
273 						String tmpURL = saveJar2TmpFile(urlString, firstJar);
274 						if (tmpURL == null) {
275 							Logger.debug("AutoTests no new jar");
276 							break;
277 						}
278 						firstJar = false;
279 						Class midletClass;
280 						try {
281 							loadJar(urlString, tmpURL, midletClassLoader);
282 							midletClass = midletClassLoader.loadClass(midletClassName);
283 						} catch (ClassNotFoundException e) {
284 							Logger.debug(e);
285 							break;
286 						}
287 						Logger.debug("AutoTests start class", midletClassName);
288 						MIDletContext context = startMidlet(midletClass, MIDletBridge.getMIDletAccess());
289 						// TODO Proper test If this is still active conetex.
290 						if (MIDletBridge.getMIDletContext() == context) {
291 							synchronized (destroyNotify) {
292 								try {
293 									destroyNotify.wait();
294 								} catch (InterruptedException e) {
295 									return;
296 								}
297 							}
298 						}
299 						while (MIDletThread.hasRunningThreads(context)) {
300 							try {
301 								Thread.sleep(100);
302 							} catch (InterruptedException e) {
303 								break;
304 							}
305 						}
306 						Logger.debug("AutoTests ends");
307 					} while (true);
308 
309 				} while (true);
310 
311 				if (exitAtTheEnd) {
312 					System.exit(0);
313 				}
314 			}
315 		};
316 
317 		t.start();
318 	}
319 
320 	protected String saveJar2TmpFile(String jarUrl, boolean reportError) {
321 		InputStream is = null;
322 		try {
323 			URL url = new URL(jad.getJarURL());
324 			URLConnection conn = url.openConnection();
325 			is = conn.getInputStream();
326 			File tmp = File.createTempFile("me2-", ".jar");
327 			tmp.deleteOnExit();
328 			IOUtils.copyToFile(is, tmp);
329 			return IOUtils.getCanonicalFileClassLoaderURL(tmp);
330 		} catch (IOException e) {
331 			if (reportError) {
332 				Message.error("Unable to open jar " + jarUrl, e);
333 			}
334 			return null;
335 		} finally {
336 			IOUtils.closeQuietly(is);
337 		}
338 	}
339 
340 	private void openJadUrl(String urlString, MIDletClassLoader midletClassLoader) throws IOException {
341 		try {
342 			Logger.debug("openJad", urlString);
343 			setStatusBar("Loading...");
344 			jad.clear();
345 			jad = loadJadProperties(urlString);
346 
347 			loadJar(urlString, jad.getJarURL(), midletClassLoader);
348 
349 			Config.getUrlsMRU().push(new MidletURLReference(jad.getSuiteName(), urlString));
350 		} catch (MalformedURLException ex) {
351 			throw ex;
352 		} catch (ClassNotFoundException ex) {
353 			Logger.error(ex);
354 			throw new IOException(ex.getMessage());
355 		} catch (FileNotFoundException ex) {
356 			Message.error("File Not found", urlString, ex);
357 		} catch (NullPointerException ex) {
358 			Logger.error("Cannot open jad", urlString, ex);
359 		} catch (IllegalArgumentException ex) {
360 			Logger.error("Cannot open jad", urlString, ex);
361 		}
362 	}
363 
364 	private MIDletContext startMidlet(Class midletClass, MIDletAccess previousMidletAccess) {
365 		try {
366 			if (previousMidletAccess != null) {
367 				previousMidletAccess.destroyApp(true);
368 			}
369 		} catch (Throwable e) {
370 			Message.error("Unable to destroy MIDlet, " + Message.getCauseMessage(e), e);
371 		}
372 
373 		MIDletContext context = new MIDletContext();
374 		MIDletBridge.setThreadMIDletContext(context);
375 		try {
376 			MIDlet m;
377 
378 			final String errorTitle = "Error starting MIDlet";
379 
380 			try {
381 				Object object = midletClass.newInstance();
382 				if (!(object instanceof MIDlet)) {
383 					Message.error(errorTitle, "Class " + midletClass.getName() + " should extend MIDlet");
384 					return null;
385 				}
386 				m = (MIDlet) object;
387 			} catch (Throwable e) {
388 				Message.error(errorTitle, "Unable to create MIDlet, " + Message.getCauseMessage(e), e);
389 				MIDletBridge.destroyMIDletContext(context);
390 				return null;
391 			}
392 
393 			try {
394 				if (context.getMIDlet() != m) {
395 					throw new Error("MIDlet Context corrupted");
396 				}
397 				context.getMIDletAccess().startApp();
398 
399 				launcher.setCurrentMIDlet(m);
400 				notifyImplementationMIDletStart();
401 				return context;
402 			} catch (Throwable e) {
403 				Message.error(errorTitle, "Unable to start MIDlet, " + Message.getCauseMessage(e), e);
404 				MIDletBridge.destroyMIDletContext(context);
405 				return null;
406 			}
407 
408 		} finally {
409 			MIDletBridge.setThreadMIDletContext(null);
410 		}
411 
412 	}
413 
414 	protected void startLauncher(MIDletContext midletContext) {
415 		if ((midletContext != null) && (midletContext.isLauncher())) {
416 			return;
417 		}
418 		if (midletContext != null) {
419 			try {
420 				MIDletAccess previousMidletAccess = midletContext.getMIDletAccess();
421 				if (previousMidletAccess != null) {
422 					previousMidletAccess.destroyApp(true);
423 				}
424 			} catch (Throwable e) {
425 				Logger.error("destroyApp error", e);
426 			}
427 		}
428 
429 		try {
430 			launcher = new Launcher(this);
431 			MIDletBridge.getMIDletAccess(launcher).startApp();
432 			launcher.setCurrentMIDlet(launcher);
433 		} catch (Throwable e) {
434 			Message.error("Unable to start launcher MIDlet, " + Message.getCauseMessage(e), e);
435 			handleStartMidletException(e);
436 		} finally {
437 		    MIDletBridge.setThreadMIDletContext(null);
438 		}
439 	}
440 
441 	public void setStatusBarListener(StatusBarListener listener) {
442 		statusBarListener = listener;
443 	}
444 
445 	public boolean platformRequest(String URL) {
446 		return false;
447 	}
448 
449 	public void setResponseInterfaceListener(ResponseInterfaceListener listener) {
450 		responseInterfaceListener = listener;
451 	}
452 
453 	protected void handleStartMidletException(Throwable e) {
454 
455 	}
456 
457 	/**
458 	 * Show message describing problem with jar if any
459 	 */
460 	protected boolean describeJarProblem(URL jarUrl, MIDletClassLoader midletClassLoader) {
461 		InputStream is = null;
462 		JarInputStream jis = null;
463 		try {
464 			final String message = "Unable to open jar " + jarUrl;
465 			URLConnection conn;
466 			try {
467 				conn = jarUrl.openConnection();
468 			} catch (IOException e) {
469 				Message.error(message, e);
470 				return true;
471 			}
472 			try {
473 				is = conn.getInputStream();
474 			} catch (FileNotFoundException e) {
475 				Message.error("The system cannot find the jar file " + jarUrl, e);
476 				return true;
477 			} catch (IOException e) {
478 				Message.error(message, e);
479 				return true;
480 			}
481 			try {
482 				jis = new JarInputStream(is);
483 			} catch (IOException e) {
484 				Message.error(message, e);
485 				return true;
486 			}
487 			try {
488 				JarEntry entry = jis.getNextJarEntry();
489 				if (entry == null) {
490 					Message.error("Empty jar " + jarUrl);
491 					return true;
492 				}
493 				// Read till the end
494 				while (jis.getNextJarEntry() != null)
495 					;
496 			} catch (ZipException e) {
497 				Message.error("Problem reading jar " + jarUrl, e);
498 				return true;
499 			} catch (IOException e) {
500 				Message.error("Problem reading jar " + jarUrl, e);
501 				return true;
502 			}
503 			// There seems to be no poblem with jar
504 			return false;
505 		} finally {
506 			IOUtils.closeQuietly(jis);
507 			IOUtils.closeQuietly(is);
508 		}
509 	}
510 
511 	protected void loadJar(String jadUrl, String jarUrl, MIDletClassLoader midletClassLoader)
512 			throws ClassNotFoundException {
513 		if (jarUrl == null) {
514 			throw new ClassNotFoundException("Cannot find MIDlet-Jar-URL property in jad");
515 		}
516 		Logger.debug("openJar", jarUrl);
517 
518 		// Close Current MIDlet before oppening new one.
519 		dispose();
520 		// MIDletBridge.destroyMIDletContext(MIDletBridge.getMIDletContext());
521 		MIDletBridge.clear();
522 
523 		setResponseInterface(false);
524 		try {
525 			URL url = null;
526 			try {
527 				url = new URL(jarUrl);
528 			} catch (MalformedURLException ex) {
529 				try {
530 					url = new URL(jadUrl.substring(0, jadUrl.lastIndexOf('/') + 1) + jarUrl);
531 					// TODO check if IOUtils.getCanonicalFileURL is needed
532 					jad.setCorrectedJarURL(url.toExternalForm());
533 					Logger.debug("openJar url", url);
534 				} catch (MalformedURLException ex1) {
535 					Logger.error("Unable to find jar url", ex1);
536 					setResponseInterface(true);
537 				}
538 			}
539 
540 			midletClassLoader.addURL(url);
541 
542 			Launcher.removeMIDletEntries();
543 
544 			manifest.clear();
545 			InputStream is = null;
546 			try {
547 				is = midletClassLoader.getResourceAsStream("META-INF/MANIFEST.MF");
548 				if (is == null) {
549 					if (!describeJarProblem(url, midletClassLoader)) {
550 						Message.error("Unable to find MANIFEST in MIDlet jar");
551 					}
552 					return;
553 				}
554 				manifest.load(is);
555 			} catch (IOException e) {
556 				Message.error("Unable to read MANIFEST", e);
557 			} finally {
558 				IOUtils.closeQuietly(is);
559 			}
560 
561 			Launcher.setSuiteName(jad.getSuiteName());
562 
563 			for (Enumeration e = jad.getMidletEntries().elements(); e.hasMoreElements();) {
564 				JadMidletEntry jadEntry = (JadMidletEntry) e.nextElement();
565 				Class midletClass = midletClassLoader.loadClass(jadEntry.getClassName());
566 				Launcher.addMIDletEntry(new MIDletEntry(jadEntry.getName(), midletClass));
567 			}
568 			startLauncher(MIDletBridge.getMIDletContext());
569 			setStatusBar("");
570 		} finally {
571 			setResponseInterface(true);
572 		}
573 	}
574 
575 	public Device getDevice() {
576 		return DeviceFactory.getDevice();
577 	}
578 
579 	public void setDevice(Device device) {
580 		MIDletSystemProperties.setDevice(device);
581 		DeviceFactory.setDevice(device);
582 	}
583 
584 	private static Common getInstance() {
585 		return instance;
586 	}
587 
588 	public static void setStatusBar(String text) {
589 		if (statusBarListener != null) {
590 			statusBarListener.statusBarChanged(text);
591 		}
592 	}
593 
594 	private void setResponseInterface(boolean state) {
595 		if (responseInterfaceListener != null) {
596 			responseInterfaceListener.stateChanged(state);
597 		}
598 	}
599 
600 	private void registerImplementation(String implClassName, Map properties, boolean notFoundError) {
601 		final String errorText = "Implementation initialization";
602 		try {
603 			Class implClass = getExtensionsClassLoader().loadClass(implClassName);
604 			if (ImplementationInitialization.class.isAssignableFrom(implClass)) {
605 				Object inst = implClass.newInstance();
606 				Map parameters = new HashMap();
607 				parameters.put(ImplementationInitialization.PARAM_EMULATOR_ID, Config.getEmulatorID());
608 				if (properties != null) {
609 					parameters.putAll(properties);
610 				} else {
611 					Map extensions = Config.getExtensions();
612 					Map prop = (Map)extensions.get(implClassName);
613 					if (prop != null) {
614 					    parameters.putAll(prop);
615 				    }
616 				}
617 				((ImplementationInitialization) inst).registerImplementation(parameters);
618 				Logger.debug("implementation registered", implClassName);
619 				extensions.add(inst);
620 			} else {
621 				Logger.debug("initialize implementation", implClassName);
622 				boolean isStatic = true;
623 				try {
624 					// Create and object or call static initializer instance();
625 					Constructor c = implClass.getConstructor(null);
626 					if (Modifier.isPublic(c.getModifiers())) {
627 						isStatic = false;
628 						implClass.newInstance();
629 					}
630 				} catch (NoSuchMethodException e) {
631 				}
632 
633 				if (isStatic) {
634 					try {
635 						Method getinst = implClass.getMethod("instance", null);
636 						if (Modifier.isStatic(getinst.getModifiers())) {
637 							getinst.invoke(implClass, null);
638 						} else {
639 							Logger.debug("No known way to initialize implementation class");
640 						}
641 					} catch (NoSuchMethodException e) {
642 						Logger.debug("No known way to initialize implementation class");
643 					} catch (InvocationTargetException e) {
644 						Logger.debug("Unable to initialize Implementation", e.getCause());
645 					}
646 				}
647 			}
648 		} catch (ClassNotFoundException e) {
649 			if (notFoundError) {
650 				Logger.error(errorText, e);
651 			} else {
652 				Logger.warn(errorText + " " + e);
653 			}
654 		} catch (InstantiationException e) {
655 			Logger.error(errorText, e);
656 		} catch (IllegalAccessException e) {
657 			Logger.error(errorText, e);
658 		}
659 	}
660 
661 	public void loadImplementationsFromConfig() {
662 		Map extensions = Config.getExtensions();
663 		for (Iterator iterator = extensions.entrySet().iterator(); iterator.hasNext();) {
664 			Map.Entry entry = (Map.Entry) iterator.next();
665 			registerImplementation((String) entry.getKey(), (Map) entry.getValue(), false);
666 		}
667 	}
668 
669 	public void notifyImplementationMIDletStart() {
670 		for (Iterator iterator = extensions.iterator(); iterator.hasNext();) {
671 			ImplementationInitialization impl = (ImplementationInitialization) iterator.next();
672 			impl.notifyMIDletStart();
673 		}
674 	}
675 
676 	public void notifyImplementationMIDletDestroyed() {
677 		for (Iterator iterator = extensions.iterator(); iterator.hasNext();) {
678 			ImplementationInitialization impl = (ImplementationInitialization) iterator.next();
679 			impl.notifyMIDletDestroyed();
680 		}
681 	}
682 
683 	public void initParams(List params, DeviceEntry defaultDevice, Class defaultDeviceClass) {
684 		MIDletClassLoaderConfig clConfig = new MIDletClassLoaderConfig();
685 		Class deviceClass = null;
686 		String deviceDescriptorLocation = null;
687 		RecordStoreManager paramRecordStoreManager = null;
688 
689 		Iterator argsIterator = params.iterator();
690 
691 		try {
692 			while (argsIterator.hasNext()) {
693 				String arg = (String) argsIterator.next();
694 				argsIterator.remove();
695 
696 				if ((arg.equals("--help")) || (arg.equals("-help"))) {
697 					System.out.println(usage());
698 					System.exit(0);
699 				} else if (arg.equals("--id")) {
700 					Config.setEmulatorID((String) argsIterator.next());
701 					argsIterator.remove();
702 				} else if ((arg.equals("--appclasspath")) || (arg.equals("-appclasspath")) || (arg.equals("-appcp"))) {
703 					if (clConfig == null) {
704 						throw new ConfigurationException("Wrong command line argument order");
705 					}
706 					clConfig.addAppClassPath((String) argsIterator.next());
707 					argsIterator.remove();
708 				} else if (arg.equals("--appclass")) {
709 					if (clConfig == null) {
710 						throw new ConfigurationException("Wrong command line argument order");
711 					}
712 					clConfig.addAppClass((String) argsIterator.next());
713 					argsIterator.remove();
714 				} else if (arg.startsWith("-Xautotest:")) {
715 					autoTests = true;
716 					jadURL = arg.substring("-Xautotest:".length());
717 				} else if (arg.equals("-Xautotest")) {
718 					autoTests = true;
719 				} else if (arg.equals("--propertiesjad")) {
720 					File file = new File((String) argsIterator.next());
721 					argsIterator.remove();
722 					propertiesJad = file.exists() ? IOUtils.getCanonicalFileURL(file) : arg;
723 				} else if (arg.equals("--appclassloader")) {
724 					if (clConfig == null) {
725 						Message.error("Error", "Wrong command line argument order");
726 						break;
727 					}
728 					clConfig.setDelegationType((String) argsIterator.next());
729 					argsIterator.remove();
730 				} else if (arg.equals("--usesystemclassloader")) {
731 					useSystemClassLoader = true;
732 					clConfig.setDelegationType("system");
733 				} else if (arg.equals("-d") || arg.equals("--device")) {
734 					if (argsIterator.hasNext()) {
735 						String tmpDevice = (String) argsIterator.next();
736 						argsIterator.remove();
737 						if (!tmpDevice.toLowerCase().endsWith(".xml")) {
738 							try {
739 								deviceClass = Class.forName(tmpDevice);
740 							} catch (ClassNotFoundException ex) {
741 							}
742 						}
743 						if (deviceClass == null) {
744 							deviceDescriptorLocation = tmpDevice;
745 						}
746 					}
747 				} else if (arg.equals("--rms")) {
748 					if (argsIterator.hasNext()) {
749 						String tmpRms = (String) argsIterator.next();
750 						argsIterator.remove();
751 						if (tmpRms.equals("file")) {
752 							paramRecordStoreManager = new FileRecordStoreManager();
753 						} else if (tmpRms.equals("memory")) {
754 							paramRecordStoreManager = new MemoryRecordStoreManager();
755 						}
756 					}
757 				} else if ((arg.equals("--classpath")) || (arg.equals("-classpath")) || (arg.equals("-cp"))) {
758 					getExtensionsClassLoader().addClasspath((String) argsIterator.next());
759 					argsIterator.remove();
760 				} else if (arg.equals("--impl")) {
761 					registerImplementation((String) argsIterator.next(), null, true);
762 					argsIterator.remove();
763 				} else {
764 					midletClassOrJad = arg;
765 				}
766 			}
767 		} catch (ConfigurationException e) {
768 			Message.error("Error", e.getMessage(), e);
769 			return;
770 		}
771 
772 		mIDletClassLoaderConfig = clConfig;
773 
774 		// TODO registerImplementations by reading jar files in classpath.
775 
776 		ClassLoader classLoader = getExtensionsClassLoader();
777 		if (deviceDescriptorLocation != null) {
778 			try {
779 				setDevice(DeviceImpl.create(emulatorContext, classLoader, deviceDescriptorLocation, defaultDeviceClass));
780 			} catch (IOException ex) {
781 				Logger.error(ex);
782 			}
783 		}
784 		if (DeviceFactory.getDevice() == null) {
785 			try {
786 				if (deviceClass == null) {
787 					if (defaultDevice.getFileName() != null) {
788 						URL[] urls = new URL[1];
789 						urls[0] = new File(Config.getConfigPath(), defaultDevice.getFileName()).toURI().toURL();
790 						classLoader = createExtensionsClassLoader(urls);
791 					}
792 					setDevice(DeviceImpl.create(emulatorContext, classLoader, defaultDevice.getDescriptorLocation(),
793 							defaultDeviceClass));
794 				} else {
795 					DeviceImpl device = (DeviceImpl) deviceClass.newInstance();
796 					device.init(emulatorContext);
797 					setDevice(device);
798 				}
799 			} catch (InstantiationException ex) {
800 				Logger.error(ex);
801 			} catch (IllegalAccessException ex) {
802 				Logger.error(ex);
803 			} catch (IOException ex) {
804 				Logger.error(ex);
805 			}
806 		}
807 
808 		if (getRecordStoreManager() == null) {
809 			if (paramRecordStoreManager == null) {
810 				String className = Config.getRecordStoreManagerClassName();
811 				if (className != null) {
812 					try {
813 						Class clazz = Class.forName(className);
814 						setRecordStoreManager((RecordStoreManager) clazz.newInstance());
815 					} catch (ClassNotFoundException ex) {
816 						Logger.error(ex);
817 					} catch (InstantiationException ex) {
818 						Logger.error(ex);
819 					} catch (IllegalAccessException ex) {
820 						Logger.error(ex);
821 					}
822 				}
823 				if (getRecordStoreManager() == null) {
824 					setRecordStoreManager(new FileRecordStoreManager());
825 				}
826 			} else {
827 				setRecordStoreManager(paramRecordStoreManager);
828 			}
829 		}
830 	}
831 
832 	private static ExtensionsClassLoader getExtensionsClassLoader() {
833 		if (instance.extensionsClassLoader == null) {
834 			instance.extensionsClassLoader = new ExtensionsClassLoader(new URL[] {}, instance.getClass()
835 					.getClassLoader());
836 		}
837 		return instance.extensionsClassLoader;
838 	}
839 
840 	private MIDletClassLoader createMIDletClassLoader() {
841 		MIDletClassLoader mcl = new MIDletClassLoader(getExtensionsClassLoader());
842 		if (!Serializable.class.isAssignableFrom(Injected.class)) {
843 			Logger
844 					.error("classpath configuration error, Wrong Injected class detected. microemu-injected module should be after microemu-javase in eclipse");
845 		}
846 		if (mIDletClassLoaderConfig != null) {
847 			try {
848 				mcl.configure(mIDletClassLoaderConfig);
849 			} catch (MalformedURLException e) {
850 				Message.error("Error", "Unable to find MIDlet classes, " + Message.getCauseMessage(e), e);
851 			}
852 		}
853 		mcl.disableClassPreporcessing(Injected.class);
854 		mcl.disableClassPreporcessing(MIDletThread.class);
855 		mcl.disableClassPreporcessing(MIDletTimer.class);
856 		MIDletResourceLoader.classLoader = mcl;
857 		return mcl;
858 	}
859 
860 	public static ClassLoader createExtensionsClassLoader(final URL[] urls) {
861 		return new ExtensionsClassLoader(urls, getExtensionsClassLoader());
862 	}
863 
864 	private static JadProperties loadJadProperties(String urlString) throws IOException {
865 		JadProperties properties = new JadProperties();
866 
867 		URL url = new URL(urlString);
868 		if (url.getUserInfo() == null) {
869 			properties.load(url.openStream());
870 		} else {
871 			URLConnection cn = url.openConnection();
872 			String userInfo = new String(Base64Coder.encode(url.getUserInfo().getBytes("UTF-8")));
873 			cn.setRequestProperty("Authorization", "Basic " + userInfo);
874 			properties.load(cn.getInputStream());
875 		}
876 
877 		return properties;
878 	}
879 
880 	public void initMIDlet(boolean startMidlet) {
881 		Class midletClass = null;
882 
883 		if (midletClassOrJad != null && midletClassOrJad.endsWith(".jad")) {
884 			try {
885 				File file = new File(midletClassOrJad);
886 				String url = file.exists() ? IOUtils.getCanonicalFileURL(file) : midletClassOrJad;
887 				openJadUrl(url);
888 			} catch (IOException exception) {
889 				Logger.error("Cannot load " + midletClassOrJad + " URL", exception);
890 			}
891 		} else if (midletClassOrJad != null) {
892 			useSystemClassLoader = mIDletClassLoaderConfig.isClassLoaderDisabled();
893 			if (!useSystemClassLoader) {
894 				MIDletClassLoader classLoader = createMIDletClassLoader();
895 				try {
896 					classLoader.addClassURL(midletClassOrJad);
897 					midletClass = classLoader.loadClass(midletClassOrJad);
898 				} catch (MalformedURLException e) {
899 					Message.error("Error", "Unable to find MIDlet class, " + Message.getCauseMessage(e), e);
900 					return;
901 				} catch (ClassNotFoundException e) {
902 					Message.error("Error", "Unable to find MIDlet class, " + Message.getCauseMessage(e), e);
903 					return;
904 				}
905 			} else {
906 				try {
907 					midletClass = instance.getClass().getClassLoader().loadClass(midletClassOrJad);
908 				} catch (ClassNotFoundException e) {
909 					Message.error("Error", "Unable to find MIDlet class, " + Message.getCauseMessage(e), e);
910 					return;
911 				}
912 			}
913 		}
914 
915 		if (autoTests) {
916 			if (jadURL != null) {
917 				runAutoTests(jadURL, true);
918 			}
919 		} else {
920 
921 			if (midletClass != null && propertiesJad != null) {
922 				try {
923 					jad = loadJadProperties(propertiesJad);
924 				} catch (IOException e) {
925 					Logger.error("Cannot load " + propertiesJad + " URL", e);
926 				}
927 			}
928 
929 			boolean started = false;
930 
931 			if (midletClass == null) {
932 				MIDletEntry entry = launcher.getSelectedMidletEntry();
933 				if (startMidlet && entry != null) {
934 					started = (null != startMidlet(entry.getMIDletClass(), MIDletBridge.getMIDletAccess()));
935 				}
936 			} else {
937 				started = (null != startMidlet(midletClass, MIDletBridge.getMIDletAccess()));
938 			}
939 			if (!started) {
940 				startLauncher(MIDletBridge.getMIDletContext());
941 			}
942 		}
943 
944 	}
945 
946 	public static String usage() {
947 		return "[(-d | --device) ({device descriptor} | {device class name}) ] \n" + "[--rms (file | memory)] \n"
948 				+ "[--id EmulatorID ] \n" + "[--impl {JSR implementation class name}]\n"
949 				+ "[(--classpath|-cp) <JSR CLASSPATH>]\n" + "[(--appclasspath|--appcp) <MIDlet CLASSPATH>]\n"
950 				+ "[--appclass <library class name>]\n" + "[--appclassloader strict|delegating|system] \n"
951 				+ "[-Xautotest:<JAD file url>\n"
952 				+ "(({MIDlet class name} [--propertiesjad {jad file location}]) | {jad file location})";
953 	}
954 
955 }