001: /*
002: * @(#)XletManager.java 1.21 06/10/10
003: *
004: * Copyright 1990-2006 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: *
026: */
027:
028: package com.sun.xlet;
029:
030: import javax.microedition.xlet.*;
031: import javax.microedition.xlet.ixc.*;
032: import java.awt.BorderLayout;
033: import java.awt.Container;
034: import java.awt.FlowLayout;
035: import java.awt.Frame;
036: import java.awt.event.WindowAdapter;
037: import java.awt.event.WindowEvent;
038: import java.io.IOException;
039: import java.io.File;
040: import java.net.URL;
041: import java.net.MalformedURLException;
042: import java.lang.reflect.Constructor;
043: import java.rmi.AccessException;
044: import java.security.AccessController;
045: import java.security.PrivilegedAction;
046: import java.security.PrivilegedExceptionAction;
047: import java.security.PrivilegedActionException;
048: import java.util.Hashtable;
049: import java.util.Vector;
050:
051: import sun.awt.SunToolkit;
052: import sun.awt.AppContext;
053:
054: public class XletManager implements XletLifecycleHandler {
055:
056: // the root frame shared by all xlets
057: static ToplevelFrame theFrame;
058: // list of active <XletContext,XletManager> pairs
059: static Hashtable activeContexts = new Hashtable();
060:
061: // the xlet class this XletManager instance handles
062: private Class xletClass;
063: // this XletManager's state queue
064: private XletStateQueue xletQueue;
065:
066: // The instance of the xletClass above.
067: // Both Class and the instance are kept
068: // as xlet instantiation calls into the user code,
069: // thus should be handled in the XletStateQueue thread,
070: // and this thread only knows about local data right now.
071: private Xlet xlet;
072:
073: // this Xlet's XletContext
074: private XletContextImpl context;
075: // Xlet's current state
076: private XletState currentState = XletState.UNLOADED;
077:
078: // this Xlet's threadGroup and AppContext
079: ThreadGroup threadGroup;
080: AppContext appContext;
081:
082: private Object stateGuard = new Object();
083:
084: protected XletManager(XletClassLoader xletClassLoader,
085: String mainClass, String[] args)
086: throws ClassNotFoundException {
087:
088: xletClass = xletClassLoader.loadClass(mainClass);
089:
090: if (!(Xlet.class).isAssignableFrom(xletClass)) {
091: throw new ClassCastException(
092: "Attempt to run a non-Xlet class: " + mainClass);
093: }
094:
095: context = new XletContextImpl(mainClass, args, this );
096: activeContexts.put(context, this );
097:
098: final ThreadGroup currentTG = Thread.currentThread()
099: .getThreadGroup();
100: final int numOfXlets = activeContexts.size();
101: final String className = mainClass;
102: final ClassLoader finalLoader = xletClassLoader;
103:
104: threadGroup = (ThreadGroup) AccessController
105: .doPrivileged(new PrivilegedAction() {
106: public Object run() {
107: return new ThreadGroup(currentTG,
108: "Xlet Thread Group " + numOfXlets);
109: }
110: });
111:
112: // Create an Xlet state change dispatcher queue
113: final XletManager this XletManager = this ;
114: xletQueue = (XletStateQueue) AccessController
115: .doPrivileged(new PrivilegedAction() {
116: public Object run() {
117: Thread t = new Thread(threadGroup,
118: new Runnable() {
119: public void run() {
120: manageLifecycle();
121: }
122: }, "Xlet lifecycle for " + className);
123: t.setContextClassLoader(finalLoader);
124: t.start();
125: return new XletStateQueue(this XletManager);
126: }
127: });
128:
129: // Let the ClassLoader have the reference to this XletManager
130: xletClassLoader.setXletManager(this );
131: // Register an AppContext with this ThreadGroup.
132: // Note: Create AppContext after registering XletManager to XletClassLoader,
133: // as AppContext creation leads to EventQueue dispatcher thread
134: // creation. ThreadGroup for the new Thread is provided by
135: // the SecurityManager if installed, and XletSecurity needs
136: // XletClassLoader to have a reference of xletManager to get TG.
137: createAppContext(threadGroup);
138:
139: if (theFrame == null) { // Grab the Frame before xlet gets it's chance.
140: theFrame = new ToplevelFrame("Xlet Frame", this );
141: }
142:
143: // Install a SecurityManager on this xlet if nothing is set.
144: // Note: If this is launching a second xlet, all code above is already
145: // executing with XletSecurity installed.
146: installSecurityManager();
147:
148: // Finally, request the xlet to be instantiated.
149: xletQueue.push(XletState.LOADED);
150: }
151:
152: /**
153: * Call SunToolkit to create a dedicated EventQueue
154: * for the caller Xlet. Guarantees that AppContext
155: * is created by the the the method returns.
156: *
157: * @param ThreadGroup which the xlet's lifecycle thread
158: * is running within
159: */
160: final Object syncObject = new Object();
161:
162: private void createAppContext(ThreadGroup tg) {
163: final ThreadGroup finalTG = tg;
164: final ClassLoader finalLoader = getClassLoader();
165: AccessController.doPrivileged(new PrivilegedAction() {
166: public Object run() {
167: Thread t2 = new Thread(finalTG, new Runnable() {
168: public void run() {
169: synchronized (syncObject) {
170: appContext = SunToolkit
171: .createNewAppContext();
172: syncObject.notifyAll();
173: }
174: }
175: }, "AppContext creation thread");
176: // Need to set ContextClassLoader, as this thread
177: // is later used as the EventQueue's dispatching thread
178: t2.setContextClassLoader(finalLoader);
179: synchronized (syncObject) {
180: t2.start();
181: try {
182: syncObject.wait();
183: } catch (InterruptedException e) {
184: }
185: }
186: return null;
187: }
188: });
189: return;
190: }
191:
192: /*
193: * Install XletSecurity if no SecurityManager is set.
194: */
195: private void installSecurityManager() {
196: if (System.getSecurityManager() == null) {
197: System.setSecurityManager(new XletSecurity(appContext));
198: }
199: }
200:
201: public Container getContainer() {
202: return theFrame.getXletContainer();
203: }
204:
205: public void postInitXlet() {
206: xletQueue.push(DesiredXletState.INITIALIZE);
207: }
208:
209: public void postStartXlet() {
210: xletQueue.push(XletState.ACTIVE);
211: }
212:
213: public void postPauseXlet() {
214: xletQueue.push(XletState.PAUSED);
215: }
216:
217: public void postDestroyXlet(boolean unconditional) {
218: if (!unconditional) {
219: xletQueue.push(DesiredXletState.CONDITIONAL_DESTROY);
220: } else {
221: xletQueue.push(XletState.DESTROYED);
222: }
223: }
224:
225: public void setState(XletState state) {
226:
227: if (currentState == XletState.DESTROYED
228: || state == currentState)
229: return;
230:
231: synchronized (stateGuard) {
232: currentState = state;
233: stateGuard.notifyAll();
234: }
235: }
236:
237: public int getState() {
238: XletState state = getXletState();
239: if (state == XletState.LOADED) {
240: return LOADED;
241: } else if (state == XletState.PAUSED) {
242: return PAUSED;
243: } else if (state == XletState.ACTIVE) {
244: return ACTIVE;
245: } else if (state == XletState.DESTROYED) {
246: return DESTROYED;
247: } else {
248: return UNKNOWN;
249: }
250: }
251:
252: public XletState getXletState() {
253: synchronized (stateGuard) {
254: return currentState;
255: }
256: }
257:
258: // typically called from XletStateQueue
259: public void handleRequest(XletState desiredState) {
260: XletState targetState = currentState;
261: synchronized (stateGuard) {
262: try {
263: if (desiredState == XletState.LOADED) {
264: if (currentState != XletState.UNLOADED)
265: return;
266: targetState = XletState.LOADED;
267: Constructor m = xletClass
268: .getConstructor(new Class[0]);
269: xlet = (Xlet) m.newInstance(new Object[0]);
270: } else if (desiredState == DesiredXletState.INITIALIZE) {
271: if (currentState != XletState.LOADED)
272: return;
273: targetState = XletState.PAUSED;
274: try {
275: xlet.initXlet(context);
276: } catch (XletStateChangeException xsce) {
277: targetState = XletState.DESTROYED;
278: xlet.destroyXlet(true);
279: }
280: } else if (desiredState == XletState.ACTIVE) {
281: if (currentState != XletState.PAUSED)
282: return;
283: targetState = XletState.ACTIVE;
284: try {
285: xlet.startXlet();
286: } catch (XletStateChangeException xsce) {
287: targetState = currentState;
288: }
289: } else if (desiredState == XletState.PAUSED) {
290: if (currentState != XletState.ACTIVE)
291: return;
292: targetState = XletState.PAUSED;
293: xlet.pauseXlet();
294: } else if (desiredState == DesiredXletState.CONDITIONAL_DESTROY) {
295: if (currentState == XletState.DESTROYED)
296: return;
297: targetState = XletState.DESTROYED;
298: try {
299: xlet.destroyXlet(false);
300: } catch (XletStateChangeException xsce) {
301: targetState = currentState;
302: }
303: } else if (desiredState == XletState.DESTROYED) {
304: targetState = XletState.DESTROYED;
305: if (currentState == XletState.DESTROYED)
306: return;
307: try {
308: xlet.destroyXlet(true);
309: } catch (XletStateChangeException xsce) {
310: }
311: }
312:
313: } catch (Exception e) {
314: // Some unknown exception from ths user code.
315: // Destroy it...
316: //System.err.println("Exception during execution: " + e);
317: e.printStackTrace();
318: if (targetState != XletState.DESTROYED) {
319: handleRequest(XletState.DESTROYED);
320: }
321: }
322:
323: // State change done - update this xlet's State.
324: setState(targetState);
325: }
326: }
327:
328: // really private, but invoked from inner class.
329: void manageLifecycle() {
330: try {
331: while (currentState != XletState.DESTROYED) {
332: synchronized (stateGuard) {
333: stateGuard.wait();
334: }
335: }
336: // xlet has destroyed, do appropreate cleanup
337: cleanUp();
338: } catch (Throwable t) {
339: System.out
340: .println("Xlet had unexpected exception in lifecycle thread.");
341: t.printStackTrace();
342: setState(XletState.DESTROYED);
343: }
344:
345: // 4739427. If no other xlet running, then shut down.
346: synchronized (activeContexts) {
347: if (activeContexts.isEmpty()) {
348: exit();
349: }
350: }
351: }
352:
353: private void cleanUp() {
354: // Clean up IxcRegistry
355: IxcRegistry.getRegistry(context).unbindAll();
356:
357: // remove the Root Container from the parent frame
358: if (context.container != null) {
359: theFrame.remove(context.container);
360: context.container = null;
361: theFrame.repaint();
362: }
363: // clear the State Queue
364: xletQueue.destroy();
365: // remove the xlet context from the hash
366: synchronized (activeContexts) {
367: activeContexts.remove(context);
368: activeContexts.notifyAll();
369: }
370: }
371:
372: static void exit() {
373: synchronized (activeContexts) {
374: XletManager[] managers = (XletManager[]) activeContexts
375: .values().toArray(
376: new XletManager[activeContexts.size()]);
377: for (int i = 0; i < managers.length; i++) {
378: try {
379: // request for a DESTROY state to be picked up at the XletStateQueue
380: managers[i].postDestroyXlet(false);
381: activeContexts.wait(1000); // wait for 1 sec for each..
382: if (activeContexts.containsValue(managers[i])) {
383: // try to interrupt anything that the XletStateQueue is doing
384: managers[i].xletQueue.queueThread.interrupt();
385: // force the state to be DESTROYED
386: managers[i].setState(XletState.DESTROYED);
387: activeContexts.wait(500); // wait for .5 sec more...
388: }
389: } catch (InterruptedException ie) {
390: managers[i].setState(XletState.DESTROYED);
391: }
392: }
393: }
394: System.exit(0);
395: }
396:
397: /**
398: * Look up the XletContext-Xlet hashtable (activeContexts) and
399: * return the classloader for the xlet based on the XletContext passed in.
400: * For XletContext.getClassLoader() impl.
401: **/
402: static ClassLoader getXletClassLoader(XletContext context) {
403: XletManager this Manager = (XletManager) activeContexts
404: .get(context);
405: if (this Manager != null) {
406: return this Manager.getClassLoader();
407: }
408: return null; // no matching xlet found.
409: }
410:
411: // Returns the ClassLoader for this xlet
412: public ClassLoader getClassLoader() {
413: return xletClass.getClassLoader();
414: }
415:
416: /**
417: * Entry point for XletManager.
418: * Instanciates the xlet class in the current dir.
419: * Returns the handler to control this xlet's lifecycle.
420: * @param mainClass the xlet class name.
421: **/
422: public static XletLifecycleHandler createXlet(String mainClass)
423: throws IOException {
424: return createXlet(mainClass, new String[] { "." },
425: new String[] {});
426: }
427:
428: /**
429: * Entry point for XletManager.
430: * Returns the handler to control this xlet's lifecycle.
431: * @param mainClass the xlet class name.
432: * @param path array of url-formed strings or file paths to find
433: * the xlet class.
434: * @param args the runtime arguments given to the xlet
435: * (accessed by XletContext.getXletProperty(ARGS))
436: **/
437: public static XletLifecycleHandler createXlet(String mainClass,
438: String[] paths, String[] args) throws IOException {
439: Vector v = new Vector();
440: int index = 0;
441: for (int i = 0; i < paths.length; i++) {
442: try {
443: v.add(new URL(paths[i]));
444: } catch (MalformedURLException mue) {
445: final File file = new File(paths[i]);
446: try {
447: URL path = (URL) AccessController
448: .doPrivileged(new PrivilegedExceptionAction() {
449: public Object run() throws IOException {
450: if (!file.exists()) {
451: System.out
452: .println("Warning: \""
453: + file
454: .getPath()
455: + "\" not found");
456: return null;
457: }
458: // CR 6252530.
459: return file.toURI().toURL();
460: }
461: });
462:
463: if (path != null)
464: v.add(path);
465:
466: } catch (PrivilegedActionException e) {
467: e.getException().printStackTrace();
468: }
469: }
470: }
471: final URL[] array = (URL[]) v.toArray(new URL[v.size()]);
472: XletClassLoader cl = (XletClassLoader) AccessController
473: .doPrivileged(new PrivilegedAction() {
474: public Object run() {
475: return new XletClassLoader(array);
476: }
477: });
478:
479: try {
480: return (XletLifecycleHandler) new XletManager(cl,
481: mainClass, args);
482: } catch (ClassNotFoundException e) {
483: e.printStackTrace();
484: throw new IOException("Cannot find class " + mainClass);
485: }
486: }
487:
488: public static int getState(XletContext context) {
489: XletManager this Manager = (XletManager) activeContexts
490: .get(context);
491: if (this Manager != null)
492: return this Manager.getState();
493: return 0;
494: }
495: }
|