--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/sun/applet/AppletPanel.java Sat Dec 01 00:00:00 2007 +0000
@@ -0,0 +1,1319 @@
+/*
+ * Copyright 1995-2007 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code 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 in the LICENSE file that
+ * accompanied this code).
+ *
+ * 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 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.applet;
+
+import java.applet.*;
+import java.awt.*;
+import java.awt.event.*;
+import java.awt.image.ColorModel;
+import java.awt.image.MemoryImageSource;
+import java.io.*;
+import java.lang.ref.WeakReference;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.InetAddress;
+import java.net.JarURLConnection;
+import java.net.MalformedURLException;
+import java.net.SocketPermission;
+import java.net.URL;
+import java.net.UnknownHostException;
+import java.security.*;
+import java.util.*;
+import java.util.Collections;
+import java.util.Locale;
+import java.util.WeakHashMap;
+import javax.swing.SwingUtilities;
+import sun.awt.AppContext;
+import sun.awt.EmbeddedFrame;
+import sun.awt.SunToolkit;
+import sun.misc.MessageUtils;
+import sun.misc.PerformanceLogger;
+import sun.misc.Queue;
+import sun.security.util.SecurityConstants;
+
+/**
+ * Applet panel class. The panel manages and manipulates the
+ * applet as it is being loaded. It forks a separate thread in a new
+ * thread group to call the applet's init(), start(), stop(), and
+ * destroy() methods.
+ *
+ * @author Arthur van Hoff
+ */
+public
+abstract class AppletPanel extends Panel implements AppletStub, Runnable {
+
+ /**
+ * The applet (if loaded).
+ */
+ Applet applet;
+
+ /**
+ * Applet will allow initialization. Should be
+ * set to false if loading a serialized applet
+ * that was pickled in the init=true state.
+ */
+ protected boolean doInit = true;
+
+
+ /**
+ * The classloader for the applet.
+ */
+ AppletClassLoader loader;
+
+ /* applet event ids */
+ public final static int APPLET_DISPOSE = 0;
+ public final static int APPLET_LOAD = 1;
+ public final static int APPLET_INIT = 2;
+ public final static int APPLET_START = 3;
+ public final static int APPLET_STOP = 4;
+ public final static int APPLET_DESTROY = 5;
+ public final static int APPLET_QUIT = 6;
+ public final static int APPLET_ERROR = 7;
+
+ /* send to the parent to force relayout */
+ public final static int APPLET_RESIZE = 51234;
+
+ /* sent to a (distant) parent to indicate that the applet is being
+ * loaded or as completed loading
+ */
+ public final static int APPLET_LOADING = 51235;
+ public final static int APPLET_LOADING_COMPLETED = 51236;
+
+ /**
+ * The current status. One of:
+ * APPLET_DISPOSE,
+ * APPLET_LOAD,
+ * APPLET_INIT,
+ * APPLET_START,
+ * APPLET_STOP,
+ * APPLET_DESTROY,
+ * APPLET_ERROR.
+ */
+ protected int status;
+
+ /**
+ * The thread for the applet.
+ */
+ Thread handler;
+
+
+ /**
+ * The initial applet size.
+ */
+ Dimension defaultAppletSize = new Dimension(10, 10);
+
+ /**
+ * The current applet size.
+ */
+ Dimension currentAppletSize = new Dimension(10, 10);
+
+ MessageUtils mu = new MessageUtils();
+
+ /**
+ * The thread to use during applet loading
+ */
+
+ Thread loaderThread = null;
+
+ /**
+ * Flag to indicate that a loading has been cancelled
+ */
+ boolean loadAbortRequest = false;
+
+ /* abstract classes */
+ abstract protected String getCode();
+ abstract protected String getJarFiles();
+ abstract protected String getSerializedObject();
+
+ abstract public int getWidth();
+ abstract public int getHeight();
+ abstract public boolean hasInitialFocus();
+
+ private static int threadGroupNumber = 0;
+
+ protected void setupAppletAppContext() {
+ // do nothing
+ }
+
+ /*
+ * Creates a thread to run the applet. This method is called
+ * each time an applet is loaded and reloaded.
+ */
+ synchronized void createAppletThread() {
+ // Create a thread group for the applet, and start a new
+ // thread to load the applet.
+ String nm = "applet-" + getCode();
+ loader = getClassLoader(getCodeBase(), getClassLoaderCacheKey());
+ loader.grab(); // Keep this puppy around!
+
+ // 4668479: Option to turn off codebase lookup in AppletClassLoader
+ // during resource requests. [stanley.ho]
+ String param = getParameter("codebase_lookup");
+
+ if (param != null && param.equals("false"))
+ loader.setCodebaseLookup(false);
+ else
+ loader.setCodebaseLookup(true);
+
+
+ ThreadGroup appletGroup = loader.getThreadGroup();
+
+ handler = new Thread(appletGroup, this, "thread " + nm);
+ // set the context class loader for this thread
+ AccessController.doPrivileged(new PrivilegedAction() {
+ public Object run() {
+ handler.setContextClassLoader(loader);
+ return null;
+ }
+ });
+ handler.start();
+ }
+
+ void joinAppletThread() throws InterruptedException {
+ if (handler != null) {
+ handler.join();
+ handler = null;
+ }
+ }
+
+ void release() {
+ if (loader != null) {
+ loader.release();
+ loader = null;
+ }
+ }
+
+ /**
+ * Construct an applet viewer and start the applet.
+ */
+ public void init() {
+ try {
+ // Get the width (if any)
+ defaultAppletSize.width = getWidth();
+ currentAppletSize.width = defaultAppletSize.width;
+
+ // Get the height (if any)
+ defaultAppletSize.height = getHeight();
+ currentAppletSize.height = defaultAppletSize.height;
+
+ } catch (NumberFormatException e) {
+ // Turn on the error flag and let TagAppletPanel
+ // do the right thing.
+ status = APPLET_ERROR;
+ showAppletStatus("badattribute.exception");
+ showAppletLog("badattribute.exception");
+ showAppletException(e);
+ }
+
+ setLayout(new BorderLayout());
+
+ createAppletThread();
+ }
+
+ /**
+ * Minimum size
+ */
+ public Dimension minimumSize() {
+ return new Dimension(defaultAppletSize.width,
+ defaultAppletSize.height);
+ }
+
+ /**
+ * Preferred size
+ */
+ public Dimension preferredSize() {
+ return new Dimension(currentAppletSize.width,
+ currentAppletSize.height);
+ }
+
+ private AppletListener listeners;
+
+ /**
+ * AppletEvent Queue
+ */
+ private Queue queue = null;
+
+
+ synchronized public void addAppletListener(AppletListener l) {
+ listeners = AppletEventMulticaster.add(listeners, l);
+ }
+
+ synchronized public void removeAppletListener(AppletListener l) {
+ listeners = AppletEventMulticaster.remove(listeners, l);
+ }
+
+ /**
+ * Dispatch event to the listeners..
+ */
+ public void dispatchAppletEvent(int id, Object argument) {
+ //System.out.println("SEND= " + id);
+ if (listeners != null) {
+ AppletEvent evt = new AppletEvent(this, id, argument);
+ listeners.appletStateChanged(evt);
+ }
+ }
+
+ /**
+ * Send an event. Queue it for execution by the handler thread.
+ */
+ public void sendEvent(int id) {
+ synchronized(this) {
+ if (queue == null) {
+ //System.out.println("SEND0= " + id);
+ queue = new Queue();
+ }
+ Integer eventId = new Integer(id);
+ queue.enqueue(eventId);
+ notifyAll();
+ }
+ if (id == APPLET_QUIT) {
+ try {
+ joinAppletThread(); // Let the applet event handler exit
+ } catch (InterruptedException e) {
+ }
+
+ // AppletClassLoader.release() must be called by a Thread
+ // not within the applet's ThreadGroup
+ if (loader == null)
+ loader = getClassLoader(getCodeBase(), getClassLoaderCacheKey());
+ release();
+ }
+ }
+
+ /**
+ * Get an event from the queue.
+ */
+ synchronized AppletEvent getNextEvent() throws InterruptedException {
+ while (queue == null || queue.isEmpty()) {
+ wait();
+ }
+ Integer eventId = (Integer)queue.dequeue();
+ return new AppletEvent(this, eventId.intValue(), null);
+ }
+
+ boolean emptyEventQueue() {
+ if ((queue == null) || (queue.isEmpty()))
+ return true;
+ else
+ return false;
+ }
+
+ /**
+ * This kludge is specific to get over AccessControlException thrown during
+ * Applet.stop() or destroy() when static thread is suspended. Set a flag
+ * in AppletClassLoader to indicate that an
+ * AccessControlException for RuntimePermission "modifyThread" or
+ * "modifyThreadGroup" had occurred.
+ */
+ private void setExceptionStatus(AccessControlException e) {
+ Permission p = e.getPermission();
+ if (p instanceof RuntimePermission) {
+ if (p.getName().startsWith("modifyThread")) {
+ if (loader == null)
+ loader = getClassLoader(getCodeBase(), getClassLoaderCacheKey());
+ loader.setExceptionStatus();
+ }
+ }
+ }
+
+ /**
+ * Execute applet events.
+ * Here is the state transition diagram
+ *
+ * Note: (XXX) is the action
+ * APPLET_XXX is the state
+ * (applet code loaded) --> APPLET_LOAD -- (applet init called)--> APPLET_INIT -- (
+ * applet start called) --> APPLET_START -- (applet stop called) -->APPLET_STOP --(applet
+ * destroyed called) --> APPLET_DESTROY -->(applet gets disposed) -->
+ * APPLET_DISPOSE -->....
+ *
+ * In the legacy lifecycle model. The applet gets loaded, inited and started. So it stays
+ * in the APPLET_START state unless the applet goes away(refresh page or leave the page).
+ * So the applet stop method called and the applet enters APPLET_STOP state. Then if the applet
+ * is revisited, it will call applet start method and enter the APPLET_START state and stay there.
+ *
+ * In the modern lifecycle model. When the applet first time visited, it is same as legacy lifecycle
+ * model. However, when the applet page goes away. It calls applet stop method and enters APPLET_STOP
+ * state and then applet destroyed method gets called and enters APPLET_DESTROY state.
+ *
+ * This code is also called by AppletViewer. In AppletViewer "Restart" menu, the applet is jump from
+ * APPLET_STOP to APPLET_DESTROY and to APPLET_INIT .
+ *
+ * Also, the applet can jump from APPLET_INIT state to APPLET_DESTROY (in Netscape/Mozilla case).
+ * Same as APPLET_LOAD to
+ * APPLET_DISPOSE since all of this are triggered by browser.
+ *
+ *
+ */
+ public void run() {
+
+ Thread curThread = Thread.currentThread();
+ if (curThread == loaderThread) {
+ // if we are in the loader thread, cause
+ // loading to occur. We may exit this with
+ // status being APPLET_DISPOSE, APPLET_ERROR,
+ // or APPLET_LOAD
+ runLoader();
+ return;
+ }
+
+ boolean disposed = false;
+ while (!disposed && !curThread.isInterrupted()) {
+ AppletEvent evt;
+ try {
+ evt = getNextEvent();
+ } catch (InterruptedException e) {
+ showAppletStatus("bail");
+ return;
+ }
+
+ //showAppletStatus("EVENT = " + evt.getID());
+ try {
+ switch (evt.getID()) {
+ case APPLET_LOAD:
+ if (!okToLoad()) {
+ break;
+ }
+ // This complexity allows loading of applets to be
+ // interruptable. The actual thread loading runs
+ // in a separate thread, so it can be interrupted
+ // without harming the applet thread.
+ // So that we don't have to worry about
+ // concurrency issues, the main applet thread waits
+ // until the loader thread terminates.
+ // (one way or another).
+ if (loaderThread == null) {
+ // REMIND: do we want a name?
+ //System.out.println("------------------- loading applet");
+ setLoaderThread(new Thread(this));
+ loaderThread.start();
+ // we get to go to sleep while this runs
+ loaderThread.join();
+ setLoaderThread(null);
+ } else {
+ // REMIND: issue an error -- this case should never
+ // occur.
+ }
+ break;
+
+ case APPLET_INIT:
+ // AppletViewer "Restart" will jump from destroy method to
+ // init, that is why we need to check status w/ APPLET_DESTROY
+ if (status != APPLET_LOAD && status != APPLET_DESTROY) {
+ showAppletStatus("notloaded");
+ break;
+ }
+ applet.resize(defaultAppletSize);
+ if (doInit) {
+ if (PerformanceLogger.loggingEnabled()) {
+ PerformanceLogger.setTime("Applet Init");
+ PerformanceLogger.outputLog();
+ }
+ applet.init();
+ }
+
+ //Need the default(fallback) font to be created in this AppContext
+ Font f = getFont();
+ if (f == null ||
+ "dialog".equals(f.getFamily().toLowerCase(Locale.ENGLISH)) &&
+ f.getSize() == 12 && f.getStyle() == Font.PLAIN) {
+ setFont(new Font(Font.DIALOG, Font.PLAIN, 12));
+ }
+
+ doInit = true; // allow restarts
+
+ // Validate the applet in event dispatch thread
+ // to avoid deadlock.
+ try {
+ final AppletPanel p = this;
+
+ SwingUtilities.invokeAndWait(new Runnable() {
+ public void run() {
+ p.validate();
+ }
+ });
+ }
+ catch(InterruptedException ie) {
+ }
+ catch(InvocationTargetException ite) {
+ }
+
+ status = APPLET_INIT;
+ showAppletStatus("inited");
+ break;
+
+ case APPLET_START:
+ {
+ if (status != APPLET_INIT && status != APPLET_STOP) {
+ showAppletStatus("notinited");
+ break;
+ }
+ applet.resize(currentAppletSize);
+ applet.start();
+
+ // Validate and show the applet in event dispatch thread
+ // to avoid deadlock.
+ try {
+ final AppletPanel p = this;
+ final Applet a = applet;
+
+ SwingUtilities.invokeAndWait(new Runnable() {
+ public void run() {
+ p.validate();
+ a.setVisible(true);
+
+ // Fix for BugTraq ID 4041703.
+ // Set the default focus for an applet.
+ if (hasInitialFocus())
+ setDefaultFocus();
+ }
+ });
+ }
+ catch(InterruptedException ie) {
+ }
+ catch(InvocationTargetException ite) {
+ }
+
+ status = APPLET_START;
+ showAppletStatus("started");
+ break;
+ }
+
+ case APPLET_STOP:
+ if (status != APPLET_START) {
+ showAppletStatus("notstarted");
+ break;
+ }
+ status = APPLET_STOP;
+
+ // Hide the applet in event dispatch thread
+ // to avoid deadlock.
+ try {
+ final Applet a = applet;
+
+ SwingUtilities.invokeAndWait(new Runnable() {
+ public void run()
+ {
+ a.setVisible(false);
+ }
+ });
+ }
+ catch(InterruptedException ie) {
+ }
+ catch(InvocationTargetException ite) {
+ }
+
+
+ // During Applet.stop(), any AccessControlException on an involved Class remains in
+ // the "memory" of the AppletClassLoader. If the same instance of the ClassLoader is
+ // reused, the same exception will occur during class loading. Set the AppletClassLoader's
+ // exceptionStatusSet flag to allow recognition of what had happened
+ // when reusing AppletClassLoader object.
+ try {
+ applet.stop();
+ } catch (java.security.AccessControlException e) {
+ setExceptionStatus(e);
+ // rethrow exception to be handled as it normally would be.
+ throw e;
+ }
+ showAppletStatus("stopped");
+ break;
+
+ case APPLET_DESTROY:
+ if (status != APPLET_STOP && status != APPLET_INIT) {
+ showAppletStatus("notstopped");
+ break;
+ }
+ status = APPLET_DESTROY;
+
+ // During Applet.destroy(), any AccessControlException on an involved Class remains in
+ // the "memory" of the AppletClassLoader. If the same instance of the ClassLoader is
+ // reused, the same exception will occur during class loading. Set the AppletClassLoader's
+ // exceptionStatusSet flag to allow recognition of what had happened
+ // when reusing AppletClassLoader object.
+ try {
+ applet.destroy();
+ } catch (java.security.AccessControlException e) {
+ setExceptionStatus(e);
+ // rethrow exception to be handled as it normally would be.
+ throw e;
+ }
+ showAppletStatus("destroyed");
+ break;
+
+ case APPLET_DISPOSE:
+ if (status != APPLET_DESTROY && status != APPLET_LOAD) {
+ showAppletStatus("notdestroyed");
+ break;
+ }
+ status = APPLET_DISPOSE;
+
+ try
+ {
+ final Applet a = applet;
+
+ EventQueue.invokeAndWait(new Runnable()
+ {
+ public void run()
+ {
+ remove(a);
+ }
+ });
+ }
+ catch(InterruptedException ie)
+ {
+ }
+ catch(InvocationTargetException ite)
+ {
+ }
+ applet = null;
+ showAppletStatus("disposed");
+ disposed = true;
+ break;
+
+ case APPLET_QUIT:
+ return;
+ }
+ } catch (Exception e) {
+ status = APPLET_ERROR;
+ if (e.getMessage() != null) {
+ showAppletStatus("exception2", e.getClass().getName(),
+ e.getMessage());
+ } else {
+ showAppletStatus("exception", e.getClass().getName());
+ }
+ showAppletException(e);
+ } catch (ThreadDeath e) {
+ showAppletStatus("death");
+ return;
+ } catch (Error e) {
+ status = APPLET_ERROR;
+ if (e.getMessage() != null) {
+ showAppletStatus("error2", e.getClass().getName(),
+ e.getMessage());
+ } else {
+ showAppletStatus("error", e.getClass().getName());
+ }
+ showAppletException(e);
+ }
+ clearLoadAbortRequest();
+ }
+ }
+
+ /**
+ * Gets most recent focus owner component associated with the given window.
+ * It does that without calling Window.getMostRecentFocusOwner since it
+ * provides its own logic contradicting with setDefautlFocus. Instead, it
+ * calls KeyboardFocusManager directly.
+ */
+ private Component getMostRecentFocusOwnerForWindow(Window w) {
+ Method meth = (Method)AccessController.doPrivileged(new PrivilegedAction() {
+ public Object run() {
+ Method meth = null;
+ try {
+ meth = KeyboardFocusManager.class.getDeclaredMethod("getMostRecentFocusOwner", new Class[] {Window.class});
+ meth.setAccessible(true);
+ } catch (Exception e) {
+ // Must never happen
+ e.printStackTrace();
+ }
+ return meth;
+ }
+ });
+ if (meth != null) {
+ // Meth refers static method
+ try {
+ return (Component)meth.invoke(null, new Object[] {w});
+ } catch (Exception e) {
+ // Must never happen
+ e.printStackTrace();
+ }
+ }
+ // Will get here if exception was thrown or meth is null
+ return w.getMostRecentFocusOwner();
+ }
+
+ /*
+ * Fix for BugTraq ID 4041703.
+ * Set the focus to a reasonable default for an Applet.
+ */
+ private void setDefaultFocus() {
+ Component toFocus = null;
+ Container parent = getParent();
+
+ if(parent != null) {
+ if (parent instanceof Window) {
+ toFocus = getMostRecentFocusOwnerForWindow((Window)parent);
+ if (toFocus == parent || toFocus == null) {
+ toFocus = parent.getFocusTraversalPolicy().
+ getInitialComponent((Window)parent);
+ }
+ } else if (parent.isFocusCycleRoot()) {
+ toFocus = parent.getFocusTraversalPolicy().
+ getDefaultComponent(parent);
+ }
+ }
+
+ if (toFocus != null) {
+ if (parent instanceof EmbeddedFrame) {
+ ((EmbeddedFrame)parent).synthesizeWindowActivation(true);
+ }
+ // EmbeddedFrame might have focus before the applet was added.
+ // Thus after its activation the most recent focus owner will be
+ // restored. We need the applet's initial focusabled component to
+ // be focused here.
+ toFocus.requestFocusInWindow();
+ }
+ }
+
+ /**
+ * Load the applet into memory.
+ * Runs in a seperate (and interruptible) thread from the rest of the
+ * applet event processing so that it can be gracefully interrupted from
+ * things like HotJava.
+ */
+ private void runLoader() {
+ if (status != APPLET_DISPOSE) {
+ showAppletStatus("notdisposed");
+ return;
+ }
+
+ dispatchAppletEvent(APPLET_LOADING, null);
+
+ // REMIND -- might be cool to visually indicate loading here --
+ // maybe do animation?
+ status = APPLET_LOAD;
+
+ // Create a class loader
+ loader = getClassLoader(getCodeBase(), getClassLoaderCacheKey());
+
+ // Load the archives if present.
+ // REMIND - this probably should be done in a separate thread,
+ // or at least the additional archives (epll).
+
+ String code = getCode();
+
+ // setup applet AppContext
+ // this must be called before loadJarFiles
+ setupAppletAppContext();
+
+ try {
+ loadJarFiles(loader);
+ applet = createApplet(loader);
+ } catch (ClassNotFoundException e) {
+ status = APPLET_ERROR;
+ showAppletStatus("notfound", code);
+ showAppletLog("notfound", code);
+ showAppletException(e);
+ return;
+ } catch (InstantiationException e) {
+ status = APPLET_ERROR;
+ showAppletStatus("nocreate", code);
+ showAppletLog("nocreate", code);
+ showAppletException(e);
+ return;
+ } catch (IllegalAccessException e) {
+ status = APPLET_ERROR;
+ showAppletStatus("noconstruct", code);
+ showAppletLog("noconstruct", code);
+ showAppletException(e);
+ // sbb -- I added a return here
+ return;
+ } catch (Exception e) {
+ status = APPLET_ERROR;
+ showAppletStatus("exception", e.getMessage());
+ showAppletException(e);
+ return;
+ } catch (ThreadDeath e) {
+ status = APPLET_ERROR;
+ showAppletStatus("death");
+ return;
+ } catch (Error e) {
+ status = APPLET_ERROR;
+ showAppletStatus("error", e.getMessage());
+ showAppletException(e);
+ return;
+ } finally {
+ // notify that loading is no longer going on
+ dispatchAppletEvent(APPLET_LOADING_COMPLETED, null);
+ }
+
+ // Fixed #4508194: NullPointerException thrown during
+ // quick page switch
+ //
+ if (applet != null)
+ {
+ // Stick it in the frame
+ applet.setStub(this);
+ applet.hide();
+ add("Center", applet);
+ showAppletStatus("loaded");
+ validate();
+ }
+ }
+
+ protected Applet createApplet(final AppletClassLoader loader) throws ClassNotFoundException,
+ IllegalAccessException, IOException, InstantiationException, InterruptedException {
+ final String serName = getSerializedObject();
+ String code = getCode();
+
+ if (code != null && serName != null) {
+ System.err.println(amh.getMessage("runloader.err"));
+// return null;
+ throw new InstantiationException("Either \"code\" or \"object\" should be specified, but not both.");
+ }
+ if (code == null && serName == null) {
+ String msg = "nocode";
+ status = APPLET_ERROR;
+ showAppletStatus(msg);
+ showAppletLog(msg);
+ repaint();
+ }
+ if (code != null) {
+ applet = (Applet)loader.loadCode(code).newInstance();
+ doInit = true;
+ } else {
+ // serName is not null;
+ InputStream is = (InputStream)
+ java.security.AccessController.doPrivileged(
+ new java.security.PrivilegedAction() {
+ public Object run() {
+ return loader.getResourceAsStream(serName);
+ }
+ });
+ ObjectInputStream ois =
+ new AppletObjectInputStream(is, loader);
+ Object serObject = ois.readObject();
+ applet = (Applet) serObject;
+ doInit = false; // skip over the first init
+ }
+
+ // Determine the JDK level that the applet targets.
+ // This is critical for enabling certain backward
+ // compatibility switch if an applet is a JDK 1.1
+ // applet. [stanley.ho]
+ findAppletJDKLevel(applet);
+
+ if (Thread.interrupted()) {
+ try {
+ status = APPLET_DISPOSE; // APPLET_ERROR?
+ applet = null;
+ // REMIND: This may not be exactly the right thing: the
+ // status is set by the stop button and not necessarily
+ // here.
+ showAppletStatus("death");
+ } finally {
+ Thread.currentThread().interrupt(); // resignal interrupt
+ }
+ return null;
+ }
+ return applet;
+ }
+
+ protected void loadJarFiles(AppletClassLoader loader) throws IOException,
+ InterruptedException {
+ // Load the archives if present.
+ // REMIND - this probably should be done in a separate thread,
+ // or at least the additional archives (epll).
+ String jarFiles = getJarFiles();
+
+ if (jarFiles != null) {
+ StringTokenizer st = new StringTokenizer(jarFiles, ",", false);
+ while(st.hasMoreTokens()) {
+ String tok = st.nextToken().trim();
+ try {
+ loader.addJar(tok);
+ } catch (IllegalArgumentException e) {
+ // bad archive name
+ continue;
+ }
+ }
+ }
+ }
+
+ /**
+ * Request that the loading of the applet be stopped.
+ */
+ protected synchronized void stopLoading() {
+ // REMIND: fill in the body
+ if (loaderThread != null) {
+ //System.out.println("Interrupting applet loader thread: " + loaderThread);
+ loaderThread.interrupt();
+ } else {
+ setLoadAbortRequest();
+ }
+ }
+
+
+ protected synchronized boolean okToLoad() {
+ return !loadAbortRequest;
+ }
+
+ protected synchronized void clearLoadAbortRequest() {
+ loadAbortRequest = false;
+ }
+
+ protected synchronized void setLoadAbortRequest() {
+ loadAbortRequest = true;
+ }
+
+
+ private synchronized void setLoaderThread(Thread loaderThread) {
+ this.loaderThread = loaderThread;
+ }
+
+ /**
+ * Return true when the applet has been started.
+ */
+ public boolean isActive() {
+ return status == APPLET_START;
+ }
+
+
+ private EventQueue appEvtQ = null;
+ /**
+ * Is called when the applet wants to be resized.
+ */
+ public void appletResize(int width, int height) {
+ currentAppletSize.width = width;
+ currentAppletSize.height = height;
+ final Dimension currentSize = new Dimension(currentAppletSize.width,
+ currentAppletSize.height);
+
+ if(loader != null) {
+ AppContext appCtxt = loader.getAppContext();
+ if(appCtxt != null)
+ appEvtQ = (java.awt.EventQueue)appCtxt.get(AppContext.EVENT_QUEUE_KEY);
+ }
+
+ final AppletPanel ap = this;
+ if (appEvtQ != null){
+ appEvtQ.postEvent(new InvocationEvent(Toolkit.getDefaultToolkit(),
+ new Runnable(){
+ public void run(){
+ if(ap != null)
+ {
+ ap.dispatchAppletEvent(APPLET_RESIZE, currentSize);
+ }
+ }
+ }));
+ }
+ }
+
+ public void setBounds(int x, int y, int width, int height) {
+ super.setBounds(x, y, width, height);
+ currentAppletSize.width = width;
+ currentAppletSize.height = height;
+ }
+
+ public Applet getApplet() {
+ return applet;
+ }
+
+ /**
+ * Status line. Called by the AppletPanel to provide
+ * feedback on the Applet's state.
+ */
+ protected void showAppletStatus(String status) {
+ getAppletContext().showStatus(amh.getMessage(status));
+ }
+
+ protected void showAppletStatus(String status, Object arg) {
+ getAppletContext().showStatus(amh.getMessage(status, arg));
+ }
+ protected void showAppletStatus(String status, Object arg1, Object arg2) {
+ getAppletContext().showStatus(amh.getMessage(status, arg1, arg2));
+ }
+
+ /**
+ * Called by the AppletPanel to print to the log.
+ */
+ protected void showAppletLog(String msg) {
+ System.out.println(amh.getMessage(msg));
+ }
+
+ protected void showAppletLog(String msg, Object arg) {
+ System.out.println(amh.getMessage(msg, arg));
+ }
+
+ /**
+ * Called by the AppletPanel to provide
+ * feedback when an exception has happened.
+ */
+ protected void showAppletException(Throwable t) {
+ t.printStackTrace();
+ repaint();
+ }
+
+ /**
+ * Get caching key for classloader cache
+ */
+ public String getClassLoaderCacheKey()
+ {
+ /**
+ * Fixed #4501142: Classlaoder sharing policy doesn't
+ * take "archive" into account. This will be overridden
+ * by Java Plug-in. [stanleyh]
+ */
+ return getCodeBase().toString();
+ }
+
+ /**
+ * The class loaders
+ */
+ private static HashMap classloaders = new HashMap();
+
+ /**
+ * Flush a class loader.
+ */
+ public static synchronized void flushClassLoader(String key) {
+ classloaders.remove(key);
+ }
+
+ /**
+ * Flush all class loaders.
+ */
+ public static synchronized void flushClassLoaders() {
+ classloaders = new HashMap();
+ }
+
+ /**
+ * This method actually creates an AppletClassLoader.
+ *
+ * It can be override by subclasses (such as the Plug-in)
+ * to provide different classloaders.
+ */
+ protected AppletClassLoader createClassLoader(final URL codebase) {
+ return new AppletClassLoader(codebase);
+ }
+
+ /**
+ * Get a class loader. Create in a restricted context
+ */
+ synchronized AppletClassLoader getClassLoader(final URL codebase, final String key) {
+ AppletClassLoader c = (AppletClassLoader)classloaders.get(key);
+ if (c == null) {
+ AccessControlContext acc =
+ getAccessControlContext(codebase);
+ c = (AppletClassLoader)
+ AccessController.doPrivileged(new PrivilegedAction() {
+ public Object run() {
+ AppletClassLoader ac = createClassLoader(codebase);
+ /* Should the creation of the classloader be
+ * within the class synchronized block? Since
+ * this class is used by the plugin, take care
+ * to avoid deadlocks, or specialize
+ * AppletPanel within the plugin. It may take
+ * an arbitrary amount of time to create a
+ * class loader (involving getting Jar files
+ * etc.) and may block unrelated applets from
+ * finishing createAppletThread (due to the
+ * class synchronization). If
+ * createAppletThread does not finish quickly,
+ * the applet cannot process other messages,
+ * particularly messages such as destroy
+ * (which timeout when called from the browser).
+ */
+ synchronized (getClass()) {
+ AppletClassLoader res =
+ (AppletClassLoader)classloaders.get(key);
+ if (res == null) {
+ classloaders.put(key, ac);
+ return ac;
+ } else {
+ return res;
+ }
+ }
+ }
+ },acc);
+ }
+ return c;
+ }
+
+ /**
+ * get the context for the AppletClassLoader we are creating.
+ * the context is granted permission to create the class loader,
+ * connnect to the codebase, and whatever else the policy grants
+ * to all codebases.
+ */
+ private AccessControlContext getAccessControlContext(final URL codebase) {
+
+ PermissionCollection perms = (PermissionCollection)
+ AccessController.doPrivileged(new PrivilegedAction() {
+ public Object run() {
+ Policy p = java.security.Policy.getPolicy();
+ if (p != null) {
+ return p.getPermissions(new CodeSource(null,
+ (java.security.cert.Certificate[]) null));
+ } else {
+ return null;
+ }
+ }
+ });
+
+ if (perms == null)
+ perms = new Permissions();
+
+ //XXX: this is needed to be able to create the classloader itself!
+
+ perms.add(SecurityConstants.CREATE_CLASSLOADER_PERMISSION);
+
+ Permission p;
+ java.net.URLConnection urlConnection = null;
+ try {
+ urlConnection = codebase.openConnection();
+ p = urlConnection.getPermission();
+ } catch (java.io.IOException ioe) {
+ p = null;
+ }
+
+ if (p != null)
+ perms.add(p);
+
+ if (p instanceof FilePermission) {
+
+ String path = p.getName();
+
+ int endIndex = path.lastIndexOf(File.separatorChar);
+
+ if (endIndex != -1) {
+ path = path.substring(0, endIndex+1);
+
+ if (path.endsWith(File.separator)) {
+ path += "-";
+ }
+ perms.add(new FilePermission(path,
+ SecurityConstants.FILE_READ_ACTION));
+ }
+ } else {
+ URL locUrl = codebase;
+ if (urlConnection instanceof JarURLConnection) {
+ locUrl = ((JarURLConnection)urlConnection).getJarFileURL();
+ }
+ String host = locUrl.getHost();
+ if (host != null && (host.length() > 0))
+ perms.add(new SocketPermission(host,
+ SecurityConstants.SOCKET_CONNECT_ACCEPT_ACTION));
+ }
+
+ ProtectionDomain domain =
+ new ProtectionDomain(new CodeSource(codebase,
+ (java.security.cert.Certificate[]) null), perms);
+ AccessControlContext acc =
+ new AccessControlContext(new ProtectionDomain[] { domain });
+
+ return acc;
+ }
+
+ public Thread getAppletHandlerThread() {
+ return handler;
+ }
+
+ public int getAppletWidth() {
+ return currentAppletSize.width;
+ }
+
+ public int getAppletHeight() {
+ return currentAppletSize.height;
+ }
+
+ public static void changeFrameAppContext(Frame frame, AppContext newAppContext)
+ {
+ // Fixed #4754451: Applet can have methods running on main
+ // thread event queue.
+ //
+ // The cause of this bug is that the frame of the applet
+ // is created in main thread group. Thus, when certain
+ // AWT/Swing events are generated, the events will be
+ // dispatched through the wrong event dispatch thread.
+ //
+ // To fix this, we rearrange the AppContext with the frame,
+ // so the proper event queue will be looked up.
+ //
+ // Swing also maintains a Frame list for the AppContext,
+ // so we will have to rearrange it as well.
+
+ // Check if frame's AppContext has already been set properly
+ AppContext oldAppContext = SunToolkit.targetToAppContext(frame);
+
+ if (oldAppContext == newAppContext)
+ return;
+
+ // Synchronization on Window.class is needed for locking the
+ // critical section of the window list in AppContext.
+ synchronized (Window.class)
+ {
+ WeakReference weakRef = null;
+ // Remove frame from the Window list in wrong AppContext
+ {
+ // Lookup current frame's AppContext
+ Vector<WeakReference<Window>> windowList = (Vector<WeakReference<Window>>)oldAppContext.get(Window.class);
+ if (windowList != null) {
+ for (WeakReference ref : windowList) {
+ if (ref.get() == frame) {
+ weakRef = ref;
+ break;
+ }
+ }
+ // Remove frame from wrong AppContext
+ if (weakRef != null)
+ windowList.remove(weakRef);
+ }
+ }
+
+ // Put the frame into the applet's AppContext map
+ SunToolkit.insertTargetMapping(frame, newAppContext);
+
+ // Insert frame into the Window list in the applet's AppContext map
+ {
+ Vector<WeakReference<Window>> windowList = (Vector)newAppContext.get(Window.class);
+ if (windowList == null) {
+ windowList = new Vector<WeakReference<Window>>();
+ newAppContext.put(Window.class, windowList);
+ }
+ // use the same weakRef here as it is used elsewhere
+ windowList.add(weakRef);
+ }
+ }
+ }
+
+ // Flag to indicate if applet is targeted for JDK 1.1.
+ private boolean jdk11Applet = false;
+
+ // Flag to indicate if applet is targeted for JDK 1.2.
+ private boolean jdk12Applet = false;
+
+ /**
+ * Determine JDK level of an applet.
+ */
+ private void findAppletJDKLevel(Applet applet)
+ {
+ // To determine the JDK level of an applet, the
+ // most reliable way is to check the major version
+ // of the applet class file.
+
+ // synchronized on applet class object, so calling from
+ // different instances of the same applet will be
+ // serialized.
+ Class appletClass = applet.getClass();
+
+ synchronized(appletClass) {
+ // Determine if the JDK level of an applet has been
+ // checked before.
+ Boolean jdk11Target = (Boolean) loader.isJDK11Target(appletClass);
+ Boolean jdk12Target = (Boolean) loader.isJDK12Target(appletClass);
+
+ // if applet JDK level has been checked before, retrieve
+ // value and return.
+ if (jdk11Target != null || jdk12Target != null) {
+ jdk11Applet = (jdk11Target == null) ? false : jdk11Target.booleanValue();
+ jdk12Applet = (jdk12Target == null) ? false : jdk12Target.booleanValue();
+ return;
+ }
+
+ String name = appletClass.getName();
+
+ // first convert any '.' to '/'
+ name = name.replace('.', '/');
+
+ // append .class
+ final String resourceName = name + ".class";
+
+ InputStream is = null;
+ byte[] classHeader = new byte[8];
+
+ try {
+ is = (InputStream) java.security.AccessController.doPrivileged(
+ new java.security.PrivilegedAction() {
+ public Object run() {
+ return loader.getResourceAsStream(resourceName);
+ }
+ });
+
+ // Read the first 8 bytes of the class file
+ int byteRead = is.read(classHeader, 0, 8);
+ is.close();
+
+ // return if the header is not read in entirely
+ // for some reasons.
+ if (byteRead != 8)
+ return;
+ }
+ catch (IOException e) {
+ return;
+ }
+
+ // Check major version in class file header
+ int major_version = readShort(classHeader, 6);
+
+ // Major version in class file is as follows:
+ // 45 - JDK 1.1
+ // 46 - JDK 1.2
+ // 47 - JDK 1.3
+ // 48 - JDK 1.4
+ // 49 - JDK 1.5
+ if (major_version < 46)
+ jdk11Applet = true;
+ else if (major_version == 46)
+ jdk12Applet = true;
+
+ // Store applet JDK level in AppContext for later lookup,
+ // e.g. page switch.
+ loader.setJDK11Target(appletClass, jdk11Applet);
+ loader.setJDK12Target(appletClass, jdk12Applet);
+ }
+ }
+
+ /**
+ * Return true if applet is targeted to JDK 1.1.
+ */
+ protected boolean isJDK11Applet() {
+ return jdk11Applet;
+ }
+
+ /**
+ * Return true if applet is targeted to JDK1.2.
+ */
+ protected boolean isJDK12Applet() {
+ return jdk12Applet;
+ }
+
+ /**
+ * Read short from byte array.
+ */
+ private int readShort(byte[] b, int off) {
+ int hi = readByte(b[off]);
+ int lo = readByte(b[off + 1]);
+ return (hi << 8) | lo;
+ }
+
+ private int readByte(byte b) {
+ return ((int)b) & 0xFF;
+ }
+
+
+ private static AppletMessageHandler amh = new AppletMessageHandler("appletpanel");
+}