Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/applet/AppletClassLoader.java
38829 views
/*1* Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.applet;2627import java.lang.NullPointerException;28import java.net.URL;29import java.net.URLClassLoader;30import java.net.SocketPermission;31import java.net.URLConnection;32import java.net.MalformedURLException;33import java.net.InetAddress;34import java.net.UnknownHostException;35import java.io.EOFException;36import java.io.File;37import java.io.FilePermission;38import java.io.IOException;39import java.io.BufferedInputStream;40import java.io.InputStream;41import java.util.Enumeration;42import java.util.HashMap;43import java.util.NoSuchElementException;44import java.security.AccessController;45import java.security.AccessControlContext;46import java.security.PrivilegedAction;47import java.security.PrivilegedExceptionAction;48import java.security.PrivilegedActionException;49import java.security.CodeSource;50import java.security.Permission;51import java.security.PermissionCollection;52import sun.awt.AppContext;53import sun.awt.SunToolkit;54import sun.misc.IOUtils;55import sun.net.www.ParseUtil;56import sun.security.util.SecurityConstants;5758/**59* This class defines the class loader for loading applet classes and60* resources. It extends URLClassLoader to search the applet code base61* for the class or resource after checking any loaded JAR files.62*/63public class AppletClassLoader extends URLClassLoader {64private URL base; /* applet code base URL */65private CodeSource codesource; /* codesource for the base URL */66private AccessControlContext acc;67private boolean exceptionStatus = false;6869private final Object threadGroupSynchronizer = new Object();70private final Object grabReleaseSynchronizer = new Object();7172private boolean codebaseLookup = true;73private volatile boolean allowRecursiveDirectoryRead = true;7475/*76* Creates a new AppletClassLoader for the specified base URL.77*/78protected AppletClassLoader(URL base) {79super(new URL[0]);80this.base = base;81this.codesource =82new CodeSource(base, (java.security.cert.Certificate[]) null);83acc = AccessController.getContext();84}8586public void disableRecursiveDirectoryRead() {87allowRecursiveDirectoryRead = false;88}899091/**92* Set the codebase lookup flag.93*/94void setCodebaseLookup(boolean codebaseLookup) {95this.codebaseLookup = codebaseLookup;96}9798/*99* Returns the applet code base URL.100*/101URL getBaseURL() {102return base;103}104105/*106* Returns the URLs used for loading classes and resources.107*/108public URL[] getURLs() {109URL[] jars = super.getURLs();110URL[] urls = new URL[jars.length + 1];111System.arraycopy(jars, 0, urls, 0, jars.length);112urls[urls.length - 1] = base;113return urls;114}115116/*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.java120*/121protected void addJar(String name) throws IOException {122URL url;123try {124url = new URL(base, name);125} catch (MalformedURLException e) {126throw new IllegalArgumentException("name");127}128addURL(url);129// DEBUG130//URL[] urls = getURLs();131//for (int i = 0; i < urls.length; i++) {132// System.out.println("url[" + i + "] = " + urls[i]);133//}134}135136/*137* Override loadClass so that class loading errors can be caught in138* order to print better error messages.139*/140public synchronized Class loadClass(String name, boolean resolve)141throws ClassNotFoundException142{143// First check if we have permission to access the package. This144// should go away once we've added support for exported packages.145int i = name.lastIndexOf('.');146if (i != -1) {147SecurityManager sm = System.getSecurityManager();148if (sm != null)149sm.checkPackageAccess(name.substring(0, i));150}151try {152return super.loadClass(name, resolve);153} catch (ClassNotFoundException e) {154//printError(name, e.getException());155throw e;156} catch (RuntimeException e) {157//printError(name, e);158throw e;159} catch (Error e) {160//printError(name, e);161throw e;162}163}164165/*166* Finds the applet class with the specified name. First searches167* loaded JAR files then the applet code base for the class.168*/169protected Class findClass(String name) throws ClassNotFoundException {170171int index = name.indexOf(";");172String cookie = "";173if(index != -1) {174cookie = name.substring(index, name.length());175name = name.substring(0, index);176}177178// check loaded JAR files179try {180return super.findClass(name);181} catch (ClassNotFoundException e) {182}183184// Otherwise, try loading the class from the code base URL185186// 4668479: Option to turn off codebase lookup in AppletClassLoader187// during resource requests. [stanley.ho]188if (codebaseLookup == false)189throw new ClassNotFoundException(name);190191// final String path = name.replace('.', '/').concat(".class").concat(cookie);192String encodedName = ParseUtil.encodePath(name.replace('.', '/'), false);193final String path = (new StringBuffer(encodedName)).append(".class").append(cookie).toString();194try {195byte[] b = (byte[]) AccessController.doPrivileged(196new PrivilegedExceptionAction() {197public Object run() throws IOException {198try {199URL finalURL = new URL(base, path);200201// Make sure the codebase won't be modified202if (base.getProtocol().equals(finalURL.getProtocol()) &&203base.getHost().equals(finalURL.getHost()) &&204base.getPort() == finalURL.getPort()) {205return getBytes(finalURL);206}207else {208return null;209}210} catch (Exception e) {211return null;212}213}214}, acc);215216if (b != null) {217return defineClass(name, b, 0, b.length, codesource);218} else {219throw new ClassNotFoundException(name);220}221} catch (PrivilegedActionException e) {222throw new ClassNotFoundException(name, e.getException());223}224}225226/**227* Returns the permissions for the given codesource object.228* The implementation of this method first calls super.getPermissions,229* to get the permissions230* granted by the super class, and then adds additional permissions231* 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 files235* and (recursively) all files and subdirectories contained in236* that directory. This is so applets with a codebase of237* file:/blah/some.jar can read in file:/blah/, which is needed to238* be backward compatible. We also add permission to connect back to239* the "localhost".240*241* @param codesource the codesource242* @throws NullPointerException if {@code codesource} is {@code null}.243* @return the permissions granted to the codesource244*/245protected PermissionCollection getPermissions(CodeSource codesource)246{247final PermissionCollection perms = super.getPermissions(codesource);248249URL url = codesource.getLocation();250251String path = null;252Permission p;253254try {255p = url.openConnection().getPermission();256} catch (java.io.IOException ioe) {257p = null;258}259260if (p instanceof FilePermission) {261path = p.getName();262} else if ((p == null) && (url.getProtocol().equals("file"))) {263path = url.getFile().replace('/', File.separatorChar);264path = ParseUtil.decode(path);265}266267if (path != null) {268final String rawPath = path;269if (!path.endsWith(File.separator)) {270int endIndex = path.lastIndexOf(File.separatorChar);271if (endIndex != -1) {272path = path.substring(0, endIndex + 1) + "-";273perms.add(new FilePermission(path,274SecurityConstants.FILE_READ_ACTION));275}276}277final File f = new File(rawPath);278final boolean isDirectory = f.isDirectory();279// grant codebase recursive read permission280// this should only be granted to non-UNC file URL codebase and281// the codesource path must either be a directory, or a file282// that ends with .jar or .zip283if (allowRecursiveDirectoryRead && (isDirectory ||284rawPath.toLowerCase().endsWith(".jar") ||285rawPath.toLowerCase().endsWith(".zip"))) {286287Permission bperm;288try {289bperm = base.openConnection().getPermission();290} catch (java.io.IOException ioe) {291bperm = null;292}293if (bperm instanceof FilePermission) {294String bpath = bperm.getName();295if (bpath.endsWith(File.separator)) {296bpath += "-";297}298perms.add(new FilePermission(bpath,299SecurityConstants.FILE_READ_ACTION));300} else if ((bperm == null) && (base.getProtocol().equals("file"))) {301String bpath = base.getFile().replace('/', File.separatorChar);302bpath = ParseUtil.decode(bpath);303if (bpath.endsWith(File.separator)) {304bpath += "-";305}306perms.add(new FilePermission(bpath, SecurityConstants.FILE_READ_ACTION));307}308309}310}311return perms;312}313314/*315* Returns the contents of the specified URL as an array of bytes.316*/317private static byte[] getBytes(URL url) throws IOException {318URLConnection uc = url.openConnection();319if (uc instanceof java.net.HttpURLConnection) {320java.net.HttpURLConnection huc = (java.net.HttpURLConnection) uc;321int code = huc.getResponseCode();322if (code >= java.net.HttpURLConnection.HTTP_BAD_REQUEST) {323throw new IOException("open HTTP connection failed.");324}325}326int len = uc.getContentLength();327328// Fixed #4507227: Slow performance to load329// class and resources. [stanleyh]330//331// Use buffered input stream [stanleyh]332InputStream in = new BufferedInputStream(uc.getInputStream());333334byte[] b;335try {336b = IOUtils.readAllBytes(in);337if (len != -1 && b.length != len)338throw new EOFException("Expected:" + len + ", read:" + b.length);339} finally {340in.close();341}342return b;343}344345// Object for synchronization around getResourceAsStream()346private Object syncResourceAsStream = new Object();347private Object syncResourceAsStreamFromJar = new Object();348349// Flag to indicate getResourceAsStream() is in call350private boolean resourceAsStreamInCall = false;351private boolean resourceAsStreamFromJarInCall = false;352353/**354* Returns an input stream for reading the specified resource.355*356* The search order is described in the documentation for {@link357* #getResource(String)}.<p>358*359* @param name the resource name360* @return an input stream for reading the resource, or <code>null</code>361* if the resource could not be found362* @since JDK1.1363*/364public InputStream getResourceAsStream(String name)365{366367if (name == null) {368throw new NullPointerException("name");369}370371try372{373InputStream is = null;374375// Fixed #4507227: Slow performance to load376// class and resources. [stanleyh]377//378// The following is used to avoid calling379// AppletClassLoader.findResource() in380// super.getResourceAsStream(). Otherwise,381// unnecessary connection will be made.382//383synchronized(syncResourceAsStream)384{385resourceAsStreamInCall = true;386387// Call super class388is = super.getResourceAsStream(name);389390resourceAsStreamInCall = false;391}392393// 4668479: Option to turn off codebase lookup in AppletClassLoader394// during resource requests. [stanley.ho]395if (codebaseLookup == true && is == null)396{397// If resource cannot be obtained,398// try to download it from codebase399URL url = new URL(base, ParseUtil.encodePath(name, false));400is = url.openStream();401}402403return is;404}405catch (Exception e)406{407return null;408}409}410411412/**413* Returns an input stream for reading the specified resource from the414* the loaded jar files.415*416* The search order is described in the documentation for {@link417* #getResource(String)}.<p>418*419* @param name the resource name420* @return an input stream for reading the resource, or <code>null</code>421* if the resource could not be found422* @since JDK1.1423*/424public InputStream getResourceAsStreamFromJar(String name) {425426if (name == null) {427throw new NullPointerException("name");428}429430try {431InputStream is = null;432synchronized(syncResourceAsStreamFromJar) {433resourceAsStreamFromJarInCall = true;434// Call super class435is = super.getResourceAsStream(name);436resourceAsStreamFromJarInCall = false;437}438439return is;440} catch (Exception e) {441return null;442}443}444445446/*447* Finds the applet resource with the specified name. First checks448* loaded JAR files then the applet code base for the resource.449*/450public URL findResource(String name) {451// check loaded JAR files452URL url = super.findResource(name);453454// 6215746: Disable META-INF/* lookup from codebase in455// applet/plugin classloader. [stanley.ho]456if (name.startsWith("META-INF/"))457return url;458459// 4668479: Option to turn off codebase lookup in AppletClassLoader460// during resource requests. [stanley.ho]461if (codebaseLookup == false)462return url;463464if (url == null)465{466//#4805170, if it is a call from Applet.getImage()467//we should check for the image only in the archives468boolean insideGetResourceAsStreamFromJar = false;469synchronized(syncResourceAsStreamFromJar) {470insideGetResourceAsStreamFromJar = resourceAsStreamFromJarInCall;471}472473if (insideGetResourceAsStreamFromJar) {474return null;475}476477// Fixed #4507227: Slow performance to load478// class and resources. [stanleyh]479//480// Check if getResourceAsStream is called.481//482boolean insideGetResourceAsStream = false;483484synchronized(syncResourceAsStream)485{486insideGetResourceAsStream = resourceAsStreamInCall;487}488489// If getResourceAsStream is called, don't490// trigger the following code. Otherwise,491// unnecessary connection will be made.492//493if (insideGetResourceAsStream == false)494{495// otherwise, try the code base496try {497url = new URL(base, ParseUtil.encodePath(name, false));498// check if resource exists499if(!resourceExists(url))500url = null;501} catch (Exception e) {502// all exceptions, including security exceptions, are caught503url = null;504}505}506}507return url;508}509510511private boolean resourceExists(URL url) {512// Check if the resource exists.513// It almost works to just try to do an openConnection() but514// HttpURLConnection will return true on HTTP_BAD_REQUEST515// when the requested name ends in ".html", ".htm", and ".txt"516// and we want to be able to handle these517//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.521boolean ok = true;522try {523URLConnection conn = url.openConnection();524if (conn instanceof java.net.HttpURLConnection) {525java.net.HttpURLConnection hconn =526(java.net.HttpURLConnection) conn;527528// To reduce overhead, using http HEAD method instead of GET method529hconn.setRequestMethod("HEAD");530531int code = hconn.getResponseCode();532if (code == java.net.HttpURLConnection.HTTP_OK) {533return true;534}535if (code >= java.net.HttpURLConnection.HTTP_BAD_REQUEST) {536return false;537}538} else {539/**540* Fix for #4182052 - stanleyh541*542* The same connection should be reused to avoid multiple543* HTTP connections544*/545546// our best guess for the other cases547InputStream is = conn.getInputStream();548is.close();549}550} catch (Exception ex) {551ok = false;552}553return ok;554}555556/*557* Returns an enumeration of all the applet resources with the specified558* name. First checks loaded JAR files then the applet code base for all559* available resources.560*/561public Enumeration findResources(String name) throws IOException {562563final Enumeration e = super.findResources(name);564565// 6215746: Disable META-INF/* lookup from codebase in566// applet/plugin classloader. [stanley.ho]567if (name.startsWith("META-INF/"))568return e;569570// 4668479: Option to turn off codebase lookup in AppletClassLoader571// during resource requests. [stanley.ho]572if (codebaseLookup == false)573return e;574575URL u = new URL(base, ParseUtil.encodePath(name, false));576if (!resourceExists(u)) {577u = null;578}579580final URL url = u;581return new Enumeration() {582private boolean done;583public Object nextElement() {584if (!done) {585if (e.hasMoreElements()) {586return e.nextElement();587}588done = true;589if (url != null) {590return url;591}592}593throw new NoSuchElementException();594}595public boolean hasMoreElements() {596return !done && (e.hasMoreElements() || url != null);597}598};599}600601/*602* Load and resolve the file specified by the applet tag CODE603* attribute. The argument can either be the relative path604* of the class file itself or just the name of the class.605*/606Class loadCode(String name) throws ClassNotFoundException {607// first convert any '/' or native file separator to .608name = name.replace('/', '.');609name = name.replace(File.separatorChar, '.');610611// deal with URL rewriting612String cookie = null;613int index = name.indexOf(";");614if(index != -1) {615cookie = name.substring(index, name.length());616name = name.substring(0, index);617}618619// save that name for later620String fullName = name;621// then strip off any suffixes622if (name.endsWith(".class") || name.endsWith(".java")) {623name = name.substring(0, name.lastIndexOf('.'));624}625try {626if(cookie != null)627name = (new StringBuffer(name)).append(cookie).toString();628return loadClass(name);629} catch (ClassNotFoundException e) {630}631// then if it didn't end with .java or .class, or in the632// really pathological case of a class named class or java633if(cookie != null)634fullName = (new StringBuffer(fullName)).append(cookie).toString();635636return loadClass(fullName);637}638639/*640* The threadgroup that the applets loaded by this classloader live641* in. In the sun.* implementation of applets, the security manager's642* (AppletSecurity) getThreadGroup returns the thread group of the643* first applet on the stack, which is the applet's thread group.644*/645private AppletThreadGroup threadGroup;646private AppContext appContext;647648public ThreadGroup getThreadGroup() {649synchronized (threadGroupSynchronizer) {650if (threadGroup == null || threadGroup.isDestroyed()) {651AccessController.doPrivileged(new PrivilegedAction() {652public Object run() {653threadGroup = new AppletThreadGroup(base + "-threadGroup");654// threadGroup.setDaemon(true);655// threadGroup is now destroyed by AppContext.dispose()656657// Create the new AppContext from within a Thread belonging658// to the newly created ThreadGroup, and wait for the659// creation to complete before returning from this method.660AppContextCreator creatorThread = new AppContextCreator(threadGroup);661662// Since this thread will later be used to launch the663// applet's AWT-event dispatch thread and we want the applet664// code executing the AWT callbacks to use their own class665// loader rather than the system class loader, explicitly666// set the context class loader to the AppletClassLoader.667creatorThread.setContextClassLoader(AppletClassLoader.this);668669creatorThread.start();670try {671synchronized(creatorThread.syncObject) {672while (!creatorThread.created) {673creatorThread.syncObject.wait();674}675}676} catch (InterruptedException e) { }677appContext = creatorThread.appContext;678return null;679}680});681}682return threadGroup;683}684}685686/*687* Get the AppContext, if any, corresponding to this AppletClassLoader.688*/689public AppContext getAppContext() {690return appContext;691}692693int usageCount = 0;694695/**696* Grab this AppletClassLoader and its ThreadGroup/AppContext, so they697* won't be destroyed.698*/699public void grab() {700synchronized(grabReleaseSynchronizer) {701usageCount++;702}703getThreadGroup(); // Make sure ThreadGroup/AppContext exist704}705706protected void setExceptionStatus()707{708exceptionStatus = true;709}710711public boolean getExceptionStatus()712{713return exceptionStatus;714}715716/**717* Release this AppletClassLoader and its ThreadGroup/AppContext.718* If nothing else has grabbed this AppletClassLoader, its ThreadGroup719* and AppContext will be destroyed.720*721* Because this method may destroy the AppletClassLoader's ThreadGroup,722* this method should NOT be called from within the AppletClassLoader's723* ThreadGroup.724*725* Changed modifier to protected in order to be able to overwrite this726* function in PluginClassLoader.java727*/728protected void release() {729730AppContext tempAppContext = null;731732synchronized(grabReleaseSynchronizer) {733if (usageCount > 1) {734--usageCount;735} else {736synchronized(threadGroupSynchronizer) {737tempAppContext = resetAppContext();738}739}740}741742// Dispose appContext outside any sync block to743// prevent potential deadlock.744if (tempAppContext != null) {745try {746tempAppContext.dispose(); // nuke the world!747} catch (IllegalThreadStateException e) { }748}749}750751/*752* reset classloader's AppContext and ThreadGroup753* This method is for subclass PluginClassLoader to754* reset superclass's AppContext and ThreadGroup but do755* not dispose the AppContext. PluginClassLoader does not756* use UsageCount to decide whether to dispose AppContext757*758* @return previous AppContext759*/760protected AppContext resetAppContext() {761AppContext tempAppContext = null;762763synchronized(threadGroupSynchronizer) {764// Store app context in temp variable765tempAppContext = appContext;766usageCount = 0;767appContext = null;768threadGroup = null;769}770return tempAppContext;771}772773774// Hash map to store applet compatibility info775private HashMap jdk11AppletInfo = new HashMap();776private HashMap jdk12AppletInfo = new HashMap();777778/**779* Set applet target level as JDK 1.1.780*781* @param clazz Applet class.782* @param bool true if JDK is targeted for JDK 1.1;783* false otherwise.784*/785void setJDK11Target(Class clazz, boolean bool)786{787jdk11AppletInfo.put(clazz.toString(), Boolean.valueOf(bool));788}789790/**791* Set applet target level as JDK 1.2.792*793* @param clazz Applet class.794* @param bool true if JDK is targeted for JDK 1.2;795* false otherwise.796*/797void setJDK12Target(Class clazz, boolean bool)798{799jdk12AppletInfo.put(clazz.toString(), Boolean.valueOf(bool));800}801802/**803* Determine if applet is targeted for JDK 1.1.804*805* @param applet Applet class.806* @return TRUE if applet is targeted for JDK 1.1;807* FALSE if applet is not;808* null if applet is unknown.809*/810Boolean isJDK11Target(Class clazz)811{812return (Boolean) jdk11AppletInfo.get(clazz.toString());813}814815/**816* Determine if applet is targeted for JDK 1.2.817*818* @param applet Applet class.819* @return TRUE if applet is targeted for JDK 1.2;820* FALSE if applet is not;821* null if applet is unknown.822*/823Boolean isJDK12Target(Class clazz)824{825return (Boolean) jdk12AppletInfo.get(clazz.toString());826}827828private static AppletMessageHandler mh =829new AppletMessageHandler("appletclassloader");830831/*832* Prints a class loading error message.833*/834private static void printError(String name, Throwable e) {835String s = null;836if (e == null) {837s = mh.getMessage("filenotfound", name);838} else if (e instanceof IOException) {839s = mh.getMessage("fileioexception", name);840} else if (e instanceof ClassFormatError) {841s = mh.getMessage("fileformat", name);842} else if (e instanceof ThreadDeath) {843s = mh.getMessage("filedeath", name);844} else if (e instanceof Error) {845s = mh.getMessage("fileerror", e.toString(), name);846}847if (s != null) {848System.err.println(s);849}850}851}852853/*854* The AppContextCreator class is used to create an AppContext from within855* a Thread belonging to the new AppContext's ThreadGroup. To wait for856* this operation to complete before continuing, wait for the notifyAll()857* operation on the syncObject to occur.858*/859class AppContextCreator extends Thread {860Object syncObject = new Object();861AppContext appContext = null;862volatile boolean created = false;863864AppContextCreator(ThreadGroup group) {865super(group, "AppContextCreator");866}867868public void run() {869appContext = SunToolkit.createNewAppContext();870created = true;871synchronized(syncObject) {872syncObject.notifyAll();873}874} // run()875876} // class AppContextCreator877878879