001: /*
002: * @(#)PXletManager.java 1.26 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 sun.mtask.xlet;
029:
030: // Standard classes used by xlets.
031: import javax.microedition.xlet.*;
032:
033: // For Inter-Xlet Communication (IXC).
034: import javax.microedition.xlet.ixc.*;
035:
036: // Provides graphic representations and event support for xlets.
037: import java.awt.Container;
038: import java.awt.Frame;
039: import java.awt.Insets;
040: import java.awt.event.ActionEvent;
041: import java.awt.event.ActionListener;
042: import java.awt.event.ComponentAdapter;
043: import java.awt.event.ComponentEvent;
044: import java.awt.event.WindowAdapter;
045: import java.awt.event.WindowEvent;
046:
047: // Utility classes for saving information about xlets.
048: import java.util.Hashtable;
049: import java.util.Vector;
050:
051: //For loading xlets.
052: import java.io.IOException;
053: import java.io.File;
054: import java.net.URL;
055: import java.net.MalformedURLException;
056: import java.lang.reflect.Constructor;
057: import java.rmi.AccessException;
058: import java.security.AccessController;
059: import java.security.PrivilegedAction;
060: import java.security.PrivilegedExceptionAction;
061: import java.security.PrivilegedActionException;
062: import sun.misc.CDCAppClassLoader;
063:
064: import com.sun.xlet.XletLifecycleHandler;
065:
066: /*
067: * Xlet Manager implementation.
068: *
069: * The main purpose of this class is to initiate the xlet’s state change based
070: * on the request coming in from the PXletStateQueue and to keep track of
071: * the xlet’s current state. It also loads and destroys xlets.
072: *
073: * Static data is shared across all xlets. Every instance of an
074: * XletManager manages one xlet. The XletManager runs a thread for
075: * every xlet, which waits until the xlet is destroyed, then performs
076: * the cleanup. The actual state change can be done either by
077: * posting the request through XletLifecycleHandler (which inserts the
078: * request into XletEventQueue), or through XletContext (which notifies the
079: * XletManager that the xlet already changed its state).
080: */
081:
082: public class PXletManager implements XletLifecycleHandler {
083: private static final int DEFAULT_XLET_WIDTH = 300;
084: private static final int DEFAULT_XLET_HEIGHT = 300;
085:
086: // The root frame of this xlet
087: private XletFrame xletFrame;
088:
089: // The XletContext which this instance of PXletManager is managing.
090: private PXletContextImpl context;
091:
092: // The xlet class instance.
093: private Class xletClass;
094:
095: // The xlet itself.
096: private Xlet xlet;
097:
098: // This xlet’s state queue. The state change request is usually posted
099: // to the XletEventQueue.
100: private PXletStateQueue xletQueue;
101:
102: // This xlet’s thread group (Package private).
103: ThreadGroup threadGroup;
104:
105: // To synchronize the state change.
106: // Synchronize the existing current state and desired state with a new
107: // current state or desired state during a state transition.
108: private Object stateGuard = new Object();
109:
110: // The current state of the xlet that this instance of XletManager is
111: // managing. The XletManager is always trying to move the Xlet from the
112: // current state to the desired state. The first state transition would be
113: // from unloaded to loaded.
114: private XletState currentState = XletState.UNLOADED;
115:
116: private static boolean verbose = (System
117: .getProperty("pxletmanager.verbose") != null)
118: && (System.getProperty("pxletmanager.verbose")
119: .toLowerCase().equals("true"));
120:
121: // Load the xlet class instance with a ClassLoader.
122: // mainClass is the main class of the Xlet.
123: // args contains the command-line arguments supplied with the arg option.
124: // They will be stored in the XletContext.
125: protected PXletManager(ClassLoader xletClassLoader, String laf,
126: String lafTheme, String mainClass, String[] args)
127: throws ClassNotFoundException {
128:
129: xletClass = xletClassLoader.loadClass(mainClass);
130:
131: if (xletClass == null
132: || (!(Xlet.class).isAssignableFrom(xletClass))) {
133: throw new IllegalArgumentException(
134: "Attempt to run a non-Xlet class: " + mainClass);
135: }
136:
137: // Create the XletContext for this xlet.
138: context = new PXletContextImpl(mainClass, args, this );
139: this .xletClass = xletClass;
140:
141: // Create a root Frame for all xlets. Every time a new xlet is
142: // added, it gets its own container inside this Frame. The XletManager
143: // sets a default size and location, adds a menu bar and window
144: // listener, and leaves the Frame invisible.
145: xletFrame = new XletFrame("Xlet Frame: " + mainClass, this ,
146: laf, lafTheme);
147:
148: // Create a thread group that all the threads used by this xlet
149: // would be a part of. This is necessary for providing a separate
150: // EventQueue per xlet.
151: threadGroup = new ThreadGroup(Thread.currentThread()
152: .getThreadGroup(), "Xlet Thread Group ");
153:
154: // Create a state queue that holds this xlet’s state change requests.
155: xletQueue = new PXletStateQueue(this );
156:
157: // Now that the setup is complete, enter a request to move this xlet
158: // from UNLOADED to LOADED.
159: xletQueue.push(XletState.LOADED);
160: }
161:
162: // Return a container for this xlet.
163: // This will be called at most once, by PXletContextImpl.getContainer()
164: public Container getContainer() {
165: return xletFrame.getContainer();
166: }
167:
168: //private Listener listener = null;
169:
170: // The following four methods are implementations of XletLifecycleHandler
171: // methods. They allow a third party to request an xlet state change.
172: // They request a state change through the xlet state queue (which holds
173: // an xlet’s state change requests).
174: public void postInitXlet() {
175: xletQueue.push(DesiredXletState.INITIALIZE);
176: }
177:
178: public void postStartXlet() {
179: xletQueue.push(XletState.ACTIVE);
180: }
181:
182: public void postPauseXlet() {
183: xletQueue.push(XletState.PAUSED);
184: }
185:
186: //
187: // The xletDestroy() from the appmanager is best effort.
188: // The appmanager inspects state. If it discovers that this jvm
189: // has not exited, it can use Client.kill() to forcibly get rid of it.
190: //
191: public void postDestroyXlet(boolean unconditional) {
192: if (!unconditional) {
193: xletQueue.push(DesiredXletState.CONDITIONAL_DESTROY);
194: } else {
195: xletQueue.push(XletState.DESTROYED);
196: }
197: }
198:
199: // Set the state of the xlet. If the current state is destroyed, don’t
200: // bother to set the state. If the target state is destroyed,
201: // exit this vm instance.
202: public void setState(XletState state) {
203: synchronized (stateGuard) {
204: if (currentState == XletState.DESTROYED)
205: return;
206: currentState = state;
207: stateGuard.notifyAll();
208: }
209: //
210: // If we are requesting a state change into DESTROYED, we want to
211: // go away. So don't linger, get rid of the enclosing JVM instance
212: //
213: if (state == XletState.DESTROYED) {
214: if (verbose) {
215: System.err
216: .println("@@PXletManager setting xlet state to DESTROYED");
217: }
218: System.exit(0);
219: }
220: }
221:
222: // Implementation of XletLifecycleHandler. Allows a third party to query
223: // the xlet state.
224: public int getState() {
225: XletState state = getXletState();
226: if (state == XletState.LOADED) {
227: return LOADED;
228: } else if (state == XletState.PAUSED) {
229: return PAUSED;
230: } else if (state == XletState.ACTIVE) {
231: return ACTIVE;
232: } else if (state == XletState.DESTROYED) {
233: return DESTROYED;
234: } else {
235: return UNKNOWN;
236: }
237: }
238:
239: // Used internally. Returns the current XletState as an instance of
240: // XletState rather than as an integer.
241: public XletState getXletState() {
242: return currentState;
243: }
244:
245: // Keeps track of whether the xlet state change request was fulfilled or not.
246: boolean requestCompleted = false;
247:
248: // Typically called from PXletStateQueue. Handles xlet’s state change
249: // request. Makes sure it only performs a permissible state change.
250: public void handleRequest(XletState desiredState) {
251: XletState targetState = currentState;
252: requestCompleted = false;
253: try {
254: synchronized (stateGuard) {
255: if (desiredState == XletState.LOADED) {
256: if (currentState != XletState.UNLOADED)
257: return;
258: targetState = XletState.LOADED;
259: Class[] types = new Class[0];
260: Constructor m = xletClass.getConstructor(types);
261: xlet = (Xlet) m.newInstance(new Object[0]);
262:
263: } else if (desiredState == DesiredXletState.INITIALIZE) {
264: if (currentState != XletState.LOADED)
265: return;
266: targetState = XletState.PAUSED;
267: try {
268: xlet.initXlet(context);
269:
270: } catch (XletStateChangeException xsce) {
271: targetState = XletState.DESTROYED;
272: //
273: // The xletDestroy() from the appmanager is best
274: // effort. The appmanager inspects state. If it
275: // discovers that this jvm has not exited, it can use
276: // Client.kill() to forcibly get rid of it.
277: //
278: try {
279: xlet.destroyXlet(true);
280: } catch (XletStateChangeException xsce2) {
281: // Xlet refused to go away
282: // This is unconditional though so ignore
283: // exception. The right thing will happen
284: // when setState(DESTROYED) is called.
285: }
286: }
287: } else if (desiredState == XletState.ACTIVE) {
288: if (currentState != XletState.PAUSED)
289: return;
290: targetState = XletState.ACTIVE;
291: try {
292: xlet.startXlet();
293: } catch (XletStateChangeException xsce) {
294: //
295: // The spec is not explicit here.
296: // If you can't activate the xlet it
297: // lingers in its paused state.
298: //
299: targetState = currentState;
300: }
301: } else if (desiredState == XletState.PAUSED) {
302: if (currentState != XletState.ACTIVE)
303: return;
304: targetState = XletState.PAUSED;
305: xlet.pauseXlet();
306: } else if (desiredState == DesiredXletState.CONDITIONAL_DESTROY) {
307: if (currentState == XletState.DESTROYED)
308: return;
309: targetState = XletState.DESTROYED;
310: try {
311: xlet.destroyXlet(false);
312: } catch (XletStateChangeException xsce) {
313: // Xlet refused to go away
314: if (verbose) {
315: System.err
316: .println("XLET REFUSED TO GO AWAY\n");
317: }
318: targetState = currentState;
319: }
320: } else if (desiredState == XletState.DESTROYED) {
321: targetState = XletState.DESTROYED;
322: if (currentState == XletState.DESTROYED)
323: return;
324: try {
325: xlet.destroyXlet(true);
326: } catch (XletStateChangeException xsce) {
327: // Xlet refused to go away
328: // This is unconditional though so ignore
329: // exception. The right thing will happen
330: // when setState(DESTROYED) is called.
331: }
332: }
333: setState(targetState);
334: }
335: } catch (Throwable e) {
336: if (verbose) {
337: if (verbose) {
338: System.err
339: .println("EXCEPTION DURING XLET STATE HANDLING: "
340: + e);
341: }
342: }
343: e.printStackTrace();
344: if (targetState == XletState.DESTROYED) {
345: setState(XletState.DESTROYED);
346: } else {
347: handleRequest(XletState.DESTROYED);
348: }
349: }
350: requestCompleted = true;
351: }
352:
353: // Creates an xlet. This static method causes a new instance of
354: // XletManager to be created, and to load the xlet.
355: public static XletLifecycleHandler createXlet(String mainClass,
356: String laf, String lafTheme) throws IOException {
357: return (XletLifecycleHandler) createXlet(mainClass, laf,
358: lafTheme, new String[] { "." }, new String[] {});
359: }
360:
361: public static PXletManager createXlet(String mainClass, String laf,
362: String lafTheme, String[] paths, String[] args)
363: throws IOException {
364:
365: Vector v = new Vector();
366: for (int i = 0; i < paths.length; i++) {
367: URL url = parseURL(paths[i]);
368: if (url != null) {
369: v.add(url);
370: }
371: }
372:
373: PXletClassLoader cl = new PXletClassLoader((URL[]) v
374: .toArray(new URL[0]), null);
375:
376: try {
377: return new PXletManager(cl, laf, lafTheme, mainClass, args);
378: } catch (ClassNotFoundException e) {
379: e.printStackTrace();
380: throw new IOException("Cannot find class " + mainClass);
381: }
382: }
383:
384: /**
385: * Following the relevant RFC, construct a valid URL based on the passed in
386: * string.
387: *
388: * @param url A string which represents either a relative or absolute URL,
389: * or a string that could be an absolute or relative path.
390: * @return A URL when the passed in string can be interpreted according
391: * to the RFC. <code>null</code> otherwise.
392: */
393: private static URL parseURL(String url) {
394: URL u = null;
395: try {
396: if (url.startsWith(File.separator)) {
397: // absolute path
398: u = new File(url).toURL();
399: } else if (url.indexOf(':') <= 1) {
400: // we were passed in a relative URL or an absolute URL on a
401: // win32 machine
402: u = new File(System.getProperty("user.dir"), url)
403: .getCanonicalFile().toURL();
404: } else {
405: if (url.startsWith("file:")
406: && url.replace(File.separatorChar, '/')
407: .indexOf('/') == -1) {
408: // We were passed in a relative "file" URL, like this:
409: // "file:index.html".
410: // Prepend current directory location.
411: String fname = url.substring("file:".length());
412: if (fname.length() > 0) {
413: u = new File(System.getProperty("user.dir"),
414: fname).toURL();
415: } else {
416: u = new URL(url);
417: }
418: } else {
419: u = new URL(url);
420: }
421: }
422: } catch (IOException e) {
423: if (verbose) {
424: System.err.println("error in parsing: " + url);
425: }
426: }
427: return u;
428: }
429: }
430:
431: class PXletClassLoader extends CDCAppClassLoader {
432: public PXletClassLoader(URL[] urls, ClassLoader parent) {
433: super(urls, parent);
434: }
435: }
|