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