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