View Javadoc
1   /*
2    * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.  Oracle designates this
8    * particular file as subject to the "Classpath" exception as provided
9    * by Oracle in the LICENSE file that accompanied this code.
10   *
11   * This code is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14   * version 2 for more details (a copy is included in the LICENSE file that
15   * accompanied this code).
16   *
17   * You should have received a copy of the GNU General Public License version
18   * 2 along with this work; if not, write to the Free Software Foundation,
19   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20   *
21   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22   * or visit www.oracle.com if you need additional information or have any
23   * questions.
24   */
25  
26  package sun.applet;
27  
28  import java.applet.*;
29  import java.awt.*;
30  import java.awt.event.*;
31  import java.awt.image.ColorModel;
32  import java.awt.image.MemoryImageSource;
33  import java.io.*;
34  import java.lang.ref.WeakReference;
35  import java.lang.reflect.InvocationTargetException;
36  import java.lang.reflect.Method;
37  import java.net.InetAddress;
38  import java.net.JarURLConnection;
39  import java.net.MalformedURLException;
40  import java.net.SocketPermission;
41  import java.net.URL;
42  import java.net.UnknownHostException;
43  import java.security.*;
44  import java.util.*;
45  import java.util.Collections;
46  import java.util.Locale;
47  import java.util.WeakHashMap;
48  import sun.awt.AWTAccessor;
49  import sun.awt.AppContext;
50  import sun.awt.EmbeddedFrame;
51  import sun.awt.SunToolkit;
52  import sun.misc.MessageUtils;
53  import sun.misc.PerformanceLogger;
54  import sun.misc.Queue;
55  import sun.security.util.SecurityConstants;
56  
57  /**
58   * Applet panel class. The panel manages and manipulates the
59   * applet as it is being loaded. It forks a separate thread in a new
60   * thread group to call the applet's init(), start(), stop(), and
61   * destroy() methods.
62   *
63   * @author      Arthur van Hoff
64   */
65  public
66  abstract class AppletPanel extends Panel implements AppletStub, Runnable {
67  
68      /**
69       * The applet (if loaded).
70       */
71      Applet applet;
72  
73      /**
74       * Applet will allow initialization.  Should be
75       * set to false if loading a serialized applet
76       * that was pickled in the init=true state.
77       */
78      protected boolean doInit = true;
79  
80  
81      /**
82       * The classloader for the applet.
83       */
84      protected AppletClassLoader loader;
85  
86      /* applet event ids */
87      public final static int APPLET_DISPOSE = 0;
88      public final static int APPLET_LOAD = 1;
89      public final static int APPLET_INIT = 2;
90      public final static int APPLET_START = 3;
91      public final static int APPLET_STOP = 4;
92      public final static int APPLET_DESTROY = 5;
93      public final static int APPLET_QUIT = 6;
94      public final static int APPLET_ERROR = 7;
95  
96      /* send to the parent to force relayout */
97      public final static int APPLET_RESIZE = 51234;
98  
99      /* sent to a (distant) parent to indicate that the applet is being
100      * loaded or as completed loading
101      */
102     public final static int APPLET_LOADING = 51235;
103     public final static int APPLET_LOADING_COMPLETED = 51236;
104 
105     /**
106      * The current status. One of:
107      *    APPLET_DISPOSE,
108      *    APPLET_LOAD,
109      *    APPLET_INIT,
110      *    APPLET_START,
111      *    APPLET_STOP,
112      *    APPLET_DESTROY,
113      *    APPLET_ERROR.
114      */
115     protected int status;
116 
117     /**
118      * The thread for the applet.
119      */
120     protected Thread handler;
121 
122 
123     /**
124      * The initial applet size.
125      */
126     Dimension defaultAppletSize = new Dimension(10, 10);
127 
128     /**
129      * The current applet size.
130      */
131     Dimension currentAppletSize = new Dimension(10, 10);
132 
133     MessageUtils mu = new MessageUtils();
134 
135     /**
136      * The thread to use during applet loading
137      */
138 
139     Thread loaderThread = null;
140 
141     /**
142      * Flag to indicate that a loading has been cancelled
143      */
144     boolean loadAbortRequest = false;
145 
146     /* abstract classes */
147     abstract protected String getCode();
148     abstract protected String getJarFiles();
149     abstract protected String getSerializedObject();
150 
151     abstract public int    getWidth();
152     abstract public int    getHeight();
153     abstract public boolean hasInitialFocus();
154 
155     private static int threadGroupNumber = 0;
156 
157     protected void setupAppletAppContext() {
158         // do nothing
159     }
160 
161     /*
162      * Creates a thread to run the applet. This method is called
163      * each time an applet is loaded and reloaded.
164      */
165     synchronized void createAppletThread() {
166         // Create a thread group for the applet, and start a new
167         // thread to load the applet.
168         String nm = "applet-" + getCode();
169         loader = getClassLoader(getCodeBase(), getClassLoaderCacheKey());
170         loader.grab(); // Keep this puppy around!
171 
172         // 4668479: Option to turn off codebase lookup in AppletClassLoader
173         // during resource requests. [stanley.ho]
174         String param = getParameter("codebase_lookup");
175 
176         if (param != null && param.equals("false"))
177             loader.setCodebaseLookup(false);
178         else
179             loader.setCodebaseLookup(true);
180 
181 
182         ThreadGroup appletGroup = loader.getThreadGroup();
183 
184         handler = new Thread(appletGroup, this, "thread " + nm);
185         // set the context class loader for this thread
186         AccessController.doPrivileged(new PrivilegedAction() {
187                 public Object run() {
188                     handler.setContextClassLoader(loader);
189                     return null;
190                 }
191             });
192         handler.start();
193     }
194 
195     void joinAppletThread() throws InterruptedException {
196         if (handler != null) {
197             handler.join();
198             handler = null;
199         }
200     }
201 
202     void release() {
203         if (loader != null) {
204             loader.release();
205             loader = null;
206         }
207     }
208 
209     /**
210      * Construct an applet viewer and start the applet.
211      */
212     public void init() {
213         try {
214             // Get the width (if any)
215             defaultAppletSize.width = getWidth();
216             currentAppletSize.width = defaultAppletSize.width;
217 
218             // Get the height (if any)
219             defaultAppletSize.height = getHeight();
220             currentAppletSize.height = defaultAppletSize.height;
221 
222         } catch (NumberFormatException e) {
223             // Turn on the error flag and let TagAppletPanel
224             // do the right thing.
225             status = APPLET_ERROR;
226             showAppletStatus("badattribute.exception");
227             showAppletLog("badattribute.exception");
228             showAppletException(e);
229         }
230 
231         setLayout(new BorderLayout());
232 
233         createAppletThread();
234     }
235 
236     /**
237      * Minimum size
238      */
239     public Dimension minimumSize() {
240         return new Dimension(defaultAppletSize.width,
241                              defaultAppletSize.height);
242     }
243 
244     /**
245      * Preferred size
246      */
247     public Dimension preferredSize() {
248         return new Dimension(currentAppletSize.width,
249                              currentAppletSize.height);
250     }
251 
252     private AppletListener listeners;
253 
254     /**
255      * AppletEvent Queue
256      */
257     private Queue queue = null;
258 
259 
260     synchronized public void addAppletListener(AppletListener l) {
261         listeners = AppletEventMulticaster.add(listeners, l);
262     }
263 
264     synchronized public void removeAppletListener(AppletListener l) {
265         listeners = AppletEventMulticaster.remove(listeners, l);
266     }
267 
268     /**
269      * Dispatch event to the listeners..
270      */
271     public void dispatchAppletEvent(int id, Object argument) {
272         //System.out.println("SEND= " + id);
273         if (listeners != null) {
274             AppletEvent evt = new AppletEvent(this, id, argument);
275             listeners.appletStateChanged(evt);
276         }
277     }
278 
279     /**
280      * Send an event. Queue it for execution by the handler thread.
281      */
282     public void sendEvent(int id) {
283         synchronized(this) {
284             if (queue == null) {
285                 //System.out.println("SEND0= " + id);
286                 queue = new Queue();
287             }
288             Integer eventId = Integer.valueOf(id);
289             queue.enqueue(eventId);
290             notifyAll();
291         }
292         if (id == APPLET_QUIT) {
293             try {
294                 joinAppletThread(); // Let the applet event handler exit
295             } catch (InterruptedException e) {
296             }
297 
298             // AppletClassLoader.release() must be called by a Thread
299             // not within the applet's ThreadGroup
300             if (loader == null)
301                 loader = getClassLoader(getCodeBase(), getClassLoaderCacheKey());
302             release();
303         }
304     }
305 
306     /**
307      * Get an event from the queue.
308      */
309     synchronized AppletEvent getNextEvent() throws InterruptedException {
310         while (queue == null || queue.isEmpty()) {
311             wait();
312         }
313         Integer eventId = (Integer)queue.dequeue();
314         return new AppletEvent(this, eventId.intValue(), null);
315     }
316 
317     boolean emptyEventQueue() {
318         if ((queue == null) || (queue.isEmpty()))
319             return true;
320         else
321             return false;
322     }
323 
324     /**
325      * This kludge is specific to get over AccessControlException thrown during
326      * Applet.stop() or destroy() when static thread is suspended.  Set a flag
327      * in AppletClassLoader to indicate that an
328      * AccessControlException for RuntimePermission "modifyThread" or
329      * "modifyThreadGroup" had occurred.
330      */
331      private void setExceptionStatus(AccessControlException e) {
332      Permission p = e.getPermission();
333      if (p instanceof RuntimePermission) {
334          if (p.getName().startsWith("modifyThread")) {
335              if (loader == null)
336                  loader = getClassLoader(getCodeBase(), getClassLoaderCacheKey());
337              loader.setExceptionStatus();
338          }
339      }
340      }
341 
342     /**
343      * Execute applet events.
344      * Here is the state transition diagram
345      *
346      *   Note: (XXX) is the action
347      *         APPLET_XXX is the state
348      *  (applet code loaded) --> APPLET_LOAD -- (applet init called)--> APPLET_INIT -- (
349      *   applet start called) --> APPLET_START -- (applet stop called) -->APPLET_STOP --(applet
350      *   destroyed called) --> APPLET_DESTROY -->(applet gets disposed) -->
351      *   APPLET_DISPOSE -->....
352      *
353      * In the legacy lifecycle model. The applet gets loaded, inited and started. So it stays
354      * in the APPLET_START state unless the applet goes away(refresh page or leave the page).
355      * So the applet stop method called and the applet enters APPLET_STOP state. Then if the applet
356      * is revisited, it will call applet start method and enter the APPLET_START state and stay there.
357      *
358      * In the modern lifecycle model. When the applet first time visited, it is same as legacy lifecycle
359      * model. However, when the applet page goes away. It calls applet stop method and enters APPLET_STOP
360      * state and then applet destroyed method gets called and enters APPLET_DESTROY state.
361      *
362      * This code is also called by AppletViewer. In AppletViewer "Restart" menu, the applet is jump from
363      * APPLET_STOP to APPLET_DESTROY and to APPLET_INIT .
364      *
365      * Also, the applet can jump from APPLET_INIT state to APPLET_DESTROY (in Netscape/Mozilla case).
366          * Same as APPLET_LOAD to
367      * APPLET_DISPOSE since all of this are triggered by browser.
368      *
369      *
370      */
371     public void run() {
372 
373         Thread curThread = Thread.currentThread();
374         if (curThread == loaderThread) {
375             // if we are in the loader thread, cause
376             // loading to occur.  We may exit this with
377             // status being APPLET_DISPOSE, APPLET_ERROR,
378             // or APPLET_LOAD
379             runLoader();
380             return;
381         }
382 
383         boolean disposed = false;
384         while (!disposed && !curThread.isInterrupted()) {
385             AppletEvent evt;
386             try {
387                 evt = getNextEvent();
388             } catch (InterruptedException e) {
389                 showAppletStatus("bail");
390                 return;
391             }
392 
393             //showAppletStatus("EVENT = " + evt.getID());
394             try {
395                 switch (evt.getID()) {
396                   case APPLET_LOAD:
397                       if (!okToLoad()) {
398                           break;
399                       }
400                       // This complexity allows loading of applets to be
401                       // interruptable.  The actual thread loading runs
402                       // in a separate thread, so it can be interrupted
403                       // without harming the applet thread.
404                       // So that we don't have to worry about
405                       // concurrency issues, the main applet thread waits
406                       // until the loader thread terminates.
407                       // (one way or another).
408                       if (loaderThread == null) {
409                           // REMIND: do we want a name?
410                           //System.out.println("------------------- loading applet");
411                           setLoaderThread(new Thread(this));
412                           loaderThread.start();
413                           // we get to go to sleep while this runs
414                           loaderThread.join();
415                           setLoaderThread(null);
416                       } else {
417                           // REMIND: issue an error -- this case should never
418                           // occur.
419                       }
420                       break;
421 
422                   case APPLET_INIT:
423                     // AppletViewer "Restart" will jump from destroy method to
424                     // init, that is why we need to check status w/ APPLET_DESTROY
425                       if (status != APPLET_LOAD && status != APPLET_DESTROY) {
426                           showAppletStatus("notloaded");
427                           break;
428                       }
429                       applet.resize(defaultAppletSize);
430                       if (doInit) {
431                           if (PerformanceLogger.loggingEnabled()) {
432                               PerformanceLogger.setTime("Applet Init");
433                               PerformanceLogger.outputLog();
434                           }
435                           applet.init();
436                       }
437 
438                       //Need the default(fallback) font to be created in this AppContext
439                       Font f = getFont();
440                       if (f == null ||
441                           "dialog".equals(f.getFamily().toLowerCase(Locale.ENGLISH)) &&
442                           f.getSize() == 12 && f.getStyle() == Font.PLAIN) {
443                           setFont(new Font(Font.DIALOG, Font.PLAIN, 12));
444                       }
445 
446                       doInit = true;    // allow restarts
447 
448                       // Validate the applet in event dispatch thread
449                       // to avoid deadlock.
450                       try {
451                           final AppletPanel p = this;
452                           Runnable r = new Runnable() {
453                               public void run() {
454                                   p.validate();
455                               }
456                           };
457                           AWTAccessor.getEventQueueAccessor().invokeAndWait(applet, r);
458                       }
459                       catch(InterruptedException ie) {
460                       }
461                       catch(InvocationTargetException ite) {
462                       }
463 
464                       status = APPLET_INIT;
465                       showAppletStatus("inited");
466                       break;
467 
468                   case APPLET_START:
469                   {
470                       if (status != APPLET_INIT && status != APPLET_STOP) {
471                           showAppletStatus("notinited");
472                           break;
473                       }
474                       applet.resize(currentAppletSize);
475                       applet.start();
476 
477                       // Validate and show the applet in event dispatch thread
478                       // to avoid deadlock.
479                       try {
480                           final AppletPanel p = this;
481                           final Applet a = applet;
482                           Runnable r = new Runnable() {
483                               public void run() {
484                                   p.validate();
485                                   a.setVisible(true);
486 
487                                   // Fix for BugTraq ID 4041703.
488                                   // Set the default focus for an applet.
489                                   if (hasInitialFocus()) {
490                                       setDefaultFocus();
491                                   }
492                               }
493                           };
494                           AWTAccessor.getEventQueueAccessor().invokeAndWait(applet, r);
495                       }
496                       catch(InterruptedException ie) {
497                       }
498                       catch(InvocationTargetException ite) {
499                       }
500 
501                       status = APPLET_START;
502                       showAppletStatus("started");
503                       break;
504                   }
505 
506                 case APPLET_STOP:
507                     if (status != APPLET_START) {
508                         showAppletStatus("notstarted");
509                         break;
510                     }
511                     status = APPLET_STOP;
512 
513                     // Hide the applet in event dispatch thread
514                     // to avoid deadlock.
515                     try {
516                         final Applet a = applet;
517                         Runnable r = new Runnable() {
518                             public void run() {
519                                 a.setVisible(false);
520                             }
521                         };
522                         AWTAccessor.getEventQueueAccessor().invokeAndWait(applet, r);
523                     }
524                     catch(InterruptedException ie) {
525                     }
526                     catch(InvocationTargetException ite) {
527                     }
528 
529 
530                     // During Applet.stop(), any AccessControlException on an involved Class remains in
531                     // the "memory" of the AppletClassLoader.  If the same instance of the ClassLoader is
532                     // reused, the same exception will occur during class loading.  Set the AppletClassLoader's
533                     // exceptionStatusSet flag to allow recognition of what had happened
534                     // when reusing AppletClassLoader object.
535                     try {
536                         applet.stop();
537                     } catch (java.security.AccessControlException e) {
538                         setExceptionStatus(e);
539                         // rethrow exception to be handled as it normally would be.
540                         throw e;
541                     }
542                     showAppletStatus("stopped");
543                     break;
544 
545                 case APPLET_DESTROY:
546                     if (status != APPLET_STOP && status != APPLET_INIT) {
547                         showAppletStatus("notstopped");
548                         break;
549                     }
550                     status = APPLET_DESTROY;
551 
552                     // During Applet.destroy(), any AccessControlException on an involved Class remains in
553                     // the "memory" of the AppletClassLoader.  If the same instance of the ClassLoader is
554                     // reused, the same exception will occur during class loading.  Set the AppletClassLoader's
555                     // exceptionStatusSet flag to allow recognition of what had happened
556                     // when reusing AppletClassLoader object.
557                     try {
558                         applet.destroy();
559                     } catch (java.security.AccessControlException e) {
560                         setExceptionStatus(e);
561                         // rethrow exception to be handled as it normally would be.
562                         throw e;
563                     }
564                     showAppletStatus("destroyed");
565                     break;
566 
567                 case APPLET_DISPOSE:
568                     if (status != APPLET_DESTROY && status != APPLET_LOAD) {
569                         showAppletStatus("notdestroyed");
570                         break;
571                     }
572                     status = APPLET_DISPOSE;
573 
574                     try {
575                         final Applet a = applet;
576                         Runnable r = new Runnable() {
577                             public void run() {
578                                 remove(a);
579                             }
580                         };
581                         AWTAccessor.getEventQueueAccessor().invokeAndWait(applet, r);
582                     }
583                     catch(InterruptedException ie)
584                     {
585                     }
586                     catch(InvocationTargetException ite)
587                     {
588                     }
589                     applet = null;
590                     showAppletStatus("disposed");
591                     disposed = true;
592                     break;
593 
594                 case APPLET_QUIT:
595                     return;
596                 }
597             } catch (Exception e) {
598                 status = APPLET_ERROR;
599                 if (e.getMessage() != null) {
600                     showAppletStatus("exception2", e.getClass().getName(),
601                                      e.getMessage());
602                 } else {
603                     showAppletStatus("exception", e.getClass().getName());
604                 }
605                 showAppletException(e);
606             } catch (ThreadDeath e) {
607                 showAppletStatus("death");
608                 return;
609             } catch (Error e) {
610                 status = APPLET_ERROR;
611                 if (e.getMessage() != null) {
612                     showAppletStatus("error2", e.getClass().getName(),
613                                      e.getMessage());
614                 } else {
615                     showAppletStatus("error", e.getClass().getName());
616                 }
617                 showAppletException(e);
618             }
619             clearLoadAbortRequest();
620         }
621     }
622 
623     /**
624      * Gets most recent focus owner component associated with the given window.
625      * It does that without calling Window.getMostRecentFocusOwner since it
626      * provides its own logic contradicting with setDefautlFocus. Instead, it
627      * calls KeyboardFocusManager directly.
628      */
629     private Component getMostRecentFocusOwnerForWindow(Window w) {
630         Method meth = (Method)AccessController.doPrivileged(new PrivilegedAction() {
631                 public Object run() {
632                     Method meth = null;
633                     try {
634                         meth = KeyboardFocusManager.class.getDeclaredMethod("getMostRecentFocusOwner", new Class[] {Window.class});
635                         meth.setAccessible(true);
636                     } catch (Exception e) {
637                         // Must never happen
638                         e.printStackTrace();
639                     }
640                     return meth;
641                 }
642             });
643         if (meth != null) {
644             // Meth refers static method
645             try {
646                 return (Component)meth.invoke(null, new Object[] {w});
647             } catch (Exception e) {
648                 // Must never happen
649                 e.printStackTrace();
650             }
651         }
652         // Will get here if exception was thrown or meth is null
653         return w.getMostRecentFocusOwner();
654     }
655 
656     /*
657      * Fix for BugTraq ID 4041703.
658      * Set the focus to a reasonable default for an Applet.
659      */
660     private void setDefaultFocus() {
661         Component toFocus = null;
662         Container parent = getParent();
663 
664         if(parent != null) {
665             if (parent instanceof Window) {
666                 toFocus = getMostRecentFocusOwnerForWindow((Window)parent);
667                 if (toFocus == parent || toFocus == null) {
668                     toFocus = parent.getFocusTraversalPolicy().
669                         getInitialComponent((Window)parent);
670                 }
671             } else if (parent.isFocusCycleRoot()) {
672                 toFocus = parent.getFocusTraversalPolicy().
673                     getDefaultComponent(parent);
674             }
675         }
676 
677         if (toFocus != null) {
678             if (parent instanceof EmbeddedFrame) {
679                 ((EmbeddedFrame)parent).synthesizeWindowActivation(true);
680             }
681             // EmbeddedFrame might have focus before the applet was added.
682             // Thus after its activation the most recent focus owner will be
683             // restored. We need the applet's initial focusabled component to
684             // be focused here.
685             toFocus.requestFocusInWindow();
686         }
687     }
688 
689     /**
690      * Load the applet into memory.
691      * Runs in a seperate (and interruptible) thread from the rest of the
692      * applet event processing so that it can be gracefully interrupted from
693      * things like HotJava.
694      */
695     private void runLoader() {
696         if (status != APPLET_DISPOSE) {
697             showAppletStatus("notdisposed");
698             return;
699         }
700 
701         dispatchAppletEvent(APPLET_LOADING, null);
702 
703         // REMIND -- might be cool to visually indicate loading here --
704         // maybe do animation?
705         status = APPLET_LOAD;
706 
707         // Create a class loader
708         loader = getClassLoader(getCodeBase(), getClassLoaderCacheKey());
709 
710         // Load the archives if present.
711         // REMIND - this probably should be done in a separate thread,
712         // or at least the additional archives (epll).
713 
714         String code = getCode();
715 
716         // setup applet AppContext
717         // this must be called before loadJarFiles
718         setupAppletAppContext();
719 
720         try {
721             loadJarFiles(loader);
722             applet = createApplet(loader);
723         } catch (ClassNotFoundException e) {
724             status = APPLET_ERROR;
725             showAppletStatus("notfound", code);
726             showAppletLog("notfound", code);
727             showAppletException(e);
728             return;
729         } catch (InstantiationException e) {
730             status = APPLET_ERROR;
731             showAppletStatus("nocreate", code);
732             showAppletLog("nocreate", code);
733             showAppletException(e);
734             return;
735         } catch (IllegalAccessException e) {
736             status = APPLET_ERROR;
737             showAppletStatus("noconstruct", code);
738             showAppletLog("noconstruct", code);
739             showAppletException(e);
740             // sbb -- I added a return here
741             return;
742         } catch (Exception e) {
743             status = APPLET_ERROR;
744             showAppletStatus("exception", e.getMessage());
745             showAppletException(e);
746             return;
747         } catch (ThreadDeath e) {
748             status = APPLET_ERROR;
749             showAppletStatus("death");
750             return;
751         } catch (Error e) {
752             status = APPLET_ERROR;
753             showAppletStatus("error", e.getMessage());
754             showAppletException(e);
755             return;
756         } finally {
757             // notify that loading is no longer going on
758             dispatchAppletEvent(APPLET_LOADING_COMPLETED, null);
759         }
760 
761         // Fixed #4508194: NullPointerException thrown during
762         // quick page switch
763         //
764         if (applet != null)
765         {
766             // Stick it in the frame
767             applet.setStub(this);
768             applet.hide();
769             add("Center", applet);
770             showAppletStatus("loaded");
771             validate();
772         }
773     }
774 
775     protected Applet createApplet(final AppletClassLoader loader) throws ClassNotFoundException,
776                                                                          IllegalAccessException, IOException, InstantiationException, InterruptedException {
777         final String serName = getSerializedObject();
778         String code = getCode();
779 
780         if (code != null && serName != null) {
781             System.err.println(amh.getMessage("runloader.err"));
782 //          return null;
783             throw new InstantiationException("Either \"code\" or \"object\" should be specified, but not both.");
784         }
785         if (code == null && serName == null) {
786             String msg = "nocode";
787             status = APPLET_ERROR;
788             showAppletStatus(msg);
789             showAppletLog(msg);
790             repaint();
791         }
792         if (code != null) {
793             applet = (Applet)loader.loadCode(code).newInstance();
794             doInit = true;
795         } else {
796             // serName is not null;
797             try (InputStream is = AccessController.doPrivileged(
798                     (PrivilegedAction<InputStream>)() -> loader.getResourceAsStream(serName));
799                  ObjectInputStream ois = new AppletObjectInputStream(is, loader)) {
800 
801                 applet = (Applet) ois.readObject();
802                 doInit = false; // skip over the first init
803             }
804         }
805 
806         // Determine the JDK level that the applet targets.
807         // This is critical for enabling certain backward
808         // compatibility switch if an applet is a JDK 1.1
809         // applet. [stanley.ho]
810         findAppletJDKLevel(applet);
811 
812         if (Thread.interrupted()) {
813             try {
814                 status = APPLET_DISPOSE; // APPLET_ERROR?
815                 applet = null;
816                 // REMIND: This may not be exactly the right thing: the
817                 // status is set by the stop button and not necessarily
818                 // here.
819                 showAppletStatus("death");
820             } finally {
821                 Thread.currentThread().interrupt(); // resignal interrupt
822             }
823             return null;
824         }
825         return applet;
826     }
827 
828     protected void loadJarFiles(AppletClassLoader loader) throws IOException,
829                                                                  InterruptedException {
830         // Load the archives if present.
831         // REMIND - this probably should be done in a separate thread,
832         // or at least the additional archives (epll).
833         String jarFiles = getJarFiles();
834 
835         if (jarFiles != null) {
836             StringTokenizer st = new StringTokenizer(jarFiles, ",", false);
837             while(st.hasMoreTokens()) {
838                 String tok = st.nextToken().trim();
839                 try {
840                     loader.addJar(tok);
841                 } catch (IllegalArgumentException e) {
842                     // bad archive name
843                     continue;
844                 }
845             }
846         }
847     }
848 
849     /**
850      * Request that the loading of the applet be stopped.
851      */
852     protected synchronized void stopLoading() {
853         // REMIND: fill in the body
854         if (loaderThread != null) {
855             //System.out.println("Interrupting applet loader thread: " + loaderThread);
856             loaderThread.interrupt();
857         } else {
858             setLoadAbortRequest();
859         }
860     }
861 
862 
863     protected synchronized boolean okToLoad() {
864         return !loadAbortRequest;
865     }
866 
867     protected synchronized void clearLoadAbortRequest() {
868         loadAbortRequest = false;
869     }
870 
871     protected synchronized void setLoadAbortRequest() {
872         loadAbortRequest = true;
873     }
874 
875 
876     private synchronized void setLoaderThread(Thread loaderThread) {
877         this.loaderThread = loaderThread;
878     }
879 
880     /**
881      * Return true when the applet has been started.
882      */
883     public boolean isActive() {
884         return status == APPLET_START;
885     }
886 
887 
888     private EventQueue appEvtQ = null;
889     /**
890      * Is called when the applet wants to be resized.
891      */
892     public void appletResize(int width, int height) {
893         currentAppletSize.width = width;
894         currentAppletSize.height = height;
895         final Dimension currentSize = new Dimension(currentAppletSize.width,
896                                                     currentAppletSize.height);
897 
898         if(loader != null) {
899             AppContext appCtxt = loader.getAppContext();
900             if(appCtxt != null)
901                 appEvtQ = (java.awt.EventQueue)appCtxt.get(AppContext.EVENT_QUEUE_KEY);
902         }
903 
904         final AppletPanel ap = this;
905         if (appEvtQ != null){
906             appEvtQ.postEvent(new InvocationEvent(Toolkit.getDefaultToolkit(),
907                                                   new Runnable(){
908                                                       public void run(){
909                                                           if(ap != null)
910                                                           {
911                                                               ap.dispatchAppletEvent(APPLET_RESIZE, currentSize);
912                                                           }
913                                                       }
914                                                   }));
915         }
916     }
917 
918     public void setBounds(int x, int y, int width, int height) {
919         super.setBounds(x, y, width, height);
920         currentAppletSize.width = width;
921         currentAppletSize.height = height;
922     }
923 
924     public Applet getApplet() {
925         return applet;
926     }
927 
928     /**
929      * Status line. Called by the AppletPanel to provide
930      * feedback on the Applet's state.
931      */
932     protected void showAppletStatus(String status) {
933         getAppletContext().showStatus(amh.getMessage(status));
934     }
935 
936     protected void showAppletStatus(String status, Object arg) {
937         getAppletContext().showStatus(amh.getMessage(status, arg));
938     }
939     protected void showAppletStatus(String status, Object arg1, Object arg2) {
940         getAppletContext().showStatus(amh.getMessage(status, arg1, arg2));
941     }
942 
943     /**
944      * Called by the AppletPanel to print to the log.
945      */
946     protected void showAppletLog(String msg) {
947         System.out.println(amh.getMessage(msg));
948     }
949 
950     protected void showAppletLog(String msg, Object arg) {
951         System.out.println(amh.getMessage(msg, arg));
952     }
953 
954     /**
955      * Called by the AppletPanel to provide
956      * feedback when an exception has happened.
957      */
958     protected void showAppletException(Throwable t) {
959         t.printStackTrace();
960         repaint();
961     }
962 
963     /**
964      * Get caching key for classloader cache
965      */
966     public String getClassLoaderCacheKey()
967     {
968         /**
969          * Fixed #4501142: Classlaoder sharing policy doesn't
970          * take "archive" into account. This will be overridden
971          * by Java Plug-in.                     [stanleyh]
972          */
973         return getCodeBase().toString();
974     }
975 
976     /**
977      * The class loaders
978      */
979     private static HashMap classloaders = new HashMap();
980 
981     /**
982      * Flush a class loader.
983      */
984     public static synchronized void flushClassLoader(String key) {
985         classloaders.remove(key);
986     }
987 
988     /**
989      * Flush all class loaders.
990      */
991     public static synchronized void flushClassLoaders() {
992         classloaders = new HashMap();
993     }
994 
995     /**
996      * This method actually creates an AppletClassLoader.
997      *
998      * It can be override by subclasses (such as the Plug-in)
999      * to provide different classloaders.
1000      */
1001     protected AppletClassLoader createClassLoader(final URL codebase) {
1002         return new AppletClassLoader(codebase);
1003     }
1004 
1005     /**
1006      * Get a class loader. Create in a restricted context
1007      */
1008     synchronized AppletClassLoader getClassLoader(final URL codebase, final String key) {
1009         AppletClassLoader c = (AppletClassLoader)classloaders.get(key);
1010         if (c == null) {
1011             AccessControlContext acc =
1012                 getAccessControlContext(codebase);
1013             c = (AppletClassLoader)
1014                 AccessController.doPrivileged(new PrivilegedAction() {
1015                         public Object run() {
1016                             AppletClassLoader ac = createClassLoader(codebase);
1017                             /* Should the creation of the classloader be
1018                              * within the class synchronized block?  Since
1019                              * this class is used by the plugin, take care
1020                              * to avoid deadlocks, or specialize
1021                              * AppletPanel within the plugin.  It may take
1022                              * an arbitrary amount of time to create a
1023                              * class loader (involving getting Jar files
1024                              * etc.) and may block unrelated applets from
1025                              * finishing createAppletThread (due to the
1026                              * class synchronization). If
1027                              * createAppletThread does not finish quickly,
1028                              * the applet cannot process other messages,
1029                              * particularly messages such as destroy
1030                              * (which timeout when called from the browser).
1031                              */
1032                             synchronized (getClass()) {
1033                                 AppletClassLoader res =
1034                                     (AppletClassLoader)classloaders.get(key);
1035                                 if (res == null) {
1036                                     classloaders.put(key, ac);
1037                                     return ac;
1038                                 } else {
1039                                     return res;
1040                                 }
1041                             }
1042                         }
1043                     },acc);
1044         }
1045         return c;
1046     }
1047 
1048     /**
1049      * get the context for the AppletClassLoader we are creating.
1050      * the context is granted permission to create the class loader,
1051      * connnect to the codebase, and whatever else the policy grants
1052      * to all codebases.
1053      */
1054     private AccessControlContext getAccessControlContext(final URL codebase) {
1055 
1056         PermissionCollection perms = (PermissionCollection)
1057             AccessController.doPrivileged(new PrivilegedAction() {
1058                     public Object run() {
1059                         Policy p = java.security.Policy.getPolicy();
1060                         if (p != null) {
1061                             return p.getPermissions(new CodeSource(null,
1062                                                                    (java.security.cert.Certificate[]) null));
1063                         } else {
1064                             return null;
1065                         }
1066                     }
1067                 });
1068 
1069         if (perms == null)
1070             perms = new Permissions();
1071 
1072         //XXX: this is needed to be able to create the classloader itself!
1073 
1074         perms.add(SecurityConstants.CREATE_CLASSLOADER_PERMISSION);
1075 
1076         Permission p;
1077         java.net.URLConnection urlConnection = null;
1078         try {
1079             urlConnection = codebase.openConnection();
1080             p = urlConnection.getPermission();
1081         } catch (java.io.IOException ioe) {
1082             p = null;
1083         }
1084 
1085         if (p != null)
1086             perms.add(p);
1087 
1088         if (p instanceof FilePermission) {
1089 
1090             String path = p.getName();
1091 
1092             int endIndex = path.lastIndexOf(File.separatorChar);
1093 
1094             if (endIndex != -1) {
1095                 path = path.substring(0, endIndex+1);
1096 
1097                 if (path.endsWith(File.separator)) {
1098                     path += "-";
1099                 }
1100                 perms.add(new FilePermission(path,
1101                                              SecurityConstants.FILE_READ_ACTION));
1102             }
1103         } else {
1104             URL locUrl = codebase;
1105             if (urlConnection instanceof JarURLConnection) {
1106                 locUrl = ((JarURLConnection)urlConnection).getJarFileURL();
1107             }
1108             String host = locUrl.getHost();
1109             if (host != null && (host.length() > 0))
1110                 perms.add(new SocketPermission(host,
1111                                                SecurityConstants.SOCKET_CONNECT_ACCEPT_ACTION));
1112         }
1113 
1114         ProtectionDomain domain =
1115             new ProtectionDomain(new CodeSource(codebase,
1116                                                 (java.security.cert.Certificate[]) null), perms);
1117         AccessControlContext acc =
1118             new AccessControlContext(new ProtectionDomain[] { domain });
1119 
1120         return acc;
1121     }
1122 
1123     public Thread getAppletHandlerThread() {
1124         return handler;
1125     }
1126 
1127     public int getAppletWidth() {
1128         return currentAppletSize.width;
1129     }
1130 
1131     public int getAppletHeight() {
1132         return currentAppletSize.height;
1133     }
1134 
1135     public static void changeFrameAppContext(Frame frame, AppContext newAppContext)
1136     {
1137         // Fixed #4754451: Applet can have methods running on main
1138         // thread event queue.
1139         //
1140         // The cause of this bug is that the frame of the applet
1141         // is created in main thread group. Thus, when certain
1142         // AWT/Swing events are generated, the events will be
1143         // dispatched through the wrong event dispatch thread.
1144         //
1145         // To fix this, we rearrange the AppContext with the frame,
1146         // so the proper event queue will be looked up.
1147         //
1148         // Swing also maintains a Frame list for the AppContext,
1149         // so we will have to rearrange it as well.
1150 
1151         // Check if frame's AppContext has already been set properly
1152         AppContext oldAppContext = SunToolkit.targetToAppContext(frame);
1153 
1154         if (oldAppContext == newAppContext)
1155             return;
1156 
1157         // Synchronization on Window.class is needed for locking the
1158         // critical section of the window list in AppContext.
1159         synchronized (Window.class)
1160         {
1161             WeakReference weakRef = null;
1162             // Remove frame from the Window list in wrong AppContext
1163             {
1164                 // Lookup current frame's AppContext
1165                 Vector<WeakReference<Window>> windowList = (Vector<WeakReference<Window>>)oldAppContext.get(Window.class);
1166                 if (windowList != null) {
1167                     for (WeakReference ref : windowList) {
1168                         if (ref.get() == frame) {
1169                             weakRef = ref;
1170                             break;
1171                         }
1172                     }
1173                     // Remove frame from wrong AppContext
1174                     if (weakRef != null)
1175                         windowList.remove(weakRef);
1176                 }
1177             }
1178 
1179             // Put the frame into the applet's AppContext map
1180             SunToolkit.insertTargetMapping(frame, newAppContext);
1181 
1182             // Insert frame into the Window list in the applet's AppContext map
1183             {
1184                 Vector<WeakReference<Window>> windowList = (Vector)newAppContext.get(Window.class);
1185                 if (windowList == null) {
1186                     windowList = new Vector<WeakReference<Window>>();
1187                     newAppContext.put(Window.class, windowList);
1188                 }
1189                 // use the same weakRef here as it is used elsewhere
1190                 windowList.add(weakRef);
1191             }
1192         }
1193     }
1194 
1195     // Flag to indicate if applet is targeted for JDK 1.1.
1196     private boolean jdk11Applet = false;
1197 
1198     // Flag to indicate if applet is targeted for JDK 1.2.
1199     private boolean jdk12Applet = false;
1200 
1201     /**
1202      * Determine JDK level of an applet.
1203      */
1204     private void findAppletJDKLevel(Applet applet)
1205     {
1206         // To determine the JDK level of an applet, the
1207         // most reliable way is to check the major version
1208         // of the applet class file.
1209 
1210         // synchronized on applet class object, so calling from
1211         // different instances of the same applet will be
1212         // serialized.
1213         Class appletClass = applet.getClass();
1214 
1215         synchronized(appletClass)  {
1216             // Determine if the JDK level of an applet has been
1217             // checked before.
1218             Boolean jdk11Target = (Boolean) loader.isJDK11Target(appletClass);
1219             Boolean jdk12Target = (Boolean) loader.isJDK12Target(appletClass);
1220 
1221             // if applet JDK level has been checked before, retrieve
1222             // value and return.
1223             if (jdk11Target != null || jdk12Target != null) {
1224                 jdk11Applet = (jdk11Target == null) ? false : jdk11Target.booleanValue();
1225                 jdk12Applet = (jdk12Target == null) ? false : jdk12Target.booleanValue();
1226                 return;
1227             }
1228 
1229             String name = appletClass.getName();
1230 
1231             // first convert any '.' to '/'
1232             name = name.replace('.', '/');
1233 
1234             // append .class
1235             final String resourceName = name + ".class";
1236 
1237             byte[] classHeader = new byte[8];
1238 
1239             try (InputStream is = AccessController.doPrivileged(
1240                     (PrivilegedAction<InputStream>) () -> loader.getResourceAsStream(resourceName))) {
1241 
1242                 // Read the first 8 bytes of the class file
1243                 int byteRead = is.read(classHeader, 0, 8);
1244 
1245                 // return if the header is not read in entirely
1246                 // for some reasons.
1247                 if (byteRead != 8)
1248                     return;
1249             }
1250             catch (IOException e)   {
1251                 return;
1252             }
1253 
1254             // Check major version in class file header
1255             int major_version = readShort(classHeader, 6);
1256 
1257             // Major version in class file is as follows:
1258             //   45 - JDK 1.1
1259             //   46 - JDK 1.2
1260             //   47 - JDK 1.3
1261             //   48 - JDK 1.4
1262             //   49 - JDK 1.5
1263             if (major_version < 46)
1264                 jdk11Applet = true;
1265             else if (major_version == 46)
1266                 jdk12Applet = true;
1267 
1268             // Store applet JDK level in AppContext for later lookup,
1269             // e.g. page switch.
1270             loader.setJDK11Target(appletClass, jdk11Applet);
1271             loader.setJDK12Target(appletClass, jdk12Applet);
1272         }
1273     }
1274 
1275     /**
1276      * Return true if applet is targeted to JDK 1.1.
1277      */
1278     protected boolean isJDK11Applet()   {
1279         return jdk11Applet;
1280     }
1281 
1282     /**
1283      * Return true if applet is targeted to JDK1.2.
1284      */
1285     protected boolean isJDK12Applet()   {
1286         return jdk12Applet;
1287     }
1288 
1289     /**
1290      * Read short from byte array.
1291      */
1292     private int readShort(byte[] b, int off)    {
1293         int hi = readByte(b[off]);
1294         int lo = readByte(b[off + 1]);
1295         return (hi << 8) | lo;
1296     }
1297 
1298     private int readByte(byte b) {
1299         return ((int)b) & 0xFF;
1300     }
1301 
1302 
1303     private static AppletMessageHandler amh = new AppletMessageHandler("appletpanel");
1304 }