View Javadoc

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