/*
 * Copyright (c) 1995, 2015, Oracle and/or its affiliates. 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.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.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.io.*;
import java.lang.ref.WeakReference;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.JarURLConnection;
import java.net.SocketPermission;
import java.net.URL;
import java.security.*;
import java.util.*;
import java.util.Locale;
import sun.awt.AWTAccessor;
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
/** * 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).
/** * 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.
/** * 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.
/** * The classloader for the applet. */
protected 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.
/** * 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.
/** * The thread for the applet. */
protected Thread handler;
The initial applet size.
/** * The initial applet size. */
Dimension defaultAppletSize = new Dimension(10, 10);
The current applet size.
/** * The current applet size. */
Dimension currentAppletSize = new Dimension(10, 10); MessageUtils mu = new MessageUtils();
The thread to use during applet loading
/** * The thread to use during applet loading */
Thread loaderThread = null;
Flag to indicate that a loading has been cancelled
/** * 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(); @Override abstract public int getWidth(); @Override 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() { @Override 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.
/** * 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
/** * Minimum size */
@Override public Dimension minimumSize() { return new Dimension(defaultAppletSize.width, defaultAppletSize.height); }
Preferred size
/** * Preferred size */
@Override public Dimension preferredSize() { return new Dimension(currentAppletSize.width, currentAppletSize.height); } private AppletListener listeners;
AppletEvent Queue
/** * 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..
/** * 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.
/** * 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 = Integer.valueOf(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.
/** * 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.
/** * 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.
/** * 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. * * */
@Override 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; Runnable r = new Runnable() { @Override public void run() { p.validate(); } }; AWTAccessor.getEventQueueAccessor().invokeAndWait(applet, r); } 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; Runnable r = new Runnable() { @Override public void run() { p.validate(); a.setVisible(true); // Fix for BugTraq ID 4041703. // Set the default focus for an applet. if (hasInitialFocus()) { setDefaultFocus(); } } }; AWTAccessor.getEventQueueAccessor().invokeAndWait(applet, r); } 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; Runnable r = new Runnable() { @Override public void run() { a.setVisible(false); } }; AWTAccessor.getEventQueueAccessor().invokeAndWait(applet, r); } 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; Runnable r = new Runnable() { @Override public void run() { remove(a); } }; AWTAccessor.getEventQueueAccessor().invokeAndWait(applet, r); } 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.
/** * 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() { @Override 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.
/** * 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; try (InputStream is = AccessController.doPrivileged( (PrivilegedAction<InputStream>)() -> loader.getResourceAsStream(serName)); ObjectInputStream ois = new AppletObjectInputStream(is, loader)) { applet = (Applet) ois.readObject(); 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.
/** * 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.
/** * Return true when the applet has been started. */
@Override public boolean isActive() { return status == APPLET_START; } private EventQueue appEvtQ = null;
Is called when the applet wants to be resized.
/** * Is called when the applet wants to be resized. */
@Override 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() { @Override public void run() { if (ap != null) { ap.dispatchAppletEvent( APPLET_RESIZE, currentSize); } } })); } } @Override 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.
/** * 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.
/** * 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.
/** * 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
/** * Get caching key for classloader cache */
public String getClassLoaderCacheKey() { /** * Fixed #4501142: Classloader sharing policy doesn't * take "archive" into account. This will be overridden * by Java Plug-in. [stanleyh] */ return getCodeBase().toString(); }
The class loaders
/** * The class loaders */
private static HashMap classloaders = new HashMap();
Flush a class loader.
/** * Flush a class loader. */
public static synchronized void flushClassLoader(String key) { classloaders.remove(key); }
Flush all class loaders.
/** * 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.
/** * 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
/** * 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() { @Override 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.
/** * 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() { @Override 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.
/** * 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"; byte[] classHeader = new byte[8]; try (InputStream is = AccessController.doPrivileged( (PrivilegedAction<InputStream>) () -> loader.getResourceAsStream(resourceName))) { // Read the first 8 bytes of the class file int byteRead = is.read(classHeader, 0, 8); // 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.
/** * Return true if applet is targeted to JDK 1.1. */
protected boolean isJDK11Applet() { return jdk11Applet; }
Return true if applet is targeted to JDK1.2.
/** * Return true if applet is targeted to JDK1.2. */
protected boolean isJDK12Applet() { return jdk12Applet; }
Read short from byte array.
/** * 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"); }