/*
* 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 java.util.concurrent.LinkedBlockingQueue;
import sun.awt.AWTAccessor;
import sun.awt.AppContext;
import sun.awt.EmbeddedFrame;
import sun.awt.SunToolkit;
import sun.awt.util.PerformanceLogger;
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 Deprecated: The Applet API is deprecated. See the
java.applet package
documentation for further information.
/**
* 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.
*
* @deprecated The Applet API is deprecated. See the
* <a href="../../java/applet/package-summary.html"> java.applet package
* documentation</a> for further information.
*
* @author Arthur van Hoff
*/
@SuppressWarnings({"serial"}) // JDK implementation class
@Deprecated(since = "9")
public
abstract class AppletPanel extends Panel implements AppletStub, Runnable {
The applet (if loaded).
/**
* The applet (if loaded).
*/
Applet applet;
The classloader for the applet.
/**
* The classloader for the applet.
*/
protected AppletClassLoader loader;
/* applet event ids */
public static final int APPLET_DISPOSE = 0;
public static final int APPLET_LOAD = 1;
public static final int APPLET_INIT = 2;
public static final int APPLET_START = 3;
public static final int APPLET_STOP = 4;
public static final int APPLET_DESTROY = 5;
public static final int APPLET_QUIT = 6;
public static final int APPLET_ERROR = 7;
/* send to the parent to force relayout */
public static final int APPLET_RESIZE = 51234;
/* sent to a (distant) parent to indicate that the applet is being
* loaded or as completed loading
*/
public static final int APPLET_LOADING = 51235;
public static final 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);
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 */
protected abstract String getCode();
protected abstract String getJarFiles();
@Override
public abstract int getWidth();
@Override
public abstract int getHeight();
public abstract 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, 0, false);
// set the context class loader for this thread
AccessController.doPrivileged(new PrivilegedAction<Object>() {
@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
@SuppressWarnings("deprecation")
public Dimension minimumSize() {
return new Dimension(defaultAppletSize.width,
defaultAppletSize.height);
}
Preferred size
/**
* Preferred size
*/
@Override
@SuppressWarnings("deprecation")
public Dimension preferredSize() {
return new Dimension(currentAppletSize.width,
currentAppletSize.height);
}
private AppletListener listeners;
AppletEvent Queue
/**
* AppletEvent Queue
*/
private LinkedBlockingQueue<Integer> queue = null;
public synchronized void addAppletListener(AppletListener l) {
listeners = AppletEventMulticaster.add(listeners, l);
}
public synchronized 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 LinkedBlockingQueue<>();
}
boolean inserted = queue.add(id);
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();
}
int eventId = queue.take();
return new AppletEvent(this, eventId, 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
*
* <pre>{@literal
* 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 --> ...
* }</pre>
*
* 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) {
setLoaderThread(new Thread(null, this,
"AppletLoader", 0, false));
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 (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));
}
// 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) {
return AWTAccessor.getKeyboardFocusManagerAccessor()
.getMostRecentFocusOwner(w);
}
/*
* 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.
*/
@SuppressWarnings("deprecation")
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 {
String code = getCode();
if (code != null) {
applet = (Applet)loader.loadCode(code).newInstance();
} else {
String msg = "nocode";
status = APPLET_ERROR;
showAppletStatus(msg);
showAppletLog(msg);
repaint();
}
// 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<String, AppletClassLoader> 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 = classloaders.get(key);
if (c == null) {
AccessControlContext acc =
getAccessControlContext(codebase);
c = AccessController.doPrivileged(
new PrivilegedAction<AppletClassLoader>() {
@Override
public AppletClassLoader 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 = 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 = AccessController.doPrivileged(
new PrivilegedAction<PermissionCollection>() {
@Override
public PermissionCollection 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<Window> weakRef = null;
// Remove frame from the Window list in wrong AppContext
{
// Lookup current frame's AppContext
@SuppressWarnings("unchecked")
Vector<WeakReference<Window>> windowList =
(Vector<WeakReference<Window>>)oldAppContext.get(Window.class);
if (windowList != null) {
for (WeakReference<Window> 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
{
@SuppressWarnings("unchecked")
Vector<WeakReference<Window>> windowList =
(Vector<WeakReference<Window>>)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 = loader.isJDK11Target(appletClass);
Boolean jdk12Target = 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");
}