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