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.awt.util.PerformanceLogger; 47 import sun.security.util.SecurityConstants; 48 49 /** 50 * Applet panel class. The panel manages and manipulates the 51 * applet as it is being loaded. It forks a separate thread in a new 52 * thread group to call the applet's init(), start(), stop(), and 53 * destroy() methods. 54 * 55 * @author Arthur van Hoff 56 */ 57 @SuppressWarnings("serial") // JDK implementation class 58 public 59 abstract class AppletPanel extends Panel implements AppletStub, Runnable { 60 61 /** 62 * The applet (if loaded). 63 */ 64 Applet applet; 65 66 67 /** 68 * The classloader for the applet. 69 */ 70 protected AppletClassLoader loader; 71 72 /* applet event ids */ 73 public static final int APPLET_DISPOSE = 0; 74 public static final int APPLET_LOAD = 1; 75 public static final int APPLET_INIT = 2; 76 public static final int APPLET_START = 3; 77 public static final int APPLET_STOP = 4; 78 public static final int APPLET_DESTROY = 5; 79 public static final int APPLET_QUIT = 6; 80 public static final int APPLET_ERROR = 7; 81 82 /* send to the parent to force relayout */ 83 public static final int APPLET_RESIZE = 51234; 84 85 /* sent to a (distant) parent to indicate that the applet is being 86 * loaded or as completed loading 87 */ 88 public static final int APPLET_LOADING = 51235; 89 public static final int APPLET_LOADING_COMPLETED = 51236; 90 91 /** 92 * The current status. One of: 93 * APPLET_DISPOSE, 94 * APPLET_LOAD, 95 * APPLET_INIT, 96 * APPLET_START, 97 * APPLET_STOP, 98 * APPLET_DESTROY, 99 * APPLET_ERROR. 100 */ 101 protected int status; 102 103 /** 104 * The thread for the applet. 105 */ 106 protected Thread handler; 107 108 109 /** 110 * The initial applet size. 111 */ 112 Dimension defaultAppletSize = new Dimension(10, 10); 113 114 /** 115 * The current applet size. 116 */ 117 Dimension currentAppletSize = new Dimension(10, 10); 118 119 /** 120 * The thread to use during applet loading 121 */ 122 123 Thread loaderThread = null; 124 125 /** 126 * Flag to indicate that a loading has been cancelled 127 */ 128 boolean loadAbortRequest = false; 129 130 /* abstract classes */ 131 protected abstract String getCode(); 132 protected abstract String getJarFiles(); 133 134 @Override 135 public abstract int getWidth(); 136 @Override 137 public abstract int getHeight(); 138 public abstract boolean hasInitialFocus(); 139 140 private static int threadGroupNumber = 0; 141 142 protected void setupAppletAppContext() { 143 // do nothing 144 } 145 146 /* 147 * Creates a thread to run the applet. This method is called 148 * each time an applet is loaded and reloaded. 149 */ 150 synchronized void createAppletThread() { 151 // Create a thread group for the applet, and start a new 152 // thread to load the applet. 153 String nm = "applet-" + getCode(); 154 loader = getClassLoader(getCodeBase(), getClassLoaderCacheKey()); 155 loader.grab(); // Keep this puppy around! 156 157 // 4668479: Option to turn off codebase lookup in AppletClassLoader 158 // during resource requests. [stanley.ho] 159 String param = getParameter("codebase_lookup"); 160 161 if (param != null && param.equals("false")) 162 loader.setCodebaseLookup(false); 163 else 164 loader.setCodebaseLookup(true); 165 166 167 ThreadGroup appletGroup = loader.getThreadGroup(); 168 handler = new Thread(appletGroup, this, "thread " + nm, 0, false); 169 // set the context class loader for this thread 170 AccessController.doPrivileged(new PrivilegedAction<Object>() { 171 @Override 172 public Object run() { 173 handler.setContextClassLoader(loader); 174 return null; 175 } 176 }); 177 handler.start(); 178 } 179 180 void joinAppletThread() throws InterruptedException { 181 if (handler != null) { 182 handler.join(); 183 handler = null; 184 } 185 } 186 187 void release() { 188 if (loader != null) { 189 loader.release(); 190 loader = null; 191 } 192 } 193 194 /** 195 * Construct an applet viewer and start the applet. 196 */ 197 public void init() { 198 try { 199 // Get the width (if any) 200 defaultAppletSize.width = getWidth(); 201 currentAppletSize.width = defaultAppletSize.width; 202 203 // Get the height (if any) 204 defaultAppletSize.height = getHeight(); 205 currentAppletSize.height = defaultAppletSize.height; 206 207 } catch (NumberFormatException e) { 208 // Turn on the error flag and let TagAppletPanel 209 // do the right thing. 210 status = APPLET_ERROR; 211 showAppletStatus("badattribute.exception"); 212 showAppletLog("badattribute.exception"); 213 showAppletException(e); 214 } 215 216 setLayout(new BorderLayout()); 217 218 createAppletThread(); 219 } 220 221 /** 222 * Minimum size 223 */ 224 @Override 225 @SuppressWarnings("deprecation") 226 public Dimension minimumSize() { 227 return new Dimension(defaultAppletSize.width, 228 defaultAppletSize.height); 229 } 230 231 /** 232 * Preferred size 233 */ 234 @Override 235 @SuppressWarnings("deprecation") 236 public Dimension preferredSize() { 237 return new Dimension(currentAppletSize.width, 238 currentAppletSize.height); 239 } 240 241 private AppletListener listeners; 242 243 /** 244 * AppletEvent Queue 245 */ 246 private LinkedBlockingQueue<Integer> queue = null; 247 248 public synchronized void addAppletListener(AppletListener l) { 249 listeners = AppletEventMulticaster.add(listeners, l); 250 } 251 252 public synchronized void removeAppletListener(AppletListener l) { 253 listeners = AppletEventMulticaster.remove(listeners, l); 254 } 255 256 /** 257 * Dispatch event to the listeners.. 258 */ 259 public void dispatchAppletEvent(int id, Object argument) { 260 //System.out.println("SEND= " + id); 261 if (listeners != null) { 262 AppletEvent evt = new AppletEvent(this, id, argument); 263 listeners.appletStateChanged(evt); 264 } 265 } 266 267 /** 268 * Send an event. Queue it for execution by the handler thread. 269 */ 270 public void sendEvent(int id) { 271 synchronized(this) { 272 if (queue == null) { 273 //System.out.println("SEND0= " + id); 274 queue = new LinkedBlockingQueue<>(); 275 } 276 boolean inserted = queue.add(id); 277 notifyAll(); 278 } 279 if (id == APPLET_QUIT) { 280 try { 281 joinAppletThread(); // Let the applet event handler exit 282 } catch (InterruptedException e) { 283 } 284 285 // AppletClassLoader.release() must be called by a Thread 286 // not within the applet's ThreadGroup 287 if (loader == null) 288 loader = getClassLoader(getCodeBase(), getClassLoaderCacheKey()); 289 release(); 290 } 291 } 292 293 /** 294 * Get an event from the queue. 295 */ 296 synchronized AppletEvent getNextEvent() throws InterruptedException { 297 while (queue == null || queue.isEmpty()) { 298 wait(); 299 } 300 int eventId = queue.take(); 301 return new AppletEvent(this, eventId, null); 302 } 303 304 boolean emptyEventQueue() { 305 if ((queue == null) || (queue.isEmpty())) 306 return true; 307 else 308 return false; 309 } 310 311 /** 312 * This kludge is specific to get over AccessControlException thrown during 313 * Applet.stop() or destroy() when static thread is suspended. Set a flag 314 * in AppletClassLoader to indicate that an 315 * AccessControlException for RuntimePermission "modifyThread" or 316 * "modifyThreadGroup" had occurred. 317 */ 318 private void setExceptionStatus(AccessControlException e) { 319 Permission p = e.getPermission(); 320 if (p instanceof RuntimePermission) { 321 if (p.getName().startsWith("modifyThread")) { 322 if (loader == null) 323 loader = getClassLoader(getCodeBase(), getClassLoaderCacheKey()); 324 loader.setExceptionStatus(); 325 } 326 } 327 } 328 329 /** 330 * Execute applet events. 331 * Here is the state transition diagram 332 * 333 * <pre>{@literal 334 * Note: (XXX) is the action 335 * APPLET_XXX is the state 336 * (applet code loaded) --> APPLET_LOAD -- (applet init called)--> APPLET_INIT -- 337 * (applet start called) --> APPLET_START -- (applet stop called) --> APPLET_STOP -- 338 * (applet destroyed called) --> APPLET_DESTROY --> (applet gets disposed) --> 339 * APPLET_DISPOSE --> ... 340 * }</pre> 341 * 342 * In the legacy lifecycle model. The applet gets loaded, inited and started. So it stays 343 * in the APPLET_START state unless the applet goes away(refresh page or leave the page). 344 * So the applet stop method called and the applet enters APPLET_STOP state. Then if the applet 345 * is revisited, it will call applet start method and enter the APPLET_START state and stay there. 346 * 347 * In the modern lifecycle model. When the applet first time visited, it is same as legacy lifecycle 348 * model. However, when the applet page goes away. It calls applet stop method and enters APPLET_STOP 349 * state and then applet destroyed method gets called and enters APPLET_DESTROY state. 350 * 351 * This code is also called by AppletViewer. In AppletViewer "Restart" menu, the applet is jump from 352 * APPLET_STOP to APPLET_DESTROY and to APPLET_INIT . 353 * 354 * Also, the applet can jump from APPLET_INIT state to APPLET_DESTROY (in Netscape/Mozilla case). 355 * Same as APPLET_LOAD to 356 * APPLET_DISPOSE since all of this are triggered by browser. 357 * 358 */ 359 @Override 360 public void run() { 361 362 Thread curThread = Thread.currentThread(); 363 if (curThread == loaderThread) { 364 // if we are in the loader thread, cause 365 // loading to occur. We may exit this with 366 // status being APPLET_DISPOSE, APPLET_ERROR, 367 // or APPLET_LOAD 368 runLoader(); 369 return; 370 } 371 372 boolean disposed = false; 373 while (!disposed && !curThread.isInterrupted()) { 374 AppletEvent evt; 375 try { 376 evt = getNextEvent(); 377 } catch (InterruptedException e) { 378 showAppletStatus("bail"); 379 return; 380 } 381 382 //showAppletStatus("EVENT = " + evt.getID()); 383 try { 384 switch (evt.getID()) { 385 case APPLET_LOAD: 386 if (!okToLoad()) { 387 break; 388 } 389 // This complexity allows loading of applets to be 390 // interruptable. The actual thread loading runs 391 // in a separate thread, so it can be interrupted 392 // without harming the applet thread. 393 // So that we don't have to worry about 394 // concurrency issues, the main applet thread waits 395 // until the loader thread terminates. 396 // (one way or another). 397 if (loaderThread == null) { 398 setLoaderThread(new Thread(null, this, 399 "AppletLoader", 0, false)); 400 loaderThread.start(); 401 // we get to go to sleep while this runs 402 loaderThread.join(); 403 setLoaderThread(null); 404 } else { 405 // REMIND: issue an error -- this case should never 406 // occur. 407 } 408 break; 409 410 case APPLET_INIT: 411 // AppletViewer "Restart" will jump from destroy method to 412 // init, that is why we need to check status w/ APPLET_DESTROY 413 if (status != APPLET_LOAD && status != APPLET_DESTROY) { 414 showAppletStatus("notloaded"); 415 break; 416 } 417 applet.resize(defaultAppletSize); 418 419 if (PerformanceLogger.loggingEnabled()) { 420 PerformanceLogger.setTime("Applet Init"); 421 PerformanceLogger.outputLog(); 422 } 423 applet.init(); 424 425 //Need the default(fallback) font to be created in this AppContext 426 Font f = getFont(); 427 if (f == null || 428 "dialog".equals(f.getFamily().toLowerCase(Locale.ENGLISH)) && 429 f.getSize() == 12 && f.getStyle() == Font.PLAIN) { 430 setFont(new Font(Font.DIALOG, Font.PLAIN, 12)); 431 } 432 433 // Validate the applet in event dispatch thread 434 // to avoid deadlock. 435 try { 436 final AppletPanel p = this; 437 Runnable r = new Runnable() { 438 @Override 439 public void run() { 440 p.validate(); 441 } 442 }; 443 AWTAccessor.getEventQueueAccessor().invokeAndWait(applet, r); 444 } 445 catch(InterruptedException ie) { 446 } 447 catch(InvocationTargetException ite) { 448 } 449 450 status = APPLET_INIT; 451 showAppletStatus("inited"); 452 break; 453 454 case APPLET_START: 455 { 456 if (status != APPLET_INIT && status != APPLET_STOP) { 457 showAppletStatus("notinited"); 458 break; 459 } 460 applet.resize(currentAppletSize); 461 applet.start(); 462 463 // Validate and show the applet in event dispatch thread 464 // to avoid deadlock. 465 try { 466 final AppletPanel p = this; 467 final Applet a = applet; 468 Runnable r = new Runnable() { 469 @Override 470 public void run() { 471 p.validate(); 472 a.setVisible(true); 473 474 // Fix for BugTraq ID 4041703. 475 // Set the default focus for an applet. 476 if (hasInitialFocus()) { 477 setDefaultFocus(); 478 } 479 } 480 }; 481 AWTAccessor.getEventQueueAccessor().invokeAndWait(applet, r); 482 } 483 catch(InterruptedException ie) { 484 } 485 catch(InvocationTargetException ite) { 486 } 487 488 status = APPLET_START; 489 showAppletStatus("started"); 490 break; 491 } 492 493 case APPLET_STOP: 494 if (status != APPLET_START) { 495 showAppletStatus("notstarted"); 496 break; 497 } 498 status = APPLET_STOP; 499 500 // Hide the applet in event dispatch thread 501 // to avoid deadlock. 502 try { 503 final Applet a = applet; 504 Runnable r = new Runnable() { 505 @Override 506 public void run() { 507 a.setVisible(false); 508 } 509 }; 510 AWTAccessor.getEventQueueAccessor().invokeAndWait(applet, r); 511 } 512 catch(InterruptedException ie) { 513 } 514 catch(InvocationTargetException ite) { 515 } 516 517 518 // During Applet.stop(), any AccessControlException on an involved Class remains in 519 // the "memory" of the AppletClassLoader. If the same instance of the ClassLoader is 520 // reused, the same exception will occur during class loading. Set the AppletClassLoader's 521 // exceptionStatusSet flag to allow recognition of what had happened 522 // when reusing AppletClassLoader object. 523 try { 524 applet.stop(); 525 } catch (java.security.AccessControlException e) { 526 setExceptionStatus(e); 527 // rethrow exception to be handled as it normally would be. 528 throw e; 529 } 530 showAppletStatus("stopped"); 531 break; 532 533 case APPLET_DESTROY: 534 if (status != APPLET_STOP && status != APPLET_INIT) { 535 showAppletStatus("notstopped"); 536 break; 537 } 538 status = APPLET_DESTROY; 539 540 // During Applet.destroy(), any AccessControlException on an involved Class remains in 541 // the "memory" of the AppletClassLoader. If the same instance of the ClassLoader is 542 // reused, the same exception will occur during class loading. Set the AppletClassLoader's 543 // exceptionStatusSet flag to allow recognition of what had happened 544 // when reusing AppletClassLoader object. 545 try { 546 applet.destroy(); 547 } catch (java.security.AccessControlException e) { 548 setExceptionStatus(e); 549 // rethrow exception to be handled as it normally would be. 550 throw e; 551 } 552 showAppletStatus("destroyed"); 553 break; 554 555 case APPLET_DISPOSE: 556 if (status != APPLET_DESTROY && status != APPLET_LOAD) { 557 showAppletStatus("notdestroyed"); 558 break; 559 } 560 status = APPLET_DISPOSE; 561 562 try { 563 final Applet a = applet; 564 Runnable r = new Runnable() { 565 @Override 566 public void run() { 567 remove(a); 568 } 569 }; 570 AWTAccessor.getEventQueueAccessor().invokeAndWait(applet, r); 571 } 572 catch(InterruptedException ie) 573 { 574 } 575 catch(InvocationTargetException ite) 576 { 577 } 578 applet = null; 579 showAppletStatus("disposed"); 580 disposed = true; 581 break; 582 583 case APPLET_QUIT: 584 return; 585 } 586 } catch (Exception e) { 587 status = APPLET_ERROR; 588 if (e.getMessage() != null) { 589 showAppletStatus("exception2", e.getClass().getName(), 590 e.getMessage()); 591 } else { 592 showAppletStatus("exception", e.getClass().getName()); 593 } 594 showAppletException(e); 595 } catch (ThreadDeath e) { 596 showAppletStatus("death"); 597 return; 598 } catch (Error e) { 599 status = APPLET_ERROR; 600 if (e.getMessage() != null) { 601 showAppletStatus("error2", e.getClass().getName(), 602 e.getMessage()); 603 } else { 604 showAppletStatus("error", e.getClass().getName()); 605 } 606 showAppletException(e); 607 } 608 clearLoadAbortRequest(); 609 } 610 } 611 612 /** 613 * Gets most recent focus owner component associated with the given window. 614 * It does that without calling Window.getMostRecentFocusOwner since it 615 * provides its own logic contradicting with setDefautlFocus. Instead, it 616 * calls KeyboardFocusManager directly. 617 */ 618 private Component getMostRecentFocusOwnerForWindow(Window w) { 619 Method meth = AccessController.doPrivileged( 620 new PrivilegedAction<Method>() { 621 @Override 622 public Method run() { 623 Method meth = null; 624 try { 625 meth = KeyboardFocusManager.class.getDeclaredMethod( 626 "getMostRecentFocusOwner", 627 new Class<?>[]{Window.class}); 628 meth.setAccessible(true); 629 } catch (Exception e) { 630 // Must never happen 631 e.printStackTrace(); 632 } 633 return meth; 634 } 635 }); 636 if (meth != null) { 637 // Meth refers static method 638 try { 639 return (Component)meth.invoke(null, new Object[] {w}); 640 } catch (Exception e) { 641 // Must never happen 642 e.printStackTrace(); 643 } 644 } 645 // Will get here if exception was thrown or meth is null 646 return w.getMostRecentFocusOwner(); 647 } 648 649 /* 650 * Fix for BugTraq ID 4041703. 651 * Set the focus to a reasonable default for an Applet. 652 */ 653 private void setDefaultFocus() { 654 Component toFocus = null; 655 Container parent = getParent(); 656 657 if(parent != null) { 658 if (parent instanceof Window) { 659 toFocus = getMostRecentFocusOwnerForWindow((Window)parent); 660 if (toFocus == parent || toFocus == null) { 661 toFocus = parent.getFocusTraversalPolicy(). 662 getInitialComponent((Window)parent); 663 } 664 } else if (parent.isFocusCycleRoot()) { 665 toFocus = parent.getFocusTraversalPolicy(). 666 getDefaultComponent(parent); 667 } 668 } 669 670 if (toFocus != null) { 671 if (parent instanceof EmbeddedFrame) { 672 ((EmbeddedFrame) parent).synthesizeWindowActivation(true); 673 } 674 // EmbeddedFrame might have focus before the applet was added. 675 // Thus after its activation the most recent focus owner will be 676 // restored. We need the applet's initial focusabled component to 677 // be focused here. 678 toFocus.requestFocusInWindow(); 679 } 680 } 681 682 /** 683 * Load the applet into memory. 684 * Runs in a seperate (and interruptible) thread from the rest of the 685 * applet event processing so that it can be gracefully interrupted from 686 * things like HotJava. 687 */ 688 @SuppressWarnings("deprecation") 689 private void runLoader() { 690 if (status != APPLET_DISPOSE) { 691 showAppletStatus("notdisposed"); 692 return; 693 } 694 695 dispatchAppletEvent(APPLET_LOADING, null); 696 697 // REMIND -- might be cool to visually indicate loading here -- 698 // maybe do animation? 699 status = APPLET_LOAD; 700 701 // Create a class loader 702 loader = getClassLoader(getCodeBase(), getClassLoaderCacheKey()); 703 704 // Load the archives if present. 705 // REMIND - this probably should be done in a separate thread, 706 // or at least the additional archives (epll). 707 708 String code = getCode(); 709 710 // setup applet AppContext 711 // this must be called before loadJarFiles 712 setupAppletAppContext(); 713 714 try { 715 loadJarFiles(loader); 716 applet = createApplet(loader); 717 } catch (ClassNotFoundException e) { 718 status = APPLET_ERROR; 719 showAppletStatus("notfound", code); 720 showAppletLog("notfound", code); 721 showAppletException(e); 722 return; 723 } catch (InstantiationException e) { 724 status = APPLET_ERROR; 725 showAppletStatus("nocreate", code); 726 showAppletLog("nocreate", code); 727 showAppletException(e); 728 return; 729 } catch (IllegalAccessException e) { 730 status = APPLET_ERROR; 731 showAppletStatus("noconstruct", code); 732 showAppletLog("noconstruct", code); 733 showAppletException(e); 734 // sbb -- I added a return here 735 return; 736 } catch (Exception e) { 737 status = APPLET_ERROR; 738 showAppletStatus("exception", e.getMessage()); 739 showAppletException(e); 740 return; 741 } catch (ThreadDeath e) { 742 status = APPLET_ERROR; 743 showAppletStatus("death"); 744 return; 745 } catch (Error e) { 746 status = APPLET_ERROR; 747 showAppletStatus("error", e.getMessage()); 748 showAppletException(e); 749 return; 750 } finally { 751 // notify that loading is no longer going on 752 dispatchAppletEvent(APPLET_LOADING_COMPLETED, null); 753 } 754 755 // Fixed #4508194: NullPointerException thrown during 756 // quick page switch 757 // 758 if (applet != null) 759 { 760 // Stick it in the frame 761 applet.setStub(this); 762 applet.hide(); 763 add("Center", applet); 764 showAppletStatus("loaded"); 765 validate(); 766 } 767 } 768 769 protected Applet createApplet(final AppletClassLoader loader) throws ClassNotFoundException, 770 IllegalAccessException, IOException, InstantiationException, InterruptedException { 771 String code = getCode(); 772 773 if (code != null) { 774 @SuppressWarnings("deprecation") 775 Object tmp = loader.loadCode(code).newInstance(); 776 applet = (Applet)tmp; 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 }