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.lang.NullPointerException; 29 import java.net.URL; 30 import java.net.URLClassLoader; 31 import java.net.SocketPermission; 32 import java.net.URLConnection; 33 import java.net.MalformedURLException; 34 import java.net.InetAddress; 35 import java.net.UnknownHostException; 36 import java.io.EOFException; 37 import java.io.File; 38 import java.io.FilePermission; 39 import java.io.IOException; 40 import java.io.BufferedInputStream; 41 import java.io.InputStream; 42 import java.util.Enumeration; 43 import java.util.HashMap; 44 import java.util.NoSuchElementException; 45 import java.security.AccessController; 46 import java.security.AccessControlContext; 47 import java.security.PrivilegedAction; 48 import java.security.PrivilegedExceptionAction; 49 import java.security.PrivilegedActionException; 50 import java.security.CodeSource; 51 import java.security.Permission; 52 import java.security.PermissionCollection; 53 import sun.awt.AppContext; 54 import sun.awt.SunToolkit; 55 import sun.net.www.ParseUtil; 56 import sun.security.util.SecurityConstants; 57 58 /** 59 * This class defines the class loader for loading applet classes and 60 * resources. It extends URLClassLoader to search the applet code base 61 * for the class or resource after checking any loaded JAR files. 62 */ 63 public class AppletClassLoader extends URLClassLoader { 64 private URL base; /* applet code base URL */ 65 private CodeSource codesource; /* codesource for the base URL */ 66 private AccessControlContext acc; 67 private boolean exceptionStatus = false; 68 69 private final Object threadGroupSynchronizer = new Object(); 70 private final Object grabReleaseSynchronizer = new Object(); 71 72 private boolean codebaseLookup = true; 73 private volatile boolean allowRecursiveDirectoryRead = true; 74 75 /* 76 * Creates a new AppletClassLoader for the specified base URL. 77 */ 78 protected AppletClassLoader(URL base) { 79 super(new URL[0]); 80 this.base = base; 81 this.codesource = 82 new CodeSource(base, (java.security.cert.Certificate[]) null); 83 acc = AccessController.getContext(); 84 } 85 86 public void disableRecursiveDirectoryRead() { 87 allowRecursiveDirectoryRead = false; 88 } 89 90 91 /** 92 * Set the codebase lookup flag. 93 */ 94 void setCodebaseLookup(boolean codebaseLookup) { 95 this.codebaseLookup = codebaseLookup; 96 } 97 98 /* 99 * Returns the applet code base URL. 100 */ 101 URL getBaseURL() { 102 return base; 103 } 104 105 /* 106 * Returns the URLs used for loading classes and resources. 107 */ 108 public URL[] getURLs() { 109 URL[] jars = super.getURLs(); 110 URL[] urls = new URL[jars.length + 1]; 111 System.arraycopy(jars, 0, urls, 0, jars.length); 112 urls[urls.length - 1] = base; 113 return urls; 114 } 115 116 /* 117 * Adds the specified JAR file to the search path of loaded JAR files. 118 * Changed modifier to protected in order to be able to overwrite addJar() 119 * in PluginClassLoader.java 120 */ 121 protected void addJar(String name) throws IOException { 122 URL url; 123 try { 124 url = new URL(base, name); 125 } catch (MalformedURLException e) { 126 throw new IllegalArgumentException("name"); 127 } 128 addURL(url); 129 // DEBUG 130 //URL[] urls = getURLs(); 131 //for (int i = 0; i < urls.length; i++) { 132 // System.out.println("url[" + i + "] = " + urls[i]); 133 //} 134 } 135 136 /* 137 * Override loadClass so that class loading errors can be caught in 138 * order to print better error messages. 139 */ 140 public synchronized Class<?> loadClass(String name, boolean resolve) 141 throws ClassNotFoundException 142 { 143 // First check if we have permission to access the package. This 144 // should go away once we've added support for exported packages. 145 int i = name.lastIndexOf('.'); 146 if (i != -1) { 147 SecurityManager sm = System.getSecurityManager(); 148 if (sm != null) 149 sm.checkPackageAccess(name.substring(0, i)); 150 } 151 try { 152 return super.loadClass(name, resolve); 153 } catch (ClassNotFoundException e) { 154 //printError(name, e.getException()); 155 throw e; 156 } catch (RuntimeException e) { 157 //printError(name, e); 158 throw e; 159 } catch (Error e) { 160 //printError(name, e); 161 throw e; 162 } 163 } 164 165 /* 166 * Finds the applet class with the specified name. First searches 167 * loaded JAR files then the applet code base for the class. 168 */ 169 protected Class<?> findClass(String name) throws ClassNotFoundException { 170 171 int index = name.indexOf(';'); 172 String cookie = ""; 173 if(index != -1) { 174 cookie = name.substring(index, name.length()); 175 name = name.substring(0, index); 176 } 177 178 // check loaded JAR files 179 try { 180 return super.findClass(name); 181 } catch (ClassNotFoundException e) { 182 } 183 184 // Otherwise, try loading the class from the code base URL 185 186 // 4668479: Option to turn off codebase lookup in AppletClassLoader 187 // during resource requests. [stanley.ho] 188 if (codebaseLookup == false) 189 throw new ClassNotFoundException(name); 190 191 // final String path = name.replace('.', '/').concat(".class").concat(cookie); 192 String encodedName = ParseUtil.encodePath(name.replace('.', '/'), false); 193 final String path = (new StringBuffer(encodedName)).append(".class").append(cookie).toString(); 194 try { 195 byte[] b = AccessController.doPrivileged( 196 new PrivilegedExceptionAction<byte[]>() { 197 public byte[] run() throws IOException { 198 try { 199 URL finalURL = new URL(base, path); 200 201 // Make sure the codebase won't be modified 202 if (base.getProtocol().equals(finalURL.getProtocol()) && 203 base.getHost().equals(finalURL.getHost()) && 204 base.getPort() == finalURL.getPort()) { 205 return getBytes(finalURL); 206 } 207 else { 208 return null; 209 } 210 } catch (Exception e) { 211 return null; 212 } 213 } 214 }, acc); 215 216 if (b != null) { 217 return defineClass(name, b, 0, b.length, codesource); 218 } else { 219 throw new ClassNotFoundException(name); 220 } 221 } catch (PrivilegedActionException e) { 222 throw new ClassNotFoundException(name, e.getException()); 223 } 224 } 225 226 /** 227 * Returns the permissions for the given codesource object. 228 * The implementation of this method first calls super.getPermissions, 229 * to get the permissions 230 * granted by the super class, and then adds additional permissions 231 * based on the URL of the codesource. 232 * <p> 233 * If the protocol is "file" 234 * and the path specifies a file, permission is granted to read all files 235 * and (recursively) all files and subdirectories contained in 236 * that directory. This is so applets with a codebase of 237 * file:/blah/some.jar can read in file:/blah/, which is needed to 238 * be backward compatible. We also add permission to connect back to 239 * the "localhost". 240 * 241 * @param codesource the codesource 242 * @throws NullPointerException if {@code codesource} is {@code null}. 243 * @return the permissions granted to the codesource 244 */ 245 protected PermissionCollection getPermissions(CodeSource codesource) 246 { 247 final PermissionCollection perms = super.getPermissions(codesource); 248 249 URL url = codesource.getLocation(); 250 251 String path = null; 252 Permission p; 253 254 try { 255 p = url.openConnection().getPermission(); 256 } catch (java.io.IOException ioe) { 257 p = null; 258 } 259 260 if (p instanceof FilePermission) { 261 path = p.getName(); 262 } else if ((p == null) && (url.getProtocol().equals("file"))) { 263 path = url.getFile().replace('/', File.separatorChar); 264 path = ParseUtil.decode(path); 265 } 266 267 if (path != null) { 268 final String rawPath = path; 269 if (!path.endsWith(File.separator)) { 270 int endIndex = path.lastIndexOf(File.separatorChar); 271 if (endIndex != -1) { 272 path = path.substring(0, endIndex + 1) + "-"; 273 perms.add(new FilePermission(path, 274 SecurityConstants.FILE_READ_ACTION)); 275 } 276 } 277 final File f = new File(rawPath); 278 final boolean isDirectory = f.isDirectory(); 279 // grant codebase recursive read permission 280 // this should only be granted to non-UNC file URL codebase and 281 // the codesource path must either be a directory, or a file 282 // that ends with .jar or .zip 283 if (allowRecursiveDirectoryRead && (isDirectory || 284 rawPath.toLowerCase().endsWith(".jar") || 285 rawPath.toLowerCase().endsWith(".zip"))) { 286 287 Permission bperm; 288 try { 289 bperm = base.openConnection().getPermission(); 290 } catch (java.io.IOException ioe) { 291 bperm = null; 292 } 293 if (bperm instanceof FilePermission) { 294 String bpath = bperm.getName(); 295 if (bpath.endsWith(File.separator)) { 296 bpath += "-"; 297 } 298 perms.add(new FilePermission(bpath, 299 SecurityConstants.FILE_READ_ACTION)); 300 } else if ((bperm == null) && (base.getProtocol().equals("file"))) { 301 String bpath = base.getFile().replace('/', File.separatorChar); 302 bpath = ParseUtil.decode(bpath); 303 if (bpath.endsWith(File.separator)) { 304 bpath += "-"; 305 } 306 perms.add(new FilePermission(bpath, SecurityConstants.FILE_READ_ACTION)); 307 } 308 309 } 310 } 311 return perms; 312 } 313 314 /* 315 * Returns the contents of the specified URL as an array of bytes. 316 */ 317 private static byte[] getBytes(URL url) throws IOException { 318 URLConnection uc = url.openConnection(); 319 if (uc instanceof java.net.HttpURLConnection) { 320 java.net.HttpURLConnection huc = (java.net.HttpURLConnection) uc; 321 int code = huc.getResponseCode(); 322 if (code >= java.net.HttpURLConnection.HTTP_BAD_REQUEST) { 323 throw new IOException("open HTTP connection failed."); 324 } 325 } 326 int len = uc.getContentLength(); 327 328 // Fixed #4507227: Slow performance to load 329 // class and resources. [stanleyh] 330 // 331 // Use buffered input stream [stanleyh] 332 InputStream in = new BufferedInputStream(uc.getInputStream()); 333 334 byte[] b; 335 try { 336 b = in.readAllBytes(); 337 if (len != -1 && b.length != len) 338 throw new EOFException("Expected:" + len + ", read:" + b.length); 339 } finally { 340 in.close(); 341 } 342 return b; 343 } 344 345 // Object for synchronization around getResourceAsStream() 346 private Object syncResourceAsStream = new Object(); 347 private Object syncResourceAsStreamFromJar = new Object(); 348 349 // Flag to indicate getResourceAsStream() is in call 350 private boolean resourceAsStreamInCall = false; 351 private boolean resourceAsStreamFromJarInCall = false; 352 353 /** 354 * Returns an input stream for reading the specified resource. 355 * 356 * The search order is described in the documentation for {@link 357 * #getResource(String)}.<p> 358 * 359 * @param name the resource name 360 * @return an input stream for reading the resource, or {@code null} 361 * if the resource could not be found 362 * @since 1.1 363 */ 364 public InputStream getResourceAsStream(String name) 365 { 366 367 if (name == null) { 368 throw new NullPointerException("name"); 369 } 370 371 try 372 { 373 InputStream is = null; 374 375 // Fixed #4507227: Slow performance to load 376 // class and resources. [stanleyh] 377 // 378 // The following is used to avoid calling 379 // AppletClassLoader.findResource() in 380 // super.getResourceAsStream(). Otherwise, 381 // unnecessary connection will be made. 382 // 383 synchronized(syncResourceAsStream) 384 { 385 resourceAsStreamInCall = true; 386 387 // Call super class 388 is = super.getResourceAsStream(name); 389 390 resourceAsStreamInCall = false; 391 } 392 393 // 4668479: Option to turn off codebase lookup in AppletClassLoader 394 // during resource requests. [stanley.ho] 395 if (codebaseLookup == true && is == null) 396 { 397 // If resource cannot be obtained, 398 // try to download it from codebase 399 URL url = new URL(base, ParseUtil.encodePath(name, false)); 400 is = url.openStream(); 401 } 402 403 return is; 404 } 405 catch (Exception e) 406 { 407 return null; 408 } 409 } 410 411 412 /** 413 * Returns an input stream for reading the specified resource from the 414 * the loaded jar files. 415 * 416 * The search order is described in the documentation for {@link 417 * #getResource(String)}.<p> 418 * 419 * @param name the resource name 420 * @return an input stream for reading the resource, or {@code null} 421 * if the resource could not be found 422 * @since 1.1 423 */ 424 public InputStream getResourceAsStreamFromJar(String name) { 425 426 if (name == null) { 427 throw new NullPointerException("name"); 428 } 429 430 try { 431 InputStream is = null; 432 synchronized(syncResourceAsStreamFromJar) { 433 resourceAsStreamFromJarInCall = true; 434 // Call super class 435 is = super.getResourceAsStream(name); 436 resourceAsStreamFromJarInCall = false; 437 } 438 439 return is; 440 } catch (Exception e) { 441 return null; 442 } 443 } 444 445 446 /* 447 * Finds the applet resource with the specified name. First checks 448 * loaded JAR files then the applet code base for the resource. 449 */ 450 public URL findResource(String name) { 451 // check loaded JAR files 452 URL url = super.findResource(name); 453 454 // 6215746: Disable META-INF/* lookup from codebase in 455 // applet/plugin classloader. [stanley.ho] 456 if (name.startsWith("META-INF/")) 457 return url; 458 459 // 4668479: Option to turn off codebase lookup in AppletClassLoader 460 // during resource requests. [stanley.ho] 461 if (codebaseLookup == false) 462 return url; 463 464 if (url == null) 465 { 466 //#4805170, if it is a call from Applet.getImage() 467 //we should check for the image only in the archives 468 boolean insideGetResourceAsStreamFromJar = false; 469 synchronized(syncResourceAsStreamFromJar) { 470 insideGetResourceAsStreamFromJar = resourceAsStreamFromJarInCall; 471 } 472 473 if (insideGetResourceAsStreamFromJar) { 474 return null; 475 } 476 477 // Fixed #4507227: Slow performance to load 478 // class and resources. [stanleyh] 479 // 480 // Check if getResourceAsStream is called. 481 // 482 boolean insideGetResourceAsStream = false; 483 484 synchronized(syncResourceAsStream) 485 { 486 insideGetResourceAsStream = resourceAsStreamInCall; 487 } 488 489 // If getResourceAsStream is called, don't 490 // trigger the following code. Otherwise, 491 // unnecessary connection will be made. 492 // 493 if (insideGetResourceAsStream == false) 494 { 495 // otherwise, try the code base 496 try { 497 url = new URL(base, ParseUtil.encodePath(name, false)); 498 // check if resource exists 499 if(!resourceExists(url)) 500 url = null; 501 } catch (Exception e) { 502 // all exceptions, including security exceptions, are caught 503 url = null; 504 } 505 } 506 } 507 return url; 508 } 509 510 511 private boolean resourceExists(URL url) { 512 // Check if the resource exists. 513 // It almost works to just try to do an openConnection() but 514 // HttpURLConnection will return true on HTTP_BAD_REQUEST 515 // when the requested name ends in ".html", ".htm", and ".txt" 516 // and we want to be able to handle these 517 // 518 // Also, cannot just open a connection for things like FileURLConnection, 519 // because they succeed when connecting to a nonexistent file. 520 // So, in those cases we open and close an input stream. 521 boolean ok = true; 522 try { 523 URLConnection conn = url.openConnection(); 524 if (conn instanceof java.net.HttpURLConnection) { 525 java.net.HttpURLConnection hconn = 526 (java.net.HttpURLConnection) conn; 527 528 // To reduce overhead, using http HEAD method instead of GET method 529 hconn.setRequestMethod("HEAD"); 530 531 int code = hconn.getResponseCode(); 532 if (code == java.net.HttpURLConnection.HTTP_OK) { 533 return true; 534 } 535 if (code >= java.net.HttpURLConnection.HTTP_BAD_REQUEST) { 536 return false; 537 } 538 } else { 539 /** 540 * Fix for #4182052 - stanleyh 541 * 542 * The same connection should be reused to avoid multiple 543 * HTTP connections 544 */ 545 546 // our best guess for the other cases 547 InputStream is = conn.getInputStream(); 548 is.close(); 549 } 550 } catch (Exception ex) { 551 ok = false; 552 } 553 return ok; 554 } 555 556 /* 557 * Returns an enumeration of all the applet resources with the specified 558 * name. First checks loaded JAR files then the applet code base for all 559 * available resources. 560 */ 561 @Override 562 public Enumeration<URL> findResources(String name) throws IOException { 563 564 final Enumeration<URL> e = super.findResources(name); 565 566 // 6215746: Disable META-INF/* lookup from codebase in 567 // applet/plugin classloader. [stanley.ho] 568 if (name.startsWith("META-INF/")) 569 return e; 570 571 // 4668479: Option to turn off codebase lookup in AppletClassLoader 572 // during resource requests. [stanley.ho] 573 if (codebaseLookup == false) 574 return e; 575 576 URL u = new URL(base, ParseUtil.encodePath(name, false)); 577 if (!resourceExists(u)) { 578 u = null; 579 } 580 581 final URL url = u; 582 return new Enumeration<URL>() { 583 private boolean done; 584 public URL nextElement() { 585 if (!done) { 586 if (e.hasMoreElements()) { 587 return e.nextElement(); 588 } 589 done = true; 590 if (url != null) { 591 return url; 592 } 593 } 594 throw new NoSuchElementException(); 595 } 596 public boolean hasMoreElements() { 597 return !done && (e.hasMoreElements() || url != null); 598 } 599 }; 600 } 601 602 /* 603 * Load and resolve the file specified by the applet tag CODE 604 * attribute. The argument can either be the relative path 605 * of the class file itself or just the name of the class. 606 */ 607 Class<?> loadCode(String name) throws ClassNotFoundException { 608 // first convert any '/' or native file separator to . 609 name = name.replace('/', '.'); 610 name = name.replace(File.separatorChar, '.'); 611 612 // deal with URL rewriting 613 String cookie = null; 614 int index = name.indexOf(';'); 615 if(index != -1) { 616 cookie = name.substring(index, name.length()); 617 name = name.substring(0, index); 618 } 619 620 // save that name for later 621 String fullName = name; 622 // then strip off any suffixes 623 if (name.endsWith(".class") || name.endsWith(".java")) { 624 name = name.substring(0, name.lastIndexOf('.')); 625 } 626 try { 627 if(cookie != null) 628 name = (new StringBuffer(name)).append(cookie).toString(); 629 return loadClass(name); 630 } catch (ClassNotFoundException e) { 631 } 632 // then if it didn't end with .java or .class, or in the 633 // really pathological case of a class named class or java 634 if(cookie != null) 635 fullName = (new StringBuffer(fullName)).append(cookie).toString(); 636 637 return loadClass(fullName); 638 } 639 640 /* 641 * The threadgroup that the applets loaded by this classloader live 642 * in. In the sun.* implementation of applets, the security manager's 643 * (AppletSecurity) getThreadGroup returns the thread group of the 644 * first applet on the stack, which is the applet's thread group. 645 */ 646 private AppletThreadGroup threadGroup; 647 private AppContext appContext; 648 649 public ThreadGroup getThreadGroup() { 650 synchronized (threadGroupSynchronizer) { 651 if (threadGroup == null || threadGroup.isDestroyed()) { 652 AccessController.doPrivileged(new PrivilegedAction<Object>() { 653 public Object run() { 654 threadGroup = new AppletThreadGroup(base + "-threadGroup"); 655 // threadGroup.setDaemon(true); 656 // threadGroup is now destroyed by AppContext.dispose() 657 658 // Create the new AppContext from within a Thread belonging 659 // to the newly created ThreadGroup, and wait for the 660 // creation to complete before returning from this method. 661 AppContextCreator creatorThread = new AppContextCreator(threadGroup); 662 663 // Since this thread will later be used to launch the 664 // applet's AWT-event dispatch thread and we want the applet 665 // code executing the AWT callbacks to use their own class 666 // loader rather than the system class loader, explicitly 667 // set the context class loader to the AppletClassLoader. 668 creatorThread.setContextClassLoader(AppletClassLoader.this); 669 670 creatorThread.start(); 671 try { 672 synchronized(creatorThread.syncObject) { 673 while (!creatorThread.created) { 674 creatorThread.syncObject.wait(); 675 } 676 } 677 } catch (InterruptedException e) { } 678 appContext = creatorThread.appContext; 679 return null; 680 } 681 }); 682 } 683 return threadGroup; 684 } 685 } 686 687 /* 688 * Get the AppContext, if any, corresponding to this AppletClassLoader. 689 */ 690 public AppContext getAppContext() { 691 return appContext; 692 } 693 694 int usageCount = 0; 695 696 /** 697 * Grab this AppletClassLoader and its ThreadGroup/AppContext, so they 698 * won't be destroyed. 699 */ 700 public void grab() { 701 synchronized(grabReleaseSynchronizer) { 702 usageCount++; 703 } 704 getThreadGroup(); // Make sure ThreadGroup/AppContext exist 705 } 706 707 protected void setExceptionStatus() 708 { 709 exceptionStatus = true; 710 } 711 712 public boolean getExceptionStatus() 713 { 714 return exceptionStatus; 715 } 716 717 /** 718 * Release this AppletClassLoader and its ThreadGroup/AppContext. 719 * If nothing else has grabbed this AppletClassLoader, its ThreadGroup 720 * and AppContext will be destroyed. 721 * 722 * Because this method may destroy the AppletClassLoader's ThreadGroup, 723 * this method should NOT be called from within the AppletClassLoader's 724 * ThreadGroup. 725 * 726 * Changed modifier to protected in order to be able to overwrite this 727 * function in PluginClassLoader.java 728 */ 729 protected void release() { 730 731 AppContext tempAppContext = null; 732 733 synchronized(grabReleaseSynchronizer) { 734 if (usageCount > 1) { 735 --usageCount; 736 } else { 737 synchronized(threadGroupSynchronizer) { 738 tempAppContext = resetAppContext(); 739 } 740 } 741 } 742 743 // Dispose appContext outside any sync block to 744 // prevent potential deadlock. 745 if (tempAppContext != null) { 746 try { 747 tempAppContext.dispose(); // nuke the world! 748 } catch (IllegalThreadStateException e) { } 749 } 750 } 751 752 /* 753 * reset classloader's AppContext and ThreadGroup 754 * This method is for subclass PluginClassLoader to 755 * reset superclass's AppContext and ThreadGroup but do 756 * not dispose the AppContext. PluginClassLoader does not 757 * use UsageCount to decide whether to dispose AppContext 758 * 759 * @return previous AppContext 760 */ 761 protected AppContext resetAppContext() { 762 AppContext tempAppContext = null; 763 764 synchronized(threadGroupSynchronizer) { 765 // Store app context in temp variable 766 tempAppContext = appContext; 767 usageCount = 0; 768 appContext = null; 769 threadGroup = null; 770 } 771 return tempAppContext; 772 } 773 774 775 // Hash map to store applet compatibility info 776 private HashMap<String, Boolean> jdk11AppletInfo = new HashMap<>(); 777 private HashMap<String, Boolean> jdk12AppletInfo = new HashMap<>(); 778 779 /** 780 * Set applet target level as JDK 1.1. 781 * 782 * @param clazz Applet class. 783 * @param bool true if JDK is targeted for JDK 1.1; 784 * false otherwise. 785 */ 786 void setJDK11Target(Class<?> clazz, boolean bool) 787 { 788 jdk11AppletInfo.put(clazz.toString(), Boolean.valueOf(bool)); 789 } 790 791 /** 792 * Set applet target level as JDK 1.2. 793 * 794 * @param clazz Applet class. 795 * @param bool true if JDK is targeted for JDK 1.2; 796 * false otherwise. 797 */ 798 void setJDK12Target(Class<?> clazz, boolean bool) 799 { 800 jdk12AppletInfo.put(clazz.toString(), Boolean.valueOf(bool)); 801 } 802 803 /** 804 * Determine if applet is targeted for JDK 1.1. 805 * 806 * @param clazz Applet class. 807 * @return TRUE if applet is targeted for JDK 1.1; 808 * FALSE if applet is not; 809 * null if applet is unknown. 810 */ 811 Boolean isJDK11Target(Class<?> clazz) 812 { 813 return jdk11AppletInfo.get(clazz.toString()); 814 } 815 816 /** 817 * Determine if applet is targeted for JDK 1.2. 818 * 819 * @param clazz Applet class. 820 * @return TRUE if applet is targeted for JDK 1.2; 821 * FALSE if applet is not; 822 * null if applet is unknown. 823 */ 824 Boolean isJDK12Target(Class<?> clazz) 825 { 826 return jdk12AppletInfo.get(clazz.toString()); 827 } 828 829 private static AppletMessageHandler mh = 830 new AppletMessageHandler("appletclassloader"); 831 832 /* 833 * Prints a class loading error message. 834 */ 835 private static void printError(String name, Throwable e) { 836 String s = null; 837 if (e == null) { 838 s = mh.getMessage("filenotfound", name); 839 } else if (e instanceof IOException) { 840 s = mh.getMessage("fileioexception", name); 841 } else if (e instanceof ClassFormatError) { 842 s = mh.getMessage("fileformat", name); 843 } else if (e instanceof ThreadDeath) { 844 s = mh.getMessage("filedeath", name); 845 } else if (e instanceof Error) { 846 s = mh.getMessage("fileerror", e.toString(), name); 847 } 848 if (s != null) { 849 System.err.println(s); 850 } 851 } 852 } 853 854 /* 855 * The AppContextCreator class is used to create an AppContext from within 856 * a Thread belonging to the new AppContext's ThreadGroup. To wait for 857 * this operation to complete before continuing, wait for the notifyAll() 858 * operation on the syncObject to occur. 859 */ 860 class AppContextCreator extends Thread { 861 Object syncObject = new Object(); 862 AppContext appContext = null; 863 volatile boolean created = false; 864 865 /** 866 * Must call the 5-args super-class constructor to erase locals. 867 */ 868 private AppContextCreator() { 869 throw new UnsupportedOperationException("Must erase locals"); 870 } 871 872 AppContextCreator(ThreadGroup group) { 873 super(group, null, "AppContextCreator", 0, false); 874 } 875 876 public void run() { 877 appContext = SunToolkit.createNewAppContext(); 878 created = true; 879 synchronized(syncObject) { 880 syncObject.notifyAll(); 881 } 882 } // run() 883 884 } // class AppContextCreator