/* * @(#)PXletManager.java 1.26 06/10/10 * * Copyright 1990-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 only, as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License version 2 for more details (a copy is * included at /legal/license.txt). * * You should have received a copy of the GNU General Public License * version 2 along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa * Clara, CA 95054 or visit www.sun.com if you need additional * information or have any questions. * */ package sun.mtask.xlet; // Standard classes used by xlets. import javax.microedition.xlet.*; // For Inter-Xlet Communication (IXC). import javax.microedition.xlet.ixc.*; // Provides graphic representations and event support for xlets. import java.awt.Container; import java.awt.Frame; import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; // Utility classes for saving information about xlets. import java.util.Hashtable; import java.util.Vector; //For loading xlets. import java.io.IOException; import java.io.File; import java.net.URL; import java.net.MalformedURLException; import java.lang.reflect.Constructor; import java.rmi.AccessException; import java.security.AccessController; import java.security.PrivilegedAction; import java.security.PrivilegedExceptionAction; import java.security.PrivilegedActionException; import sun.misc.CDCAppClassLoader; import com.sun.xlet.XletLifecycleHandler; /* * Xlet Manager implementation. * * The main purpose of this class is to initiate the xlet�s state change based * on the request coming in from the PXletStateQueue and to keep track of * the xlet�s current state. It also loads and destroys xlets. * * Static data is shared across all xlets. Every instance of an * XletManager manages one xlet. The XletManager runs a thread for * every xlet, which waits until the xlet is destroyed, then performs * the cleanup. The actual state change can be done either by * posting the request through XletLifecycleHandler (which inserts the * request into XletEventQueue), or through XletContext (which notifies the * XletManager that the xlet already changed its state). */ public class PXletManager implements XletLifecycleHandler { private static final int DEFAULT_XLET_WIDTH = 300; private static final int DEFAULT_XLET_HEIGHT = 300; // The root frame of this xlet private XletFrame xletFrame; // The XletContext which this instance of PXletManager is managing. private PXletContextImpl context; // The xlet class instance. private Class xletClass; // The xlet itself. private Xlet xlet; // This xlet�s state queue. The state change request is usually posted // to the XletEventQueue. private PXletStateQueue xletQueue; // This xlet�s thread group (Package private). ThreadGroup threadGroup; // To synchronize the state change. // Synchronize the existing current state and desired state with a new // current state or desired state during a state transition. private Object stateGuard = new Object(); // The current state of the xlet that this instance of XletManager is // managing. The XletManager is always trying to move the Xlet from the // current state to the desired state. The first state transition would be // from unloaded to loaded. private XletState currentState = XletState.UNLOADED; private static boolean verbose = (System.getProperty("pxletmanager.verbose") != null) && (System.getProperty("pxletmanager.verbose").toLowerCase().equals("true")); // Load the xlet class instance with a ClassLoader. // mainClass is the main class of the Xlet. // args contains the command-line arguments supplied with the arg option. // They will be stored in the XletContext. protected PXletManager(ClassLoader xletClassLoader, String laf, String lafTheme, String mainClass, String[] args) throws ClassNotFoundException { xletClass = xletClassLoader.loadClass(mainClass); if (xletClass == null || (!(Xlet.class).isAssignableFrom(xletClass))) { throw new IllegalArgumentException( "Attempt to run a non-Xlet class: " + mainClass); } // Create the XletContext for this xlet. context = new PXletContextImpl(mainClass, args, this); this.xletClass = xletClass; // Create a root Frame for all xlets. Every time a new xlet is // added, it gets its own container inside this Frame. The XletManager // sets a default size and location, adds a menu bar and window // listener, and leaves the Frame invisible. xletFrame = new XletFrame("Xlet Frame: " + mainClass, this, laf, lafTheme); // Create a thread group that all the threads used by this xlet // would be a part of. This is necessary for providing a separate // EventQueue per xlet. threadGroup = new ThreadGroup(Thread.currentThread().getThreadGroup(), "Xlet Thread Group "); // Create a state queue that holds this xlet�s state change requests. xletQueue = new PXletStateQueue(this); // Now that the setup is complete, enter a request to move this xlet // from UNLOADED to LOADED. xletQueue.push(XletState.LOADED); } // Return a container for this xlet. // This will be called at most once, by PXletContextImpl.getContainer() public Container getContainer() { return xletFrame.getContainer(); } //private Listener listener = null; // The following four methods are implementations of XletLifecycleHandler // methods. They allow a third party to request an xlet state change. // They request a state change through the xlet state queue (which holds // an xlet�s state change requests). public void postInitXlet() { xletQueue.push(DesiredXletState.INITIALIZE); } public void postStartXlet() { xletQueue.push(XletState.ACTIVE); } public void postPauseXlet() { xletQueue.push(XletState.PAUSED); } // // The xletDestroy() from the appmanager is best effort. // The appmanager inspects state. If it discovers that this jvm // has not exited, it can use Client.kill() to forcibly get rid of it. // public void postDestroyXlet(boolean unconditional) { if (!unconditional) { xletQueue.push(DesiredXletState.CONDITIONAL_DESTROY); } else { xletQueue.push(XletState.DESTROYED); } } // Set the state of the xlet. If the current state is destroyed, don�t // bother to set the state. If the target state is destroyed, // exit this vm instance. public void setState(XletState state) { synchronized (stateGuard) { if (currentState == XletState.DESTROYED) return; currentState = state; stateGuard.notifyAll(); } // // If we are requesting a state change into DESTROYED, we want to // go away. So don't linger, get rid of the enclosing JVM instance // if (state == XletState.DESTROYED) { if (verbose) { System.err.println("@@PXletManager setting xlet state to DESTROYED"); } System.exit(0); } } // Implementation of XletLifecycleHandler. Allows a third party to query // the xlet state. public int getState() { XletState state = getXletState(); if (state == XletState.LOADED) { return LOADED; } else if (state == XletState.PAUSED) { return PAUSED; } else if (state == XletState.ACTIVE) { return ACTIVE; } else if (state == XletState.DESTROYED) { return DESTROYED; } else { return UNKNOWN; } } // Used internally. Returns the current XletState as an instance of // XletState rather than as an integer. public XletState getXletState() { return currentState; } // Keeps track of whether the xlet state change request was fulfilled or not. boolean requestCompleted = false; // Typically called from PXletStateQueue. Handles xlet�s state change // request. Makes sure it only performs a permissible state change. public void handleRequest(XletState desiredState) { XletState targetState = currentState; requestCompleted = false; try { synchronized (stateGuard) { if (desiredState == XletState.LOADED) { if (currentState != XletState.UNLOADED) return; targetState = XletState.LOADED; Class[] types = new Class[0]; Constructor m = xletClass.getConstructor(types); xlet = (Xlet) m.newInstance(new Object[0]); } else if (desiredState == DesiredXletState.INITIALIZE) { if (currentState != XletState.LOADED) return; targetState = XletState.PAUSED; try { xlet.initXlet(context); } catch (XletStateChangeException xsce) { targetState = XletState.DESTROYED; // // The xletDestroy() from the appmanager is best // effort. The appmanager inspects state. If it // discovers that this jvm has not exited, it can use // Client.kill() to forcibly get rid of it. // try { xlet.destroyXlet(true); } catch (XletStateChangeException xsce2) { // Xlet refused to go away // This is unconditional though so ignore // exception. The right thing will happen // when setState(DESTROYED) is called. } } } else if (desiredState == XletState.ACTIVE) { if (currentState != XletState.PAUSED) return; targetState = XletState.ACTIVE; try { xlet.startXlet(); } catch (XletStateChangeException xsce) { // // The spec is not explicit here. // If you can't activate the xlet it // lingers in its paused state. // targetState = currentState; } } else if (desiredState == XletState.PAUSED) { if (currentState != XletState.ACTIVE) return; targetState = XletState.PAUSED; xlet.pauseXlet(); } else if (desiredState == DesiredXletState.CONDITIONAL_DESTROY) { if (currentState == XletState.DESTROYED) return; targetState = XletState.DESTROYED; try { xlet.destroyXlet(false); } catch (XletStateChangeException xsce) { // Xlet refused to go away if (verbose) { System.err.println("XLET REFUSED TO GO AWAY\n"); } targetState = currentState; } } else if (desiredState == XletState.DESTROYED) { targetState = XletState.DESTROYED; if (currentState == XletState.DESTROYED) return; try { xlet.destroyXlet(true); } catch (XletStateChangeException xsce) { // Xlet refused to go away // This is unconditional though so ignore // exception. The right thing will happen // when setState(DESTROYED) is called. } } setState(targetState); } } catch (Throwable e) { if (verbose) { if (verbose) { System.err.println("EXCEPTION DURING XLET STATE HANDLING: "+e); } } e.printStackTrace(); if (targetState == XletState.DESTROYED) { setState(XletState.DESTROYED); } else { handleRequest(XletState.DESTROYED); } } requestCompleted = true; } // Creates an xlet. This static method causes a new instance of // XletManager to be created, and to load the xlet. public static XletLifecycleHandler createXlet(String mainClass, String laf, String lafTheme) throws IOException { return (XletLifecycleHandler)createXlet(mainClass, laf, lafTheme, new String[] {"."}, new String[]{}); } public static PXletManager createXlet(String mainClass, String laf, String lafTheme, String[] paths, String[] args) throws IOException { Vector v = new Vector(); for (int i = 0; i < paths.length; i++) { URL url = parseURL(paths[i]); if (url != null) { v.add(url); } } PXletClassLoader cl = new PXletClassLoader( (URL[])v.toArray(new URL[0]), null); try { return new PXletManager(cl, laf, lafTheme, mainClass, args); } catch (ClassNotFoundException e) { e.printStackTrace(); throw new IOException("Cannot find class " + mainClass); } } /** * Following the relevant RFC, construct a valid URL based on the passed in * string. * * @param url A string which represents either a relative or absolute URL, * or a string that could be an absolute or relative path. * @return A URL when the passed in string can be interpreted according * to the RFC. <code>null</code> otherwise. */ private static URL parseURL(String url) { URL u = null; try { if (url.startsWith(File.separator)) { // absolute path u = new File(url).toURL(); } else if (url.indexOf(':') <= 1) { // we were passed in a relative URL or an absolute URL on a // win32 machine u = new File(System.getProperty("user.dir"), url).getCanonicalFile().toURL(); } else { if (url.startsWith("file:") && url.replace(File.separatorChar, '/').indexOf('/') == -1) { // We were passed in a relative "file" URL, like this: // "file:index.html". // Prepend current directory location. String fname = url.substring("file:".length()); if (fname.length() > 0) { u = new File(System.getProperty("user.dir"), fname).toURL(); } else { u = new URL(url); } } else { u = new URL(url); } } } catch (IOException e) { if (verbose) { System.err.println("error in parsing: " + url); } } return u; } } class PXletClassLoader extends CDCAppClassLoader { public PXletClassLoader(URL[] urls, ClassLoader parent) { super(urls, parent); } }