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