0001: /*
0002: * Copyright 1995-2007 Sun Microsystems, Inc. All Rights Reserved.
0003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0004: *
0005: * This code is free software; you can redistribute it and/or modify it
0006: * under the terms of the GNU General Public License version 2 only, as
0007: * published by the Free Software Foundation. Sun designates this
0008: * particular file as subject to the "Classpath" exception as provided
0009: * by Sun in the LICENSE file that accompanied this code.
0010: *
0011: * This code is distributed in the hope that it will be useful, but WITHOUT
0012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0014: * version 2 for more details (a copy is included in the LICENSE file that
0015: * accompanied this code).
0016: *
0017: * You should have received a copy of the GNU General Public License version
0018: * 2 along with this work; if not, write to the Free Software Foundation,
0019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0020: *
0021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0022: * CA 95054 USA or visit www.sun.com if you need additional information or
0023: * have any questions.
0024: */
0025:
0026: package sun.applet;
0027:
0028: import java.applet.*;
0029: import java.awt.*;
0030: import java.awt.event.*;
0031: import java.awt.image.ColorModel;
0032: import java.awt.image.MemoryImageSource;
0033: import java.io.*;
0034: import java.lang.ref.WeakReference;
0035: import java.lang.reflect.InvocationTargetException;
0036: import java.lang.reflect.Method;
0037: import java.net.InetAddress;
0038: import java.net.JarURLConnection;
0039: import java.net.MalformedURLException;
0040: import java.net.SocketPermission;
0041: import java.net.URL;
0042: import java.net.UnknownHostException;
0043: import java.security.*;
0044: import java.util.*;
0045: import java.util.Collections;
0046: import java.util.Locale;
0047: import java.util.WeakHashMap;
0048: import javax.swing.SwingUtilities;
0049: import sun.awt.AppContext;
0050: import sun.awt.EmbeddedFrame;
0051: import sun.awt.SunToolkit;
0052: import sun.misc.MessageUtils;
0053: import sun.misc.PerformanceLogger;
0054: import sun.misc.Queue;
0055: import sun.security.util.SecurityConstants;
0056:
0057: /**
0058: * Applet panel class. The panel manages and manipulates the
0059: * applet as it is being loaded. It forks a separate thread in a new
0060: * thread group to call the applet's init(), start(), stop(), and
0061: * destroy() methods.
0062: *
0063: * @version 1.98, 12/17/02
0064: * @author Arthur van Hoff
0065: */
0066: public abstract class AppletPanel extends Panel implements AppletStub,
0067: Runnable {
0068:
0069: /**
0070: * The applet (if loaded).
0071: */
0072: Applet applet;
0073:
0074: /**
0075: * Applet will allow initialization. Should be
0076: * set to false if loading a serialized applet
0077: * that was pickled in the init=true state.
0078: */
0079: protected boolean doInit = true;
0080:
0081: /**
0082: * The classloader for the applet.
0083: */
0084: AppletClassLoader loader;
0085:
0086: /* applet event ids */
0087: public final static int APPLET_DISPOSE = 0;
0088: public final static int APPLET_LOAD = 1;
0089: public final static int APPLET_INIT = 2;
0090: public final static int APPLET_START = 3;
0091: public final static int APPLET_STOP = 4;
0092: public final static int APPLET_DESTROY = 5;
0093: public final static int APPLET_QUIT = 6;
0094: public final static int APPLET_ERROR = 7;
0095:
0096: /* send to the parent to force relayout */
0097: public final static int APPLET_RESIZE = 51234;
0098:
0099: /* sent to a (distant) parent to indicate that the applet is being
0100: * loaded or as completed loading
0101: */
0102: public final static int APPLET_LOADING = 51235;
0103: public final static int APPLET_LOADING_COMPLETED = 51236;
0104:
0105: /**
0106: * The current status. One of:
0107: * APPLET_DISPOSE,
0108: * APPLET_LOAD,
0109: * APPLET_INIT,
0110: * APPLET_START,
0111: * APPLET_STOP,
0112: * APPLET_DESTROY,
0113: * APPLET_ERROR.
0114: */
0115: protected int status;
0116:
0117: /**
0118: * The thread for the applet.
0119: */
0120: Thread handler;
0121:
0122: /**
0123: * The initial applet size.
0124: */
0125: Dimension defaultAppletSize = new Dimension(10, 10);
0126:
0127: /**
0128: * The current applet size.
0129: */
0130: Dimension currentAppletSize = new Dimension(10, 10);
0131:
0132: MessageUtils mu = new MessageUtils();
0133:
0134: /**
0135: * The thread to use during applet loading
0136: */
0137:
0138: Thread loaderThread = null;
0139:
0140: /**
0141: * Flag to indicate that a loading has been cancelled
0142: */
0143: boolean loadAbortRequest = false;
0144:
0145: /* abstract classes */
0146: abstract protected String getCode();
0147:
0148: abstract protected String getJarFiles();
0149:
0150: abstract protected String getSerializedObject();
0151:
0152: abstract public int getWidth();
0153:
0154: abstract public int getHeight();
0155:
0156: abstract public boolean hasInitialFocus();
0157:
0158: private static int threadGroupNumber = 0;
0159:
0160: protected void setupAppletAppContext() {
0161: // do nothing
0162: }
0163:
0164: /*
0165: * Creates a thread to run the applet. This method is called
0166: * each time an applet is loaded and reloaded.
0167: */
0168: synchronized void createAppletThread() {
0169: // Create a thread group for the applet, and start a new
0170: // thread to load the applet.
0171: String nm = "applet-" + getCode();
0172: loader = getClassLoader(getCodeBase(), getClassLoaderCacheKey());
0173: loader.grab(); // Keep this puppy around!
0174:
0175: // 4668479: Option to turn off codebase lookup in AppletClassLoader
0176: // during resource requests. [stanley.ho]
0177: String param = getParameter("codebase_lookup");
0178:
0179: if (param != null && param.equals("false"))
0180: loader.setCodebaseLookup(false);
0181: else
0182: loader.setCodebaseLookup(true);
0183:
0184: ThreadGroup appletGroup = loader.getThreadGroup();
0185:
0186: handler = new Thread(appletGroup, this , "thread " + nm);
0187: // set the context class loader for this thread
0188: AccessController.doPrivileged(new PrivilegedAction() {
0189: public Object run() {
0190: handler.setContextClassLoader(loader);
0191: return null;
0192: }
0193: });
0194: handler.start();
0195: }
0196:
0197: void joinAppletThread() throws InterruptedException {
0198: if (handler != null) {
0199: handler.join();
0200: handler = null;
0201: }
0202: }
0203:
0204: void release() {
0205: if (loader != null) {
0206: loader.release();
0207: loader = null;
0208: }
0209: }
0210:
0211: /**
0212: * Construct an applet viewer and start the applet.
0213: */
0214: public void init() {
0215: try {
0216: // Get the width (if any)
0217: defaultAppletSize.width = getWidth();
0218: currentAppletSize.width = defaultAppletSize.width;
0219:
0220: // Get the height (if any)
0221: defaultAppletSize.height = getHeight();
0222: currentAppletSize.height = defaultAppletSize.height;
0223:
0224: } catch (NumberFormatException e) {
0225: // Turn on the error flag and let TagAppletPanel
0226: // do the right thing.
0227: status = APPLET_ERROR;
0228: showAppletStatus("badattribute.exception");
0229: showAppletLog("badattribute.exception");
0230: showAppletException(e);
0231: }
0232:
0233: setLayout(new BorderLayout());
0234:
0235: createAppletThread();
0236: }
0237:
0238: /**
0239: * Minimum size
0240: */
0241: public Dimension minimumSize() {
0242: return new Dimension(defaultAppletSize.width,
0243: defaultAppletSize.height);
0244: }
0245:
0246: /**
0247: * Preferred size
0248: */
0249: public Dimension preferredSize() {
0250: return new Dimension(currentAppletSize.width,
0251: currentAppletSize.height);
0252: }
0253:
0254: private AppletListener listeners;
0255:
0256: /**
0257: * AppletEvent Queue
0258: */
0259: private Queue queue = null;
0260:
0261: synchronized public void addAppletListener(AppletListener l) {
0262: listeners = AppletEventMulticaster.add(listeners, l);
0263: }
0264:
0265: synchronized public void removeAppletListener(AppletListener l) {
0266: listeners = AppletEventMulticaster.remove(listeners, l);
0267: }
0268:
0269: /**
0270: * Dispatch event to the listeners..
0271: */
0272: public void dispatchAppletEvent(int id, Object argument) {
0273: //System.out.println("SEND= " + id);
0274: if (listeners != null) {
0275: AppletEvent evt = new AppletEvent(this , id, argument);
0276: listeners.appletStateChanged(evt);
0277: }
0278: }
0279:
0280: /**
0281: * Send an event. Queue it for execution by the handler thread.
0282: */
0283: public void sendEvent(int id) {
0284: synchronized (this ) {
0285: if (queue == null) {
0286: //System.out.println("SEND0= " + id);
0287: queue = new Queue();
0288: }
0289: Integer eventId = new Integer(id);
0290: queue.enqueue(eventId);
0291: notifyAll();
0292: }
0293: if (id == APPLET_QUIT) {
0294: try {
0295: joinAppletThread(); // Let the applet event handler exit
0296: } catch (InterruptedException e) {
0297: }
0298:
0299: // AppletClassLoader.release() must be called by a Thread
0300: // not within the applet's ThreadGroup
0301: if (loader == null)
0302: loader = getClassLoader(getCodeBase(),
0303: getClassLoaderCacheKey());
0304: release();
0305: }
0306: }
0307:
0308: /**
0309: * Get an event from the queue.
0310: */
0311: synchronized AppletEvent getNextEvent() throws InterruptedException {
0312: while (queue == null || queue.isEmpty()) {
0313: wait();
0314: }
0315: Integer eventId = (Integer) queue.dequeue();
0316: return new AppletEvent(this , eventId.intValue(), null);
0317: }
0318:
0319: boolean emptyEventQueue() {
0320: if ((queue == null) || (queue.isEmpty()))
0321: return true;
0322: else
0323: return false;
0324: }
0325:
0326: /**
0327: * This kludge is specific to get over AccessControlException thrown during
0328: * Applet.stop() or destroy() when static thread is suspended. Set a flag
0329: * in AppletClassLoader to indicate that an
0330: * AccessControlException for RuntimePermission "modifyThread" or
0331: * "modifyThreadGroup" had occurred.
0332: */
0333: private void setExceptionStatus(AccessControlException e) {
0334: Permission p = e.getPermission();
0335: if (p instanceof RuntimePermission) {
0336: if (p.getName().startsWith("modifyThread")) {
0337: if (loader == null)
0338: loader = getClassLoader(getCodeBase(),
0339: getClassLoaderCacheKey());
0340: loader.setExceptionStatus();
0341: }
0342: }
0343: }
0344:
0345: /**
0346: * Execute applet events.
0347: * Here is the state transition diagram
0348: *
0349: * Note: (XXX) is the action
0350: * APPLET_XXX is the state
0351: * (applet code loaded) --> APPLET_LOAD -- (applet init called)--> APPLET_INIT -- (
0352: * applet start called) --> APPLET_START -- (applet stop called) -->APPLET_STOP --(applet
0353: * destroyed called) --> APPLET_DESTROY -->(applet gets disposed) -->
0354: * APPLET_DISPOSE -->....
0355: *
0356: * In the legacy lifecycle model. The applet gets loaded, inited and started. So it stays
0357: * in the APPLET_START state unless the applet goes away(refresh page or leave the page).
0358: * So the applet stop method called and the applet enters APPLET_STOP state. Then if the applet
0359: * is revisited, it will call applet start method and enter the APPLET_START state and stay there.
0360: *
0361: * In the modern lifecycle model. When the applet first time visited, it is same as legacy lifecycle
0362: * model. However, when the applet page goes away. It calls applet stop method and enters APPLET_STOP
0363: * state and then applet destroyed method gets called and enters APPLET_DESTROY state.
0364: *
0365: * This code is also called by AppletViewer. In AppletViewer "Restart" menu, the applet is jump from
0366: * APPLET_STOP to APPLET_DESTROY and to APPLET_INIT .
0367: *
0368: * Also, the applet can jump from APPLET_INIT state to APPLET_DESTROY (in Netscape/Mozilla case).
0369: * Same as APPLET_LOAD to
0370: * APPLET_DISPOSE since all of this are triggered by browser.
0371: *
0372: *
0373: */
0374: public void run() {
0375:
0376: Thread curThread = Thread.currentThread();
0377: if (curThread == loaderThread) {
0378: // if we are in the loader thread, cause
0379: // loading to occur. We may exit this with
0380: // status being APPLET_DISPOSE, APPLET_ERROR,
0381: // or APPLET_LOAD
0382: runLoader();
0383: return;
0384: }
0385:
0386: boolean disposed = false;
0387: while (!disposed && !curThread.isInterrupted()) {
0388: AppletEvent evt;
0389: try {
0390: evt = getNextEvent();
0391: } catch (InterruptedException e) {
0392: showAppletStatus("bail");
0393: return;
0394: }
0395:
0396: //showAppletStatus("EVENT = " + evt.getID());
0397: try {
0398: switch (evt.getID()) {
0399: case APPLET_LOAD:
0400: if (!okToLoad()) {
0401: break;
0402: }
0403: // This complexity allows loading of applets to be
0404: // interruptable. The actual thread loading runs
0405: // in a separate thread, so it can be interrupted
0406: // without harming the applet thread.
0407: // So that we don't have to worry about
0408: // concurrency issues, the main applet thread waits
0409: // until the loader thread terminates.
0410: // (one way or another).
0411: if (loaderThread == null) {
0412: // REMIND: do we want a name?
0413: //System.out.println("------------------- loading applet");
0414: setLoaderThread(new Thread(this ));
0415: loaderThread.start();
0416: // we get to go to sleep while this runs
0417: loaderThread.join();
0418: setLoaderThread(null);
0419: } else {
0420: // REMIND: issue an error -- this case should never
0421: // occur.
0422: }
0423: break;
0424:
0425: case APPLET_INIT:
0426: // AppletViewer "Restart" will jump from destroy method to
0427: // init, that is why we need to check status w/ APPLET_DESTROY
0428: if (status != APPLET_LOAD
0429: && status != APPLET_DESTROY) {
0430: showAppletStatus("notloaded");
0431: break;
0432: }
0433: applet.resize(defaultAppletSize);
0434: if (doInit) {
0435: if (PerformanceLogger.loggingEnabled()) {
0436: PerformanceLogger.setTime("Applet Init");
0437: PerformanceLogger.outputLog();
0438: }
0439: applet.init();
0440: }
0441:
0442: //Need the default(fallback) font to be created in this AppContext
0443: Font f = getFont();
0444: if (f == null
0445: || "dialog".equals(f.getFamily()
0446: .toLowerCase(Locale.ENGLISH))
0447: && f.getSize() == 12
0448: && f.getStyle() == Font.PLAIN) {
0449: setFont(new Font(Font.DIALOG, Font.PLAIN, 12));
0450: }
0451:
0452: doInit = true; // allow restarts
0453:
0454: // Validate the applet in event dispatch thread
0455: // to avoid deadlock.
0456: try {
0457: final AppletPanel p = this ;
0458:
0459: SwingUtilities.invokeAndWait(new Runnable() {
0460: public void run() {
0461: p.validate();
0462: }
0463: });
0464: } catch (InterruptedException ie) {
0465: } catch (InvocationTargetException ite) {
0466: }
0467:
0468: status = APPLET_INIT;
0469: showAppletStatus("inited");
0470: break;
0471:
0472: case APPLET_START: {
0473: if (status != APPLET_INIT && status != APPLET_STOP) {
0474: showAppletStatus("notinited");
0475: break;
0476: }
0477: applet.resize(currentAppletSize);
0478: applet.start();
0479:
0480: // Validate and show the applet in event dispatch thread
0481: // to avoid deadlock.
0482: try {
0483: final AppletPanel p = this ;
0484: final Applet a = applet;
0485:
0486: SwingUtilities.invokeAndWait(new Runnable() {
0487: public void run() {
0488: p.validate();
0489: a.setVisible(true);
0490:
0491: // Fix for BugTraq ID 4041703.
0492: // Set the default focus for an applet.
0493: if (hasInitialFocus())
0494: setDefaultFocus();
0495: }
0496: });
0497: } catch (InterruptedException ie) {
0498: } catch (InvocationTargetException ite) {
0499: }
0500:
0501: status = APPLET_START;
0502: showAppletStatus("started");
0503: break;
0504: }
0505:
0506: case APPLET_STOP:
0507: if (status != APPLET_START) {
0508: showAppletStatus("notstarted");
0509: break;
0510: }
0511: status = APPLET_STOP;
0512:
0513: // Hide the applet in event dispatch thread
0514: // to avoid deadlock.
0515: try {
0516: final Applet a = applet;
0517:
0518: SwingUtilities.invokeAndWait(new Runnable() {
0519: public void run() {
0520: a.setVisible(false);
0521: }
0522: });
0523: } catch (InterruptedException ie) {
0524: } catch (InvocationTargetException ite) {
0525: }
0526:
0527: // During Applet.stop(), any AccessControlException on an involved Class remains in
0528: // the "memory" of the AppletClassLoader. If the same instance of the ClassLoader is
0529: // reused, the same exception will occur during class loading. Set the AppletClassLoader's
0530: // exceptionStatusSet flag to allow recognition of what had happened
0531: // when reusing AppletClassLoader object.
0532: try {
0533: applet.stop();
0534: } catch (java.security.AccessControlException e) {
0535: setExceptionStatus(e);
0536: // rethrow exception to be handled as it normally would be.
0537: throw e;
0538: }
0539: showAppletStatus("stopped");
0540: break;
0541:
0542: case APPLET_DESTROY:
0543: if (status != APPLET_STOP && status != APPLET_INIT) {
0544: showAppletStatus("notstopped");
0545: break;
0546: }
0547: status = APPLET_DESTROY;
0548:
0549: // During Applet.destroy(), any AccessControlException on an involved Class remains in
0550: // the "memory" of the AppletClassLoader. If the same instance of the ClassLoader is
0551: // reused, the same exception will occur during class loading. Set the AppletClassLoader's
0552: // exceptionStatusSet flag to allow recognition of what had happened
0553: // when reusing AppletClassLoader object.
0554: try {
0555: applet.destroy();
0556: } catch (java.security.AccessControlException e) {
0557: setExceptionStatus(e);
0558: // rethrow exception to be handled as it normally would be.
0559: throw e;
0560: }
0561: showAppletStatus("destroyed");
0562: break;
0563:
0564: case APPLET_DISPOSE:
0565: if (status != APPLET_DESTROY
0566: && status != APPLET_LOAD) {
0567: showAppletStatus("notdestroyed");
0568: break;
0569: }
0570: status = APPLET_DISPOSE;
0571:
0572: try {
0573: final Applet a = applet;
0574:
0575: EventQueue.invokeAndWait(new Runnable() {
0576: public void run() {
0577: remove(a);
0578: }
0579: });
0580: } catch (InterruptedException ie) {
0581: } catch (InvocationTargetException ite) {
0582: }
0583: applet = null;
0584: showAppletStatus("disposed");
0585: disposed = true;
0586: break;
0587:
0588: case APPLET_QUIT:
0589: return;
0590: }
0591: } catch (Exception e) {
0592: status = APPLET_ERROR;
0593: if (e.getMessage() != null) {
0594: showAppletStatus("exception2", e.getClass()
0595: .getName(), e.getMessage());
0596: } else {
0597: showAppletStatus("exception", e.getClass()
0598: .getName());
0599: }
0600: showAppletException(e);
0601: } catch (ThreadDeath e) {
0602: showAppletStatus("death");
0603: return;
0604: } catch (Error e) {
0605: status = APPLET_ERROR;
0606: if (e.getMessage() != null) {
0607: showAppletStatus("error2", e.getClass().getName(),
0608: e.getMessage());
0609: } else {
0610: showAppletStatus("error", e.getClass().getName());
0611: }
0612: showAppletException(e);
0613: }
0614: clearLoadAbortRequest();
0615: }
0616: }
0617:
0618: /**
0619: * Gets most recent focus owner component associated with the given window.
0620: * It does that without calling Window.getMostRecentFocusOwner since it
0621: * provides its own logic contradicting with setDefautlFocus. Instead, it
0622: * calls KeyboardFocusManager directly.
0623: */
0624: private Component getMostRecentFocusOwnerForWindow(Window w) {
0625: Method meth = (Method) AccessController
0626: .doPrivileged(new PrivilegedAction() {
0627: public Object run() {
0628: Method meth = null;
0629: try {
0630: meth = KeyboardFocusManager.class
0631: .getDeclaredMethod(
0632: "getMostRecentFocusOwner",
0633: new Class[] { Window.class });
0634: meth.setAccessible(true);
0635: } catch (Exception e) {
0636: // Must never happen
0637: e.printStackTrace();
0638: }
0639: return meth;
0640: }
0641: });
0642: if (meth != null) {
0643: // Meth refers static method
0644: try {
0645: return (Component) meth
0646: .invoke(null, new Object[] { w });
0647: } catch (Exception e) {
0648: // Must never happen
0649: e.printStackTrace();
0650: }
0651: }
0652: // Will get here if exception was thrown or meth is null
0653: return w.getMostRecentFocusOwner();
0654: }
0655:
0656: /*
0657: * Fix for BugTraq ID 4041703.
0658: * Set the focus to a reasonable default for an Applet.
0659: */
0660: private void setDefaultFocus() {
0661: Component toFocus = null;
0662: Container parent = getParent();
0663:
0664: if (parent != null) {
0665: if (parent instanceof Window) {
0666: toFocus = getMostRecentFocusOwnerForWindow((Window) parent);
0667: if (toFocus == parent || toFocus == null) {
0668: toFocus = parent.getFocusTraversalPolicy()
0669: .getInitialComponent((Window) parent);
0670: }
0671: } else if (parent.isFocusCycleRoot()) {
0672: toFocus = parent.getFocusTraversalPolicy()
0673: .getDefaultComponent(parent);
0674: }
0675: }
0676:
0677: if (toFocus != null) {
0678: if (parent instanceof EmbeddedFrame) {
0679: ((EmbeddedFrame) parent)
0680: .synthesizeWindowActivation(true);
0681: }
0682: // EmbeddedFrame might have focus before the applet was added.
0683: // Thus after its activation the most recent focus owner will be
0684: // restored. We need the applet's initial focusabled component to
0685: // be focused here.
0686: toFocus.requestFocusInWindow();
0687: }
0688: }
0689:
0690: /**
0691: * Load the applet into memory.
0692: * Runs in a seperate (and interruptible) thread from the rest of the
0693: * applet event processing so that it can be gracefully interrupted from
0694: * things like HotJava.
0695: */
0696: private void runLoader() {
0697: if (status != APPLET_DISPOSE) {
0698: showAppletStatus("notdisposed");
0699: return;
0700: }
0701:
0702: dispatchAppletEvent(APPLET_LOADING, null);
0703:
0704: // REMIND -- might be cool to visually indicate loading here --
0705: // maybe do animation?
0706: status = APPLET_LOAD;
0707:
0708: // Create a class loader
0709: loader = getClassLoader(getCodeBase(), getClassLoaderCacheKey());
0710:
0711: // Load the archives if present.
0712: // REMIND - this probably should be done in a separate thread,
0713: // or at least the additional archives (epll).
0714:
0715: String code = getCode();
0716:
0717: // setup applet AppContext
0718: // this must be called before loadJarFiles
0719: setupAppletAppContext();
0720:
0721: try {
0722: loadJarFiles(loader);
0723: applet = createApplet(loader);
0724: } catch (ClassNotFoundException e) {
0725: status = APPLET_ERROR;
0726: showAppletStatus("notfound", code);
0727: showAppletLog("notfound", code);
0728: showAppletException(e);
0729: return;
0730: } catch (InstantiationException e) {
0731: status = APPLET_ERROR;
0732: showAppletStatus("nocreate", code);
0733: showAppletLog("nocreate", code);
0734: showAppletException(e);
0735: return;
0736: } catch (IllegalAccessException e) {
0737: status = APPLET_ERROR;
0738: showAppletStatus("noconstruct", code);
0739: showAppletLog("noconstruct", code);
0740: showAppletException(e);
0741: // sbb -- I added a return here
0742: return;
0743: } catch (Exception e) {
0744: status = APPLET_ERROR;
0745: showAppletStatus("exception", e.getMessage());
0746: showAppletException(e);
0747: return;
0748: } catch (ThreadDeath e) {
0749: status = APPLET_ERROR;
0750: showAppletStatus("death");
0751: return;
0752: } catch (Error e) {
0753: status = APPLET_ERROR;
0754: showAppletStatus("error", e.getMessage());
0755: showAppletException(e);
0756: return;
0757: } finally {
0758: // notify that loading is no longer going on
0759: dispatchAppletEvent(APPLET_LOADING_COMPLETED, null);
0760: }
0761:
0762: // Fixed #4508194: NullPointerException thrown during
0763: // quick page switch
0764: //
0765: if (applet != null) {
0766: // Stick it in the frame
0767: applet.setStub(this );
0768: applet.hide();
0769: add("Center", applet);
0770: showAppletStatus("loaded");
0771: validate();
0772: }
0773: }
0774:
0775: protected Applet createApplet(final AppletClassLoader loader)
0776: throws ClassNotFoundException, IllegalAccessException,
0777: IOException, InstantiationException, InterruptedException {
0778: final String serName = getSerializedObject();
0779: String code = getCode();
0780:
0781: if (code != null && serName != null) {
0782: System.err.println(amh.getMessage("runloader.err"));
0783: // return null;
0784: throw new InstantiationException(
0785: "Either \"code\" or \"object\" should be specified, but not both.");
0786: }
0787: if (code == null && serName == null) {
0788: String msg = "nocode";
0789: status = APPLET_ERROR;
0790: showAppletStatus(msg);
0791: showAppletLog(msg);
0792: repaint();
0793: }
0794: if (code != null) {
0795: applet = (Applet) loader.loadCode(code).newInstance();
0796: doInit = true;
0797: } else {
0798: // serName is not null;
0799: InputStream is = (InputStream) java.security.AccessController
0800: .doPrivileged(new java.security.PrivilegedAction() {
0801: public Object run() {
0802: return loader.getResourceAsStream(serName);
0803: }
0804: });
0805: ObjectInputStream ois = new AppletObjectInputStream(is,
0806: loader);
0807: Object serObject = ois.readObject();
0808: applet = (Applet) serObject;
0809: doInit = false; // skip over the first init
0810: }
0811:
0812: // Determine the JDK level that the applet targets.
0813: // This is critical for enabling certain backward
0814: // compatibility switch if an applet is a JDK 1.1
0815: // applet. [stanley.ho]
0816: findAppletJDKLevel(applet);
0817:
0818: if (Thread.interrupted()) {
0819: try {
0820: status = APPLET_DISPOSE; // APPLET_ERROR?
0821: applet = null;
0822: // REMIND: This may not be exactly the right thing: the
0823: // status is set by the stop button and not necessarily
0824: // here.
0825: showAppletStatus("death");
0826: } finally {
0827: Thread.currentThread().interrupt(); // resignal interrupt
0828: }
0829: return null;
0830: }
0831: return applet;
0832: }
0833:
0834: protected void loadJarFiles(AppletClassLoader loader)
0835: throws IOException, InterruptedException {
0836: // Load the archives if present.
0837: // REMIND - this probably should be done in a separate thread,
0838: // or at least the additional archives (epll).
0839: String jarFiles = getJarFiles();
0840:
0841: if (jarFiles != null) {
0842: StringTokenizer st = new StringTokenizer(jarFiles, ",",
0843: false);
0844: while (st.hasMoreTokens()) {
0845: String tok = st.nextToken().trim();
0846: try {
0847: loader.addJar(tok);
0848: } catch (IllegalArgumentException e) {
0849: // bad archive name
0850: continue;
0851: }
0852: }
0853: }
0854: }
0855:
0856: /**
0857: * Request that the loading of the applet be stopped.
0858: */
0859: protected synchronized void stopLoading() {
0860: // REMIND: fill in the body
0861: if (loaderThread != null) {
0862: //System.out.println("Interrupting applet loader thread: " + loaderThread);
0863: loaderThread.interrupt();
0864: } else {
0865: setLoadAbortRequest();
0866: }
0867: }
0868:
0869: protected synchronized boolean okToLoad() {
0870: return !loadAbortRequest;
0871: }
0872:
0873: protected synchronized void clearLoadAbortRequest() {
0874: loadAbortRequest = false;
0875: }
0876:
0877: protected synchronized void setLoadAbortRequest() {
0878: loadAbortRequest = true;
0879: }
0880:
0881: private synchronized void setLoaderThread(Thread loaderThread) {
0882: this .loaderThread = loaderThread;
0883: }
0884:
0885: /**
0886: * Return true when the applet has been started.
0887: */
0888: public boolean isActive() {
0889: return status == APPLET_START;
0890: }
0891:
0892: private EventQueue appEvtQ = null;
0893:
0894: /**
0895: * Is called when the applet wants to be resized.
0896: */
0897: public void appletResize(int width, int height) {
0898: currentAppletSize.width = width;
0899: currentAppletSize.height = height;
0900: final Dimension currentSize = new Dimension(
0901: currentAppletSize.width, currentAppletSize.height);
0902:
0903: if (loader != null) {
0904: AppContext appCtxt = loader.getAppContext();
0905: if (appCtxt != null)
0906: appEvtQ = (java.awt.EventQueue) appCtxt
0907: .get(AppContext.EVENT_QUEUE_KEY);
0908: }
0909:
0910: final AppletPanel ap = this ;
0911: if (appEvtQ != null) {
0912: appEvtQ.postEvent(new InvocationEvent(Toolkit
0913: .getDefaultToolkit(), new Runnable() {
0914: public void run() {
0915: if (ap != null) {
0916: ap.dispatchAppletEvent(APPLET_RESIZE,
0917: currentSize);
0918: }
0919: }
0920: }));
0921: }
0922: }
0923:
0924: public void setBounds(int x, int y, int width, int height) {
0925: super .setBounds(x, y, width, height);
0926: currentAppletSize.width = width;
0927: currentAppletSize.height = height;
0928: }
0929:
0930: public Applet getApplet() {
0931: return applet;
0932: }
0933:
0934: /**
0935: * Status line. Called by the AppletPanel to provide
0936: * feedback on the Applet's state.
0937: */
0938: protected void showAppletStatus(String status) {
0939: getAppletContext().showStatus(amh.getMessage(status));
0940: }
0941:
0942: protected void showAppletStatus(String status, Object arg) {
0943: getAppletContext().showStatus(amh.getMessage(status, arg));
0944: }
0945:
0946: protected void showAppletStatus(String status, Object arg1,
0947: Object arg2) {
0948: getAppletContext().showStatus(
0949: amh.getMessage(status, arg1, arg2));
0950: }
0951:
0952: /**
0953: * Called by the AppletPanel to print to the log.
0954: */
0955: protected void showAppletLog(String msg) {
0956: System.out.println(amh.getMessage(msg));
0957: }
0958:
0959: protected void showAppletLog(String msg, Object arg) {
0960: System.out.println(amh.getMessage(msg, arg));
0961: }
0962:
0963: /**
0964: * Called by the AppletPanel to provide
0965: * feedback when an exception has happened.
0966: */
0967: protected void showAppletException(Throwable t) {
0968: t.printStackTrace();
0969: repaint();
0970: }
0971:
0972: /**
0973: * Get caching key for classloader cache
0974: */
0975: public String getClassLoaderCacheKey() {
0976: /**
0977: * Fixed #4501142: Classlaoder sharing policy doesn't
0978: * take "archive" into account. This will be overridden
0979: * by Java Plug-in. [stanleyh]
0980: */
0981: return getCodeBase().toString();
0982: }
0983:
0984: /**
0985: * The class loaders
0986: */
0987: private static HashMap classloaders = new HashMap();
0988:
0989: /**
0990: * Flush a class loader.
0991: */
0992: public static synchronized void flushClassLoader(String key) {
0993: classloaders.remove(key);
0994: }
0995:
0996: /**
0997: * Flush all class loaders.
0998: */
0999: public static synchronized void flushClassLoaders() {
1000: classloaders = new HashMap();
1001: }
1002:
1003: /**
1004: * This method actually creates an AppletClassLoader.
1005: *
1006: * It can be override by subclasses (such as the Plug-in)
1007: * to provide different classloaders.
1008: */
1009: protected AppletClassLoader createClassLoader(final URL codebase) {
1010: return new AppletClassLoader(codebase);
1011: }
1012:
1013: /**
1014: * Get a class loader. Create in a restricted context
1015: */
1016: synchronized AppletClassLoader getClassLoader(final URL codebase,
1017: final String key) {
1018: AppletClassLoader c = (AppletClassLoader) classloaders.get(key);
1019: if (c == null) {
1020: AccessControlContext acc = getAccessControlContext(codebase);
1021: c = (AppletClassLoader) AccessController.doPrivileged(
1022: new PrivilegedAction() {
1023: public Object run() {
1024: AppletClassLoader ac = createClassLoader(codebase);
1025: /* Should the creation of the classloader be
1026: * within the class synchronized block? Since
1027: * this class is used by the plugin, take care
1028: * to avoid deadlocks, or specialize
1029: * AppletPanel within the plugin. It may take
1030: * an arbitrary amount of time to create a
1031: * class loader (involving getting Jar files
1032: * etc.) and may block unrelated applets from
1033: * finishing createAppletThread (due to the
1034: * class synchronization). If
1035: * createAppletThread does not finish quickly,
1036: * the applet cannot process other messages,
1037: * particularly messages such as destroy
1038: * (which timeout when called from the browser).
1039: */
1040: synchronized (getClass()) {
1041: AppletClassLoader res = (AppletClassLoader) classloaders
1042: .get(key);
1043: if (res == null) {
1044: classloaders.put(key, ac);
1045: return ac;
1046: } else {
1047: return res;
1048: }
1049: }
1050: }
1051: }, acc);
1052: }
1053: return c;
1054: }
1055:
1056: /**
1057: * get the context for the AppletClassLoader we are creating.
1058: * the context is granted permission to create the class loader,
1059: * connnect to the codebase, and whatever else the policy grants
1060: * to all codebases.
1061: */
1062: private AccessControlContext getAccessControlContext(
1063: final URL codebase) {
1064:
1065: PermissionCollection perms = (PermissionCollection) AccessController
1066: .doPrivileged(new PrivilegedAction() {
1067: public Object run() {
1068: Policy p = java.security.Policy.getPolicy();
1069: if (p != null) {
1070: return p
1071: .getPermissions(new CodeSource(
1072: null,
1073: (java.security.cert.Certificate[]) null));
1074: } else {
1075: return null;
1076: }
1077: }
1078: });
1079:
1080: if (perms == null)
1081: perms = new Permissions();
1082:
1083: //XXX: this is needed to be able to create the classloader itself!
1084:
1085: perms.add(SecurityConstants.CREATE_CLASSLOADER_PERMISSION);
1086:
1087: Permission p;
1088: java.net.URLConnection urlConnection = null;
1089: try {
1090: urlConnection = codebase.openConnection();
1091: p = urlConnection.getPermission();
1092: } catch (java.io.IOException ioe) {
1093: p = null;
1094: }
1095:
1096: if (p != null)
1097: perms.add(p);
1098:
1099: if (p instanceof FilePermission) {
1100:
1101: String path = p.getName();
1102:
1103: int endIndex = path.lastIndexOf(File.separatorChar);
1104:
1105: if (endIndex != -1) {
1106: path = path.substring(0, endIndex + 1);
1107:
1108: if (path.endsWith(File.separator)) {
1109: path += "-";
1110: }
1111: perms.add(new FilePermission(path,
1112: SecurityConstants.FILE_READ_ACTION));
1113: }
1114: } else {
1115: URL locUrl = codebase;
1116: if (urlConnection instanceof JarURLConnection) {
1117: locUrl = ((JarURLConnection) urlConnection)
1118: .getJarFileURL();
1119: }
1120: String host = locUrl.getHost();
1121: if (host != null && (host.length() > 0))
1122: perms
1123: .add(new SocketPermission(
1124: host,
1125: SecurityConstants.SOCKET_CONNECT_ACCEPT_ACTION));
1126: }
1127:
1128: ProtectionDomain domain = new ProtectionDomain(new CodeSource(
1129: codebase, (java.security.cert.Certificate[]) null),
1130: perms);
1131: AccessControlContext acc = new AccessControlContext(
1132: new ProtectionDomain[] { domain });
1133:
1134: return acc;
1135: }
1136:
1137: public Thread getAppletHandlerThread() {
1138: return handler;
1139: }
1140:
1141: public int getAppletWidth() {
1142: return currentAppletSize.width;
1143: }
1144:
1145: public int getAppletHeight() {
1146: return currentAppletSize.height;
1147: }
1148:
1149: public static void changeFrameAppContext(Frame frame,
1150: AppContext newAppContext) {
1151: // Fixed #4754451: Applet can have methods running on main
1152: // thread event queue.
1153: //
1154: // The cause of this bug is that the frame of the applet
1155: // is created in main thread group. Thus, when certain
1156: // AWT/Swing events are generated, the events will be
1157: // dispatched through the wrong event dispatch thread.
1158: //
1159: // To fix this, we rearrange the AppContext with the frame,
1160: // so the proper event queue will be looked up.
1161: //
1162: // Swing also maintains a Frame list for the AppContext,
1163: // so we will have to rearrange it as well.
1164:
1165: // Check if frame's AppContext has already been set properly
1166: AppContext oldAppContext = SunToolkit.targetToAppContext(frame);
1167:
1168: if (oldAppContext == newAppContext)
1169: return;
1170:
1171: // Synchronization on Window.class is needed for locking the
1172: // critical section of the window list in AppContext.
1173: synchronized (Window.class) {
1174: WeakReference weakRef = null;
1175: // Remove frame from the Window list in wrong AppContext
1176: {
1177: // Lookup current frame's AppContext
1178: Vector<WeakReference<Window>> windowList = (Vector<WeakReference<Window>>) oldAppContext
1179: .get(Window.class);
1180: if (windowList != null) {
1181: for (WeakReference ref : windowList) {
1182: if (ref.get() == frame) {
1183: weakRef = ref;
1184: break;
1185: }
1186: }
1187: // Remove frame from wrong AppContext
1188: if (weakRef != null)
1189: windowList.remove(weakRef);
1190: }
1191: }
1192:
1193: // Put the frame into the applet's AppContext map
1194: SunToolkit.insertTargetMapping(frame, newAppContext);
1195:
1196: // Insert frame into the Window list in the applet's AppContext map
1197: {
1198: Vector<WeakReference<Window>> windowList = (Vector) newAppContext
1199: .get(Window.class);
1200: if (windowList == null) {
1201: windowList = new Vector<WeakReference<Window>>();
1202: newAppContext.put(Window.class, windowList);
1203: }
1204: // use the same weakRef here as it is used elsewhere
1205: windowList.add(weakRef);
1206: }
1207: }
1208: }
1209:
1210: // Flag to indicate if applet is targeted for JDK 1.1.
1211: private boolean jdk11Applet = false;
1212:
1213: // Flag to indicate if applet is targeted for JDK 1.2.
1214: private boolean jdk12Applet = false;
1215:
1216: /**
1217: * Determine JDK level of an applet.
1218: */
1219: private void findAppletJDKLevel(Applet applet) {
1220: // To determine the JDK level of an applet, the
1221: // most reliable way is to check the major version
1222: // of the applet class file.
1223:
1224: // synchronized on applet class object, so calling from
1225: // different instances of the same applet will be
1226: // serialized.
1227: Class appletClass = applet.getClass();
1228:
1229: synchronized (appletClass) {
1230: // Determine if the JDK level of an applet has been
1231: // checked before.
1232: Boolean jdk11Target = (Boolean) loader
1233: .isJDK11Target(appletClass);
1234: Boolean jdk12Target = (Boolean) loader
1235: .isJDK12Target(appletClass);
1236:
1237: // if applet JDK level has been checked before, retrieve
1238: // value and return.
1239: if (jdk11Target != null || jdk12Target != null) {
1240: jdk11Applet = (jdk11Target == null) ? false
1241: : jdk11Target.booleanValue();
1242: jdk12Applet = (jdk12Target == null) ? false
1243: : jdk12Target.booleanValue();
1244: return;
1245: }
1246:
1247: String name = appletClass.getName();
1248:
1249: // first convert any '.' to '/'
1250: name = name.replace('.', '/');
1251:
1252: // append .class
1253: final String resourceName = name + ".class";
1254:
1255: InputStream is = null;
1256: byte[] classHeader = new byte[8];
1257:
1258: try {
1259: is = (InputStream) java.security.AccessController
1260: .doPrivileged(new java.security.PrivilegedAction() {
1261: public Object run() {
1262: return loader
1263: .getResourceAsStream(resourceName);
1264: }
1265: });
1266:
1267: // Read the first 8 bytes of the class file
1268: int byteRead = is.read(classHeader, 0, 8);
1269: is.close();
1270:
1271: // return if the header is not read in entirely
1272: // for some reasons.
1273: if (byteRead != 8)
1274: return;
1275: } catch (IOException e) {
1276: return;
1277: }
1278:
1279: // Check major version in class file header
1280: int major_version = readShort(classHeader, 6);
1281:
1282: // Major version in class file is as follows:
1283: // 45 - JDK 1.1
1284: // 46 - JDK 1.2
1285: // 47 - JDK 1.3
1286: // 48 - JDK 1.4
1287: // 49 - JDK 1.5
1288: if (major_version < 46)
1289: jdk11Applet = true;
1290: else if (major_version == 46)
1291: jdk12Applet = true;
1292:
1293: // Store applet JDK level in AppContext for later lookup,
1294: // e.g. page switch.
1295: loader.setJDK11Target(appletClass, jdk11Applet);
1296: loader.setJDK12Target(appletClass, jdk12Applet);
1297: }
1298: }
1299:
1300: /**
1301: * Return true if applet is targeted to JDK 1.1.
1302: */
1303: protected boolean isJDK11Applet() {
1304: return jdk11Applet;
1305: }
1306:
1307: /**
1308: * Return true if applet is targeted to JDK1.2.
1309: */
1310: protected boolean isJDK12Applet() {
1311: return jdk12Applet;
1312: }
1313:
1314: /**
1315: * Read short from byte array.
1316: */
1317: private int readShort(byte[] b, int off) {
1318: int hi = readByte(b[off]);
1319: int lo = readByte(b[off + 1]);
1320: return (hi << 8) | lo;
1321: }
1322:
1323: private int readByte(byte b) {
1324: return ((int) b) & 0xFF;
1325: }
1326:
1327: private static AppletMessageHandler amh = new AppletMessageHandler(
1328: "appletpanel");
1329: }
|