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