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 applet = (Applet)loader.loadCode(code).newInstance(); 775 } else { 776 String msg = "nocode"; 777 status = APPLET_ERROR; 778 showAppletStatus(msg); 779 showAppletLog(msg); 780 repaint(); 781 } 782 783 // Determine the JDK level that the applet targets. 784 // This is critical for enabling certain backward 785 // compatibility switch if an applet is a JDK 1.1 786 // applet. [stanley.ho] 787 findAppletJDKLevel(applet); 788 789 if (Thread.interrupted()) { 790 try { 791 status = APPLET_DISPOSE; // APPLET_ERROR? 792 applet = null; 793 // REMIND: This may not be exactly the right thing: the 794 // status is set by the stop button and not necessarily 795 // here. 796 showAppletStatus("death"); 797 } finally { 798 Thread.currentThread().interrupt(); // resignal interrupt 799 } 800 return null; 801 } 802 return applet; 803 } 804 805 protected void loadJarFiles(AppletClassLoader loader) throws IOException, 806 InterruptedException { 807 // Load the archives if present. 808 // REMIND - this probably should be done in a separate thread, 809 // or at least the additional archives (epll). 810 String jarFiles = getJarFiles(); 811 812 if (jarFiles != null) { 813 StringTokenizer st = new StringTokenizer(jarFiles, ",", false); 814 while(st.hasMoreTokens()) { 815 String tok = st.nextToken().trim(); 816 try { 817 loader.addJar(tok); 818 } catch (IllegalArgumentException e) { 819 // bad archive name 820 continue; 821 } 822 } 823 } 824 } 825 826 /** 827 * Request that the loading of the applet be stopped. 828 */ 829 protected synchronized void stopLoading() { 830 // REMIND: fill in the body 831 if (loaderThread != null) { 832 //System.out.println("Interrupting applet loader thread: " + loaderThread); 833 loaderThread.interrupt(); 834 } else { 835 setLoadAbortRequest(); 836 } 837 } 838 839 840 protected synchronized boolean okToLoad() { 841 return !loadAbortRequest; 842 } 843 844 protected synchronized void clearLoadAbortRequest() { 845 loadAbortRequest = false; 846 } 847 848 protected synchronized void setLoadAbortRequest() { 849 loadAbortRequest = true; 850 } 851 852 853 private synchronized void setLoaderThread(Thread loaderThread) { 854 this.loaderThread = loaderThread; 855 } 856 857 /** 858 * Return true when the applet has been started. 859 */ 860 @Override 861 public boolean isActive() { 862 return status == APPLET_START; 863 } 864 865 866 private EventQueue appEvtQ = null; 867 /** 868 * Is called when the applet wants to be resized. 869 */ 870 @Override 871 public void appletResize(int width, int height) { 872 currentAppletSize.width = width; 873 currentAppletSize.height = height; 874 final Dimension currentSize = new Dimension(currentAppletSize.width, 875 currentAppletSize.height); 876 877 if(loader != null) { 878 AppContext appCtxt = loader.getAppContext(); 879 if(appCtxt != null) 880 appEvtQ = (java.awt.EventQueue)appCtxt.get(AppContext.EVENT_QUEUE_KEY); 881 } 882 883 final AppletPanel ap = this; 884 if (appEvtQ != null){ 885 appEvtQ.postEvent(new InvocationEvent(Toolkit.getDefaultToolkit(), 886 new Runnable() { 887 @Override 888 public void run() { 889 if (ap != null) { 890 ap.dispatchAppletEvent( 891 APPLET_RESIZE, 892 currentSize); 893 } 894 } 895 })); 896 } 897 } 898 899 @Override 900 public void setBounds(int x, int y, int width, int height) { 901 super.setBounds(x, y, width, height); 902 currentAppletSize.width = width; 903 currentAppletSize.height = height; 904 } 905 906 public Applet getApplet() { 907 return applet; 908 } 909 910 /** 911 * Status line. Called by the AppletPanel to provide 912 * feedback on the Applet's state. 913 */ 914 protected void showAppletStatus(String status) { 915 getAppletContext().showStatus(amh.getMessage(status)); 916 } 917 918 protected void showAppletStatus(String status, Object arg) { 919 getAppletContext().showStatus(amh.getMessage(status, arg)); 920 } 921 protected void showAppletStatus(String status, Object arg1, Object arg2) { 922 getAppletContext().showStatus(amh.getMessage(status, arg1, arg2)); 923 } 924 925 /** 926 * Called by the AppletPanel to print to the log. 927 */ 928 protected void showAppletLog(String msg) { 929 System.out.println(amh.getMessage(msg)); 930 } 931 932 protected void showAppletLog(String msg, Object arg) { 933 System.out.println(amh.getMessage(msg, arg)); 934 } 935 936 /** 937 * Called by the AppletPanel to provide 938 * feedback when an exception has happened. 939 */ 940 protected void showAppletException(Throwable t) { 941 t.printStackTrace(); 942 repaint(); 943 } 944 945 /** 946 * Get caching key for classloader cache 947 */ 948 public String getClassLoaderCacheKey() 949 { 950 /** 951 * Fixed #4501142: Classloader sharing policy doesn't 952 * take "archive" into account. This will be overridden 953 * by Java Plug-in. [stanleyh] 954 */ 955 return getCodeBase().toString(); 956 } 957 958 /** 959 * The class loaders 960 */ 961 private static HashMap<String, AppletClassLoader> classloaders = new HashMap<>(); 962 963 /** 964 * Flush a class loader. 965 */ 966 public static synchronized void flushClassLoader(String key) { 967 classloaders.remove(key); 968 } 969 970 /** 971 * Flush all class loaders. 972 */ 973 public static synchronized void flushClassLoaders() { 974 classloaders = new HashMap<>(); 975 } 976 977 /** 978 * This method actually creates an AppletClassLoader. 979 * 980 * It can be override by subclasses (such as the Plug-in) 981 * to provide different classloaders. 982 */ 983 protected AppletClassLoader createClassLoader(final URL codebase) { 984 return new AppletClassLoader(codebase); 985 } 986 987 /** 988 * Get a class loader. Create in a restricted context 989 */ 990 synchronized AppletClassLoader getClassLoader(final URL codebase, final String key) { 991 AppletClassLoader c = classloaders.get(key); 992 if (c == null) { 993 AccessControlContext acc = 994 getAccessControlContext(codebase); 995 c = AccessController.doPrivileged( 996 new PrivilegedAction<AppletClassLoader>() { 997 @Override 998 public AppletClassLoader run() { 999 AppletClassLoader ac = createClassLoader(codebase); 1000 /* Should the creation of the classloader be 1001 * within the class synchronized block? Since 1002 * this class is used by the plugin, take care 1003 * to avoid deadlocks, or specialize 1004 * AppletPanel within the plugin. It may take 1005 * an arbitrary amount of time to create a 1006 * class loader (involving getting Jar files 1007 * etc.) and may block unrelated applets from 1008 * finishing createAppletThread (due to the 1009 * class synchronization). If 1010 * createAppletThread does not finish quickly, 1011 * the applet cannot process other messages, 1012 * particularly messages such as destroy 1013 * (which timeout when called from the browser). 1014 */ 1015 synchronized (getClass()) { 1016 AppletClassLoader res = classloaders.get(key); 1017 if (res == null) { 1018 classloaders.put(key, ac); 1019 return ac; 1020 } else { 1021 return res; 1022 } 1023 } 1024 } 1025 },acc); 1026 } 1027 return c; 1028 } 1029 1030 /** 1031 * get the context for the AppletClassLoader we are creating. 1032 * the context is granted permission to create the class loader, 1033 * connnect to the codebase, and whatever else the policy grants 1034 * to all codebases. 1035 */ 1036 private AccessControlContext getAccessControlContext(final URL codebase) { 1037 1038 PermissionCollection perms = AccessController.doPrivileged( 1039 new PrivilegedAction<PermissionCollection>() { 1040 @Override 1041 public PermissionCollection run() { 1042 Policy p = java.security.Policy.getPolicy(); 1043 if (p != null) { 1044 return p.getPermissions(new CodeSource(null, 1045 (java.security.cert.Certificate[]) null)); 1046 } else { 1047 return null; 1048 } 1049 } 1050 }); 1051 1052 if (perms == null) 1053 perms = new Permissions(); 1054 1055 //XXX: this is needed to be able to create the classloader itself! 1056 1057 perms.add(SecurityConstants.CREATE_CLASSLOADER_PERMISSION); 1058 1059 Permission p; 1060 java.net.URLConnection urlConnection = null; 1061 try { 1062 urlConnection = codebase.openConnection(); 1063 p = urlConnection.getPermission(); 1064 } catch (java.io.IOException ioe) { 1065 p = null; 1066 } 1067 1068 if (p != null) 1069 perms.add(p); 1070 1071 if (p instanceof FilePermission) { 1072 1073 String path = p.getName(); 1074 1075 int endIndex = path.lastIndexOf(File.separatorChar); 1076 1077 if (endIndex != -1) { 1078 path = path.substring(0, endIndex+1); 1079 1080 if (path.endsWith(File.separator)) { 1081 path += "-"; 1082 } 1083 perms.add(new FilePermission(path, 1084 SecurityConstants.FILE_READ_ACTION)); 1085 } 1086 } else { 1087 URL locUrl = codebase; 1088 if (urlConnection instanceof JarURLConnection) { 1089 locUrl = ((JarURLConnection)urlConnection).getJarFileURL(); 1090 } 1091 String host = locUrl.getHost(); 1092 if (host != null && (host.length() > 0)) 1093 perms.add(new SocketPermission(host, 1094 SecurityConstants.SOCKET_CONNECT_ACCEPT_ACTION)); 1095 } 1096 1097 ProtectionDomain domain = 1098 new ProtectionDomain(new CodeSource(codebase, 1099 (java.security.cert.Certificate[]) null), perms); 1100 AccessControlContext acc = 1101 new AccessControlContext(new ProtectionDomain[] { domain }); 1102 1103 return acc; 1104 } 1105 1106 public Thread getAppletHandlerThread() { 1107 return handler; 1108 } 1109 1110 public int getAppletWidth() { 1111 return currentAppletSize.width; 1112 } 1113 1114 public int getAppletHeight() { 1115 return currentAppletSize.height; 1116 } 1117 1118 public static void changeFrameAppContext(Frame frame, AppContext newAppContext) 1119 { 1120 // Fixed #4754451: Applet can have methods running on main 1121 // thread event queue. 1122 // 1123 // The cause of this bug is that the frame of the applet 1124 // is created in main thread group. Thus, when certain 1125 // AWT/Swing events are generated, the events will be 1126 // dispatched through the wrong event dispatch thread. 1127 // 1128 // To fix this, we rearrange the AppContext with the frame, 1129 // so the proper event queue will be looked up. 1130 // 1131 // Swing also maintains a Frame list for the AppContext, 1132 // so we will have to rearrange it as well. 1133 1134 // Check if frame's AppContext has already been set properly 1135 AppContext oldAppContext = SunToolkit.targetToAppContext(frame); 1136 1137 if (oldAppContext == newAppContext) 1138 return; 1139 1140 // Synchronization on Window.class is needed for locking the 1141 // critical section of the window list in AppContext. 1142 synchronized (Window.class) 1143 { 1144 WeakReference<Window> weakRef = null; 1145 // Remove frame from the Window list in wrong AppContext 1146 { 1147 // Lookup current frame's AppContext 1148 @SuppressWarnings("unchecked") 1149 Vector<WeakReference<Window>> windowList = 1150 (Vector<WeakReference<Window>>)oldAppContext.get(Window.class); 1151 if (windowList != null) { 1152 for (WeakReference<Window> ref : windowList) { 1153 if (ref.get() == frame) { 1154 weakRef = ref; 1155 break; 1156 } 1157 } 1158 // Remove frame from wrong AppContext 1159 if (weakRef != null) 1160 windowList.remove(weakRef); 1161 } 1162 } 1163 1164 // Put the frame into the applet's AppContext map 1165 SunToolkit.insertTargetMapping(frame, newAppContext); 1166 1167 // Insert frame into the Window list in the applet's AppContext map 1168 { 1169 @SuppressWarnings("unchecked") 1170 Vector<WeakReference<Window>> windowList = 1171 (Vector<WeakReference<Window>>)newAppContext.get(Window.class); 1172 if (windowList == null) { 1173 windowList = new Vector<WeakReference<Window>>(); 1174 newAppContext.put(Window.class, windowList); 1175 } 1176 // use the same weakRef here as it is used elsewhere 1177 windowList.add(weakRef); 1178 } 1179 } 1180 } 1181 1182 // Flag to indicate if applet is targeted for JDK 1.1. 1183 private boolean jdk11Applet = false; 1184 1185 // Flag to indicate if applet is targeted for JDK 1.2. 1186 private boolean jdk12Applet = false; 1187 1188 /** 1189 * Determine JDK level of an applet. 1190 */ 1191 private void findAppletJDKLevel(Applet applet) 1192 { 1193 // To determine the JDK level of an applet, the 1194 // most reliable way is to check the major version 1195 // of the applet class file. 1196 1197 // synchronized on applet class object, so calling from 1198 // different instances of the same applet will be 1199 // serialized. 1200 Class<?> appletClass = applet.getClass(); 1201 1202 synchronized(appletClass) { 1203 // Determine if the JDK level of an applet has been 1204 // checked before. 1205 Boolean jdk11Target = loader.isJDK11Target(appletClass); 1206 Boolean jdk12Target = loader.isJDK12Target(appletClass); 1207 1208 // if applet JDK level has been checked before, retrieve 1209 // value and return. 1210 if (jdk11Target != null || jdk12Target != null) { 1211 jdk11Applet = (jdk11Target == null) ? false : jdk11Target.booleanValue(); 1212 jdk12Applet = (jdk12Target == null) ? false : jdk12Target.booleanValue(); 1213 return; 1214 } 1215 1216 String name = appletClass.getName(); 1217 1218 // first convert any '.' to '/' 1219 name = name.replace('.', '/'); 1220 1221 // append .class 1222 final String resourceName = name + ".class"; 1223 1224 byte[] classHeader = new byte[8]; 1225 1226 try (InputStream is = AccessController.doPrivileged( 1227 (PrivilegedAction<InputStream>) () -> loader.getResourceAsStream(resourceName))) { 1228 1229 // Read the first 8 bytes of the class file 1230 int byteRead = is.read(classHeader, 0, 8); 1231 1232 // return if the header is not read in entirely 1233 // for some reasons. 1234 if (byteRead != 8) 1235 return; 1236 } 1237 catch (IOException e) { 1238 return; 1239 } 1240 1241 // Check major version in class file header 1242 int major_version = readShort(classHeader, 6); 1243 1244 // Major version in class file is as follows: 1245 // 45 - JDK 1.1 1246 // 46 - JDK 1.2 1247 // 47 - JDK 1.3 1248 // 48 - JDK 1.4 1249 // 49 - JDK 1.5 1250 if (major_version < 46) 1251 jdk11Applet = true; 1252 else if (major_version == 46) 1253 jdk12Applet = true; 1254 1255 // Store applet JDK level in AppContext for later lookup, 1256 // e.g. page switch. 1257 loader.setJDK11Target(appletClass, jdk11Applet); 1258 loader.setJDK12Target(appletClass, jdk12Applet); 1259 } 1260 } 1261 1262 /** 1263 * Return true if applet is targeted to JDK 1.1. 1264 */ 1265 protected boolean isJDK11Applet() { 1266 return jdk11Applet; 1267 } 1268 1269 /** 1270 * Return true if applet is targeted to JDK1.2. 1271 */ 1272 protected boolean isJDK12Applet() { 1273 return jdk12Applet; 1274 } 1275 1276 /** 1277 * Read short from byte array. 1278 */ 1279 private int readShort(byte[] b, int off) { 1280 int hi = readByte(b[off]); 1281 int lo = readByte(b[off + 1]); 1282 return (hi << 8) | lo; 1283 } 1284 1285 private int readByte(byte b) { 1286 return ((int)b) & 0xFF; 1287 } 1288 1289 1290 private static AppletMessageHandler amh = new AppletMessageHandler("appletpanel"); 1291 }