| Method from sun.applet.AppletPanel Detail: |
public synchronized void addAppletListener(AppletListener l) {
listeners = AppletEventMulticaster.add(listeners, l);
}
|
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);
}
}
}));
}
}
Is called when the applet wants to be resized. |
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);
}
}
}
|
protected synchronized void clearLoadAbortRequest() {
loadAbortRequest = false;
}
|
protected Applet createApplet(AppletClassLoader loader) throws IOException, ClassNotFoundException, InterruptedException, InstantiationException, IllegalAccessException {
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;
}
|
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();
}
|
protected AppletClassLoader createClassLoader(URL codebase) {
return new AppletClassLoader(codebase);
}
This method actually creates an AppletClassLoader.
It can be override by subclasses (such as the Plug-in)
to provide different classloaders. |
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);
}
}
Dispatch event to the listeners.. |
boolean emptyEventQueue() {
if ((queue == null) || (queue.isEmpty()))
return true;
else
return false;
}
|
public static synchronized void flushClassLoader(String key) {
classloaders.remove(key);
}
|
public static synchronized void flushClassLoaders() {
classloaders = new HashMap();
}
|
public Applet getApplet() {
return applet;
}
|
public Thread getAppletHandlerThread() {
return handler;
}
|
public int getAppletHeight() {
return currentAppletSize.height;
}
|
public int getAppletWidth() {
return currentAppletSize.width;
}
|
synchronized AppletClassLoader getClassLoader(URL codebase,
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 a class loader. Create in a restricted context |
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();
}
Get caching key for classloader cache |
abstract protected String getCode()
|
abstract public int getHeight()
|
abstract protected String getJarFiles()
|
synchronized AppletEvent getNextEvent() throws InterruptedException {
while (queue == null || queue.isEmpty()) {
wait();
}
Integer eventId = (Integer)queue.dequeue();
return new AppletEvent(this, eventId.intValue(), null);
}
Get an event from the queue. |
abstract protected String getSerializedObject()
|
abstract public int getWidth()
|
abstract public boolean hasInitialFocus()
|
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();
}
Construct an applet viewer and start the applet. |
public boolean isActive() {
return status == APPLET_START;
}
Return true when the applet has been started. |
protected boolean isJDK11Applet() {
return jdk11Applet;
}
Return true if applet is targeted to JDK 1.1. |
protected boolean isJDK12Applet() {
return jdk12Applet;
}
Return true if applet is targeted to JDK1.2. |
void joinAppletThread() throws InterruptedException {
if (handler != null) {
handler.join();
handler = null;
}
}
|
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;
}
}
}
}
|
public Dimension minimumSize() {
return new Dimension(defaultAppletSize.width,
defaultAppletSize.height);
}
|
protected synchronized boolean okToLoad() {
return !loadAbortRequest;
}
|
public Dimension preferredSize() {
return new Dimension(currentAppletSize.width,
currentAppletSize.height);
}
|
void release() {
if (loader != null) {
loader.release();
loader = null;
}
}
|
public synchronized void removeAppletListener(AppletListener l) {
listeners = AppletEventMulticaster.remove(listeners, l);
}
|
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();
}
}
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 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();
}
}
Send an event. Queue it for execution by the handler thread. |
public void setBounds(int x,
int y,
int width,
int height) {
super.setBounds(x, y, width, height);
currentAppletSize.width = width;
currentAppletSize.height = height;
}
|
protected synchronized void setLoadAbortRequest() {
loadAbortRequest = true;
}
|
protected void setupAppletAppContext() {
// do nothing
}
|
protected void showAppletException(Throwable t) {
t.printStackTrace();
repaint();
}
Called by the AppletPanel to provide
feedback when an exception has happened. |
protected void showAppletLog(String msg) {
System.out.println(amh.getMessage(msg));
}
Called by the AppletPanel to print to the log. |
protected void showAppletLog(String msg,
Object arg) {
System.out.println(amh.getMessage(msg, arg));
}
|
protected void showAppletStatus(String status) {
getAppletContext().showStatus(amh.getMessage(status));
}
Status line. Called by the AppletPanel to provide
feedback on the Applet's state. |
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));
}
|
protected synchronized void stopLoading() {
// REMIND: fill in the body
if (loaderThread != null) {
//System.out.println("Interrupting applet loader thread: " + loaderThread);
loaderThread.interrupt();
} else {
setLoadAbortRequest();
}
}
Request that the loading of the applet be stopped. |