1 /*
2 * Copyright (c) 1995, 2013, 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.IOUtils;
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 */
AppletClassLoader(URL base)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
disableRecursiveDirectoryRead()87 public void disableRecursiveDirectoryRead() {
88 allowRecursiveDirectoryRead = false;
89 }
90
91
92 /**
93 * Set the codebase lookup flag.
94 */
setCodebaseLookup(boolean codebaseLookup)95 void setCodebaseLookup(boolean codebaseLookup) {
96 this.codebaseLookup = codebaseLookup;
97 }
98
99 /*
100 * Returns the applet code base URL.
101 */
getBaseURL()102 URL getBaseURL() {
103 return base;
104 }
105
106 /*
107 * Returns the URLs used for loading classes and resources.
108 */
getURLs()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 */
addJar(String name)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 */
loadClass(String name, boolean resolve)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 */
findClass(String name)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 = (byte[]) AccessController.doPrivileged(
197 new PrivilegedExceptionAction() {
198 public Object 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 */
getPermissions(CodeSource codesource)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 */
getBytes(URL url)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 = IOUtils.readAllBytes(in);
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 JDK1.1
364 */
getResourceAsStream(String name)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 JDK1.1
424 */
getResourceAsStreamFromJar(String name)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 */
findResource(String name)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
resourceExists(URL url)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 */
findResources(String name)562 public Enumeration findResources(String name) throws IOException {
563
564 final Enumeration 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() {
583 private boolean done;
584 public Object 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 */
loadCode(String name)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
getThreadGroup()649 public ThreadGroup getThreadGroup() {
650 synchronized (threadGroupSynchronizer) {
651 if (threadGroup == null || threadGroup.isDestroyed()) {
652 AccessController.doPrivileged(new PrivilegedAction() {
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 */
getAppContext()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 */
grab()700 public void grab() {
701 synchronized(grabReleaseSynchronizer) {
702 usageCount++;
703 }
704 getThreadGroup(); // Make sure ThreadGroup/AppContext exist
705 }
706
setExceptionStatus()707 protected void setExceptionStatus()
708 {
709 exceptionStatus = true;
710 }
711
getExceptionStatus()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 */
release()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 */
resetAppContext()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 jdk11AppletInfo = new HashMap();
777 private HashMap 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 */
setJDK11Target(Class clazz, boolean bool)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 */
setJDK12Target(Class clazz, boolean bool)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 applet 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 */
isJDK11Target(Class clazz)811 Boolean isJDK11Target(Class clazz)
812 {
813 return (Boolean) jdk11AppletInfo.get(clazz.toString());
814 }
815
816 /**
817 * Determine if applet is targeted for JDK 1.2.
818 *
819 * @param applet 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 */
isJDK12Target(Class clazz)824 Boolean isJDK12Target(Class clazz)
825 {
826 return (Boolean) 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 */
printError(String name, Throwable e)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 AppContextCreator(ThreadGroup group) {
866 super(group, "AppContextCreator");
867 }
868
869 public void run() {
870 appContext = SunToolkit.createNewAppContext();
871 created = true;
872 synchronized(syncObject) {
873 syncObject.notifyAll();
874 }
875 } // run()
876
877 } // class AppContextCreator
878