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