AppletPanel.java revision 1365
/*
* Copyright 1995-2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* 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.
*/
/**
* 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
/**
* The applet (if loaded).
*/
/**
* 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.
*/
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.
*/
protected int status;
/**
* The thread for the applet.
*/
/**
* The initial applet size.
*/
/**
* The current applet size.
*/
/**
* The thread to use during applet loading
*/
/**
* Flag to indicate that a loading has been cancelled
*/
boolean loadAbortRequest = false;
/* abstract classes */
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.
// 4668479: Option to turn off codebase lookup in AppletClassLoader
// during resource requests. [stanley.ho]
loader.setCodebaseLookup(false);
else
loader.setCodebaseLookup(true);
// set the context class loader for this thread
return null;
}
});
}
void joinAppletThread() throws InterruptedException {
}
}
void release() {
}
}
/**
* Construct an applet viewer and start the applet.
*/
public void init() {
try {
// Get the width (if any)
// Get the height (if any)
} catch (NumberFormatException e) {
// Turn on the error flag and let TagAppletPanel
// do the right thing.
showAppletStatus("badattribute.exception");
showAppletLog("badattribute.exception");
}
setLayout(new BorderLayout());
}
/**
* Minimum size
*/
public Dimension minimumSize() {
}
/**
* Preferred size
*/
public Dimension preferredSize() {
}
private AppletListener listeners;
/**
* AppletEvent Queue
*/
synchronized public void addAppletListener(AppletListener l) {
}
synchronized public void removeAppletListener(AppletListener l) {
}
/**
* Dispatch event to the listeners..
*/
//System.out.println("SEND= " + id);
}
}
/**
* Send an event. Queue it for execution by the handler thread.
*/
synchronized(this) {
//System.out.println("SEND0= " + 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
release();
}
}
/**
* Get an event from the queue.
*/
wait();
}
}
boolean emptyEventQueue() {
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) {
}
}
}
/**
* 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 .
*
* Same as APPLET_LOAD to
* APPLET_DISPOSE since all of this are triggered by browser.
*
*
*/
public void run() {
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;
try {
evt = getNextEvent();
} catch (InterruptedException e) {
showAppletStatus("bail");
return;
}
//showAppletStatus("EVENT = " + evt.getID());
try {
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));
// we get to go to sleep while this runs
loaderThread.join();
} 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
showAppletStatus("notloaded");
break;
}
if (doInit) {
if (PerformanceLogger.loggingEnabled()) {
}
}
//Need the default(fallback) font to be created in this AppContext
if (f == null ||
}
doInit = true; // allow restarts
// Validate the applet in event dispatch thread
// to avoid deadlock.
try {
final AppletPanel p = this;
public void run() {
p.validate();
}
});
}
catch(InterruptedException ie) {
}
catch(InvocationTargetException ite) {
}
showAppletStatus("inited");
break;
case APPLET_START:
{
showAppletStatus("notinited");
break;
}
// Validate and show the applet in event dispatch thread
// to avoid deadlock.
try {
final AppletPanel p = this;
public void run() {
p.validate();
a.setVisible(true);
// Fix for BugTraq ID 4041703.
// Set the default focus for an applet.
if (hasInitialFocus())
}
});
}
catch(InterruptedException ie) {
}
catch(InvocationTargetException ite) {
}
showAppletStatus("started");
break;
}
case APPLET_STOP:
if (status != APPLET_START) {
showAppletStatus("notstarted");
break;
}
// Hide the applet in event dispatch thread
// to avoid deadlock.
try {
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 {
// rethrow exception to be handled as it normally would be.
throw e;
}
showAppletStatus("stopped");
break;
case APPLET_DESTROY:
showAppletStatus("notstopped");
break;
}
// 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 {
// rethrow exception to be handled as it normally would be.
throw e;
}
showAppletStatus("destroyed");
break;
case APPLET_DISPOSE:
showAppletStatus("notdestroyed");
break;
}
try
{
{
public void run()
{
remove(a);
}
});
}
catch(InterruptedException ie)
{
}
catch(InvocationTargetException ite)
{
}
showAppletStatus("disposed");
disposed = true;
break;
case APPLET_QUIT:
return;
}
} catch (Exception e) {
if (e.getMessage() != null) {
e.getMessage());
} else {
}
} catch (ThreadDeath e) {
showAppletStatus("death");
return;
} catch (Error e) {
if (e.getMessage() != null) {
e.getMessage());
} else {
}
}
}
}
/**
* 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.
*/
try {
meth = KeyboardFocusManager.class.getDeclaredMethod("getMostRecentFocusOwner", new Class[] {Window.class});
meth.setAccessible(true);
} catch (Exception e) {
// Must never happen
e.printStackTrace();
}
return meth;
}
});
// Meth refers static method
try {
} 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() {
}
} else if (parent.isFocusCycleRoot()) {
}
}
if (parent instanceof EmbeddedFrame) {
}
// 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.
}
}
/**
* 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;
}
// REMIND -- might be cool to visually indicate loading here --
// maybe do animation?
// Create a class loader
// Load the archives if present.
// REMIND - this probably should be done in a separate thread,
// or at least the additional archives (epll).
// setup applet AppContext
// this must be called before loadJarFiles
try {
} catch (ClassNotFoundException e) {
return;
} catch (InstantiationException e) {
return;
} catch (IllegalAccessException e) {
// sbb -- I added a return here
return;
} catch (Exception e) {
return;
} catch (ThreadDeath e) {
showAppletStatus("death");
return;
} catch (Error e) {
return;
} finally {
// notify that loading is no longer going on
}
// Fixed #4508194: NullPointerException thrown during
// quick page switch
//
{
// Stick it in the frame
showAppletStatus("loaded");
validate();
}
}
// return null;
throw new InstantiationException("Either \"code\" or \"object\" should be specified, but not both.");
}
repaint();
}
doInit = true;
} else {
// serName is not null;
}
});
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]
if (Thread.interrupted()) {
try {
// REMIND: This may not be exactly the right thing: the
// status is set by the stop button and not necessarily
// here.
showAppletStatus("death");
} finally {
}
return null;
}
return applet;
}
// Load the archives if present.
// REMIND - this probably should be done in a separate thread,
// or at least the additional archives (epll).
while(st.hasMoreTokens()) {
try {
} 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);
} else {
}
}
protected synchronized boolean okToLoad() {
return !loadAbortRequest;
}
protected synchronized void clearLoadAbortRequest() {
loadAbortRequest = false;
}
protected synchronized void setLoadAbortRequest() {
loadAbortRequest = true;
}
this.loaderThread = loaderThread;
}
/**
* Return true when the applet has been started.
*/
public boolean isActive() {
return status == APPLET_START;
}
/**
* Is called when the applet wants to be resized.
*/
}
final AppletPanel ap = this;
new Runnable(){
public void run(){
{
}
}
}));
}
}
}
return applet;
}
/**
* Status line. Called by the AppletPanel to provide
* feedback on the Applet's state.
*/
}
}
}
/**
* Called by the AppletPanel to print to the log.
*/
}
}
/**
* 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
*/
/**
* Flush a class loader.
*/
}
/**
* 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.
*/
return new AppletClassLoader(codebase);
}
/**
* Get a class loader. Create in a restricted context
*/
if (c == null) {
c = (AppletClassLoader)
/* 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()) {
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.
*/
if (p != null) {
} else {
return null;
}
}
});
perms = new Permissions();
//XXX: this is needed to be able to create the classloader itself!
Permission p;
try {
p = urlConnection.getPermission();
p = null;
}
if (p != null)
if (p instanceof FilePermission) {
if (endIndex != -1) {
path += "-";
}
}
} else {
if (urlConnection instanceof JarURLConnection) {
}
}
return acc;
}
public Thread getAppletHandlerThread() {
return handler;
}
public int getAppletWidth() {
return currentAppletSize.width;
}
public int getAppletHeight() {
return currentAppletSize.height;
}
{
// 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
// 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
if (oldAppContext == newAppContext)
return;
// Synchronization on Window.class is needed for locking the
// critical section of the window list in AppContext.
synchronized (Window.class)
{
// 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) {
break;
}
}
// Remove frame from wrong AppContext
}
}
// Put the frame into the applet's AppContext map
// Insert frame into the Window list in the applet's AppContext map
{
if (windowList == null) {
}
// use the same weakRef here as it is used elsewhere
}
}
}
// 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.
*/
{
// 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.
synchronized(appletClass) {
// Determine if the JDK level of an applet has been
// checked before.
// if applet JDK level has been checked before, retrieve
// value and return.
return;
}
// first convert any '.' to '/'
// append .class
byte[] classHeader = new byte[8];
try {
}
});
// Read the first 8 bytes of the class file
// 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
// 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.
}
}
/**
* 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 readByte(byte b) {
return ((int)b) & 0xFF;
}
}