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