|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package jdk.internal.loader; |
|
|
|
import java.io.IOException; |
|
import java.io.InputStream; |
|
import java.lang.module.ModuleDescriptor; |
|
import java.lang.module.ModuleReference; |
|
import java.lang.module.ModuleReader; |
|
import java.lang.ref.SoftReference; |
|
import java.net.MalformedURLException; |
|
import java.net.URI; |
|
import java.net.URL; |
|
import java.nio.ByteBuffer; |
|
import java.security.AccessController; |
|
import java.security.CodeSigner; |
|
import java.security.CodeSource; |
|
import java.security.PermissionCollection; |
|
import java.security.PrivilegedAction; |
|
import java.security.PrivilegedActionException; |
|
import java.security.PrivilegedExceptionAction; |
|
import java.security.SecureClassLoader; |
|
import java.util.ArrayList; |
|
import java.util.Collections; |
|
import java.util.Enumeration; |
|
import java.util.Iterator; |
|
import java.util.List; |
|
import java.util.Map; |
|
import java.util.NoSuchElementException; |
|
import java.util.Optional; |
|
import java.util.concurrent.ConcurrentHashMap; |
|
import java.util.function.Function; |
|
import java.util.jar.Attributes; |
|
import java.util.jar.Manifest; |
|
import java.util.stream.Stream; |
|
|
|
import jdk.internal.access.SharedSecrets; |
|
import jdk.internal.misc.VM; |
|
import jdk.internal.module.ModulePatcher.PatchedModuleReader; |
|
import jdk.internal.module.Resources; |
|
import jdk.internal.vm.annotation.Stable; |
|
import sun.security.util.LazyCodeSourcePermissionCollection; |
|
|
|
|
|
/** |
|
* The platform or application class loader. Resources loaded from modules |
|
* defined to the boot class loader are also loaded via an instance of this |
|
* ClassLoader type. |
|
* |
|
* <p> This ClassLoader supports loading of classes and resources from modules. |
|
* Modules are defined to the ClassLoader by invoking the {@link #loadModule} |
|
* method. Defining a module to this ClassLoader has the effect of making the |
|
* types in the module visible. </p> |
|
* |
|
* <p> This ClassLoader also supports loading of classes and resources from a |
|
* class path of URLs that are specified to the ClassLoader at construction |
|
* time. The class path may expand at runtime (the Class-Path attribute in JAR |
|
* files or via instrumentation agents). </p> |
|
* |
|
* <p> The delegation model used by this ClassLoader differs to the regular |
|
* delegation model. When requested to load a class then this ClassLoader first |
|
* maps the class name to its package name. If there is a module defined to a |
|
* BuiltinClassLoader containing this package then the class loader delegates |
|
* directly to that class loader. If there isn't a module containing the |
|
* package then it delegates the search to the parent class loader and if not |
|
* found in the parent then it searches the class path. The main difference |
|
* between this and the usual delegation model is that it allows the platform |
|
* class loader to delegate to the application class loader, important with |
|
* upgraded modules defined to the platform class loader. |
|
*/ |
|
|
|
public class BuiltinClassLoader |
|
extends SecureClassLoader |
|
{ |
|
static { |
|
if (!ClassLoader.registerAsParallelCapable()) |
|
throw new InternalError("Unable to register as parallel capable"); |
|
} |
|
|
|
|
|
private final BuiltinClassLoader parent; |
|
|
|
|
|
private @Stable URLClassPath ucp; |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private static class LoadedModule { |
|
private final BuiltinClassLoader loader; |
|
private final ModuleReference mref; |
|
private final URI uri; |
|
private @Stable URL codeSourceURL; |
|
|
|
LoadedModule(BuiltinClassLoader loader, ModuleReference mref) { |
|
URL url = null; |
|
this.uri = mref.location().orElse(null); |
|
|
|
// for non-jrt schemes we need to resolve the codeSourceURL |
|
// eagerly during bootstrap since the handler might be |
|
|
|
if (uri != null && !"jrt".equals(uri.getScheme())) { |
|
url = createURL(uri); |
|
} |
|
this.loader = loader; |
|
this.mref = mref; |
|
this.codeSourceURL = url; |
|
} |
|
|
|
BuiltinClassLoader loader() { return loader; } |
|
ModuleReference mref() { return mref; } |
|
String name() { return mref.descriptor().name(); } |
|
|
|
URL codeSourceURL() { |
|
URL url = codeSourceURL; |
|
if (url == null && uri != null) { |
|
codeSourceURL = url = createURL(uri); |
|
} |
|
return url; |
|
} |
|
|
|
private URL createURL(URI uri) { |
|
URL url = null; |
|
try { |
|
url = uri.toURL(); |
|
} catch (MalformedURLException | IllegalArgumentException e) { |
|
} |
|
return url; |
|
} |
|
} |
|
|
|
|
|
private static final Map<String, LoadedModule> packageToModule; |
|
static { |
|
ArchivedClassLoaders archivedClassLoaders = ArchivedClassLoaders.get(); |
|
if (archivedClassLoaders != null) { |
|
@SuppressWarnings("unchecked") |
|
Map<String, LoadedModule> map |
|
= (Map<String, LoadedModule>) archivedClassLoaders.packageToModule(); |
|
packageToModule = map; |
|
} else { |
|
packageToModule = new ConcurrentHashMap<>(1024); |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
static Map<String, ?> packageToModule() { |
|
return packageToModule; |
|
} |
|
|
|
|
|
private final Map<String, ModuleReference> nameToModule; |
|
|
|
|
|
private final Map<ModuleReference, ModuleReader> moduleToReader; |
|
|
|
// cache of resource name -> list of URLs. |
|
|
|
private volatile SoftReference<Map<String, List<URL>>> resourceCache; |
|
|
|
|
|
|
|
*/ |
|
BuiltinClassLoader(String name, BuiltinClassLoader parent, URLClassPath ucp) { |
|
|
|
super(name, parent == null || parent == ClassLoaders.bootLoader() ? null : parent); |
|
|
|
this.parent = parent; |
|
this.ucp = ucp; |
|
|
|
this.nameToModule = new ConcurrentHashMap<>(32); |
|
this.moduleToReader = new ConcurrentHashMap<>(); |
|
} |
|
|
|
|
|
|
|
*/ |
|
void appendClassPath(String path) { |
|
|
|
ucp.addFile(path); |
|
} |
|
|
|
|
|
|
|
*/ |
|
void setClassPath(URLClassPath ucp) { |
|
this.ucp = ucp; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
boolean hasClassPath() { |
|
return ucp != null; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public void loadModule(ModuleReference mref) { |
|
ModuleDescriptor descriptor = mref.descriptor(); |
|
String mn = descriptor.name(); |
|
if (nameToModule.putIfAbsent(mn, mref) != null) { |
|
throw new InternalError(mn + " already defined to this loader"); |
|
} |
|
|
|
LoadedModule loadedModule = new LoadedModule(this, mref); |
|
for (String pn : descriptor.packages()) { |
|
LoadedModule other = packageToModule.putIfAbsent(pn, loadedModule); |
|
if (other != null) { |
|
throw new InternalError(pn + " in modules " + mn + " and " |
|
+ other.name()); |
|
} |
|
} |
|
|
|
|
|
if (resourceCache != null && VM.isModuleSystemInited()) { |
|
resourceCache = null; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected ModuleReference findModule(String name) { |
|
return nameToModule.get(name); |
|
} |
|
|
|
|
|
// -- finding resources |
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public URL findResource(String mn, String name) throws IOException { |
|
URL url = null; |
|
|
|
if (mn != null) { |
|
|
|
ModuleReference mref = nameToModule.get(mn); |
|
if (mref != null) { |
|
url = findResource(mref, name); |
|
} |
|
} else { |
|
|
|
url = findResourceOnClassPath(name); |
|
} |
|
|
|
return checkURL(url); |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
@SuppressWarnings("removal") |
|
public InputStream findResourceAsStream(String mn, String name) |
|
throws IOException |
|
{ |
|
// Need URL to resource when running with a security manager so that |
|
|
|
if (System.getSecurityManager() != null || mn == null) { |
|
URL url = findResource(mn, name); |
|
return (url != null) ? url.openStream() : null; |
|
} |
|
|
|
|
|
ModuleReference mref = nameToModule.get(mn); |
|
if (mref != null) { |
|
return moduleReaderFor(mref).open(name).orElse(null); |
|
} else { |
|
return null; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public URL findResource(String name) { |
|
String pn = Resources.toPackageName(name); |
|
LoadedModule module = packageToModule.get(pn); |
|
if (module != null) { |
|
|
|
|
|
if (module.loader() == this) { |
|
URL url; |
|
try { |
|
url = findResource(module.name(), name); |
|
} catch (IOException ioe) { |
|
return null; |
|
} |
|
if (url != null |
|
&& (name.endsWith(".class") |
|
|| url.toString().endsWith("/") |
|
|| isOpen(module.mref(), pn))) { |
|
return url; |
|
} |
|
} |
|
|
|
} else { |
|
|
|
|
|
try { |
|
List<URL> urls = findMiscResource(name); |
|
if (!urls.isEmpty()) { |
|
URL url = urls.get(0); |
|
if (url != null) { |
|
return checkURL(url); |
|
} |
|
} |
|
} catch (IOException ioe) { |
|
return null; |
|
} |
|
|
|
} |
|
|
|
|
|
URL url = findResourceOnClassPath(name); |
|
return checkURL(url); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public Enumeration<URL> findResources(String name) throws IOException { |
|
List<URL> checked = new ArrayList<>(); |
|
|
|
String pn = Resources.toPackageName(name); |
|
LoadedModule module = packageToModule.get(pn); |
|
if (module != null) { |
|
|
|
|
|
if (module.loader() == this) { |
|
URL url = findResource(module.name(), name); |
|
if (url != null |
|
&& (name.endsWith(".class") |
|
|| url.toString().endsWith("/") |
|
|| isOpen(module.mref(), pn))) { |
|
checked.add(url); |
|
} |
|
} |
|
|
|
} else { |
|
|
|
for (URL url : findMiscResource(name)) { |
|
url = checkURL(url); |
|
if (url != null) { |
|
checked.add(url); |
|
} |
|
} |
|
} |
|
|
|
|
|
Enumeration<URL> e = findResourcesOnClassPath(name); |
|
|
|
|
|
return new Enumeration<>() { |
|
final Iterator<URL> iterator = checked.iterator(); |
|
URL next; |
|
private boolean hasNext() { |
|
if (next != null) { |
|
return true; |
|
} else if (iterator.hasNext()) { |
|
next = iterator.next(); |
|
return true; |
|
} else { |
|
|
|
while (e.hasMoreElements() && next == null) { |
|
next = checkURL(e.nextElement()); |
|
} |
|
return next != null; |
|
} |
|
} |
|
@Override |
|
public boolean hasMoreElements() { |
|
return hasNext(); |
|
} |
|
@Override |
|
public URL nextElement() { |
|
if (hasNext()) { |
|
URL result = next; |
|
next = null; |
|
return result; |
|
} else { |
|
throw new NoSuchElementException(); |
|
} |
|
} |
|
}; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@SuppressWarnings("removal") |
|
private List<URL> findMiscResource(String name) throws IOException { |
|
SoftReference<Map<String, List<URL>>> ref = this.resourceCache; |
|
Map<String, List<URL>> map = (ref != null) ? ref.get() : null; |
|
if (map == null) { |
|
|
|
if (VM.isModuleSystemInited()) { |
|
map = new ConcurrentHashMap<>(); |
|
this.resourceCache = new SoftReference<>(map); |
|
} |
|
} else { |
|
List<URL> urls = map.get(name); |
|
if (urls != null) |
|
return urls; |
|
} |
|
|
|
|
|
List<URL> urls; |
|
try { |
|
urls = AccessController.doPrivileged( |
|
new PrivilegedExceptionAction<>() { |
|
@Override |
|
public List<URL> run() throws IOException { |
|
List<URL> result = null; |
|
for (ModuleReference mref : nameToModule.values()) { |
|
URI u = moduleReaderFor(mref).find(name).orElse(null); |
|
if (u != null) { |
|
try { |
|
if (result == null) |
|
result = new ArrayList<>(); |
|
result.add(u.toURL()); |
|
} catch (MalformedURLException | |
|
IllegalArgumentException e) { |
|
} |
|
} |
|
} |
|
return (result != null) ? result : Collections.emptyList(); |
|
} |
|
}); |
|
} catch (PrivilegedActionException pae) { |
|
throw (IOException) pae.getCause(); |
|
} |
|
|
|
|
|
if (map != null) { |
|
map.putIfAbsent(name, urls); |
|
} |
|
|
|
return urls; |
|
} |
|
|
|
|
|
|
|
*/ |
|
@SuppressWarnings("removal") |
|
private URL findResource(ModuleReference mref, String name) throws IOException { |
|
URI u; |
|
if (System.getSecurityManager() == null) { |
|
u = moduleReaderFor(mref).find(name).orElse(null); |
|
} else { |
|
try { |
|
u = AccessController.doPrivileged(new PrivilegedExceptionAction<> () { |
|
@Override |
|
public URI run() throws IOException { |
|
return moduleReaderFor(mref).find(name).orElse(null); |
|
} |
|
}); |
|
} catch (PrivilegedActionException pae) { |
|
throw (IOException) pae.getCause(); |
|
} |
|
} |
|
if (u != null) { |
|
try { |
|
return u.toURL(); |
|
} catch (MalformedURLException | IllegalArgumentException e) { } |
|
} |
|
return null; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private URL findResourceOrNull(ModuleReference mref, String name) { |
|
try { |
|
return findResource(mref, name); |
|
} catch (IOException ignore) { |
|
return null; |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
@SuppressWarnings("removal") |
|
private URL findResourceOnClassPath(String name) { |
|
if (hasClassPath()) { |
|
if (System.getSecurityManager() == null) { |
|
return ucp.findResource(name, false); |
|
} else { |
|
PrivilegedAction<URL> pa = () -> ucp.findResource(name, false); |
|
return AccessController.doPrivileged(pa); |
|
} |
|
} else { |
|
|
|
return null; |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
@SuppressWarnings("removal") |
|
private Enumeration<URL> findResourcesOnClassPath(String name) { |
|
if (hasClassPath()) { |
|
if (System.getSecurityManager() == null) { |
|
return ucp.findResources(name, false); |
|
} else { |
|
PrivilegedAction<Enumeration<URL>> pa; |
|
pa = () -> ucp.findResources(name, false); |
|
return AccessController.doPrivileged(pa); |
|
} |
|
} else { |
|
|
|
return Collections.emptyEnumeration(); |
|
} |
|
} |
|
|
|
// -- finding/loading classes |
|
|
|
|
|
|
|
*/ |
|
@Override |
|
protected Class<?> findClass(String cn) throws ClassNotFoundException { |
|
|
|
if (!VM.isModuleSystemInited()) |
|
throw new ClassNotFoundException(cn); |
|
|
|
|
|
LoadedModule loadedModule = findLoadedModule(cn); |
|
|
|
Class<?> c = null; |
|
if (loadedModule != null) { |
|
|
|
|
|
if (loadedModule.loader() == this) { |
|
c = findClassInModuleOrNull(loadedModule, cn); |
|
} |
|
|
|
} else { |
|
|
|
|
|
if (hasClassPath()) { |
|
c = findClassOnClassPathOrNull(cn); |
|
} |
|
|
|
} |
|
|
|
|
|
if (c == null) |
|
throw new ClassNotFoundException(cn); |
|
|
|
return c; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
protected Class<?> findClass(String mn, String cn) { |
|
if (mn != null) { |
|
|
|
LoadedModule loadedModule = findLoadedModule(mn, cn); |
|
if (loadedModule == null) { |
|
return null; |
|
} |
|
|
|
|
|
assert loadedModule.loader() == this; |
|
return findClassInModuleOrNull(loadedModule, cn); |
|
} |
|
|
|
|
|
if (hasClassPath()) { |
|
return findClassOnClassPathOrNull(cn); |
|
} |
|
|
|
return null; |
|
} |
|
|
|
|
|
|
|
*/ |
|
@Override |
|
protected Class<?> loadClass(String cn, boolean resolve) |
|
throws ClassNotFoundException |
|
{ |
|
Class<?> c = loadClassOrNull(cn, resolve); |
|
if (c == null) |
|
throw new ClassNotFoundException(cn); |
|
return c; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected Class<?> loadClassOrNull(String cn, boolean resolve) { |
|
synchronized (getClassLoadingLock(cn)) { |
|
|
|
Class<?> c = findLoadedClass(cn); |
|
|
|
if (c == null) { |
|
|
|
|
|
LoadedModule loadedModule = findLoadedModule(cn); |
|
if (loadedModule != null) { |
|
|
|
|
|
BuiltinClassLoader loader = loadedModule.loader(); |
|
if (loader == this) { |
|
if (VM.isModuleSystemInited()) { |
|
c = findClassInModuleOrNull(loadedModule, cn); |
|
} |
|
} else { |
|
|
|
c = loader.loadClassOrNull(cn); |
|
} |
|
|
|
} else { |
|
|
|
|
|
if (parent != null) { |
|
c = parent.loadClassOrNull(cn); |
|
} |
|
|
|
|
|
if (c == null && hasClassPath() && VM.isModuleSystemInited()) { |
|
c = findClassOnClassPathOrNull(cn); |
|
} |
|
} |
|
|
|
} |
|
|
|
if (resolve && c != null) |
|
resolveClass(c); |
|
|
|
return c; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected final Class<?> loadClassOrNull(String cn) { |
|
return loadClassOrNull(cn, false); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private LoadedModule findLoadedModule(String cn) { |
|
int pos = cn.lastIndexOf('.'); |
|
if (pos < 0) |
|
return null; |
|
|
|
String pn = cn.substring(0, pos); |
|
return packageToModule.get(pn); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private LoadedModule findLoadedModule(String mn, String cn) { |
|
LoadedModule loadedModule = findLoadedModule(cn); |
|
if (loadedModule != null && mn.equals(loadedModule.name())) { |
|
return loadedModule; |
|
} else { |
|
return null; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@SuppressWarnings("removal") |
|
private Class<?> findClassInModuleOrNull(LoadedModule loadedModule, String cn) { |
|
if (System.getSecurityManager() == null) { |
|
return defineClass(cn, loadedModule); |
|
} else { |
|
PrivilegedAction<Class<?>> pa = () -> defineClass(cn, loadedModule); |
|
return AccessController.doPrivileged(pa); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@SuppressWarnings("removal") |
|
private Class<?> findClassOnClassPathOrNull(String cn) { |
|
String path = cn.replace('.', '/').concat(".class"); |
|
if (System.getSecurityManager() == null) { |
|
Resource res = ucp.getResource(path, false); |
|
if (res != null) { |
|
try { |
|
return defineClass(cn, res); |
|
} catch (IOException ioe) { |
|
// TBD on how I/O errors should be propagated |
|
} |
|
} |
|
return null; |
|
} else { |
|
|
|
PrivilegedAction<Class<?>> pa = new PrivilegedAction<>() { |
|
public Class<?> run() { |
|
Resource res = ucp.getResource(path, false); |
|
if (res != null) { |
|
try { |
|
return defineClass(cn, res); |
|
} catch (IOException ioe) { |
|
// TBD on how I/O errors should be propagated |
|
} |
|
} |
|
return null; |
|
} |
|
}; |
|
return AccessController.doPrivileged(pa); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private Class<?> defineClass(String cn, LoadedModule loadedModule) { |
|
ModuleReference mref = loadedModule.mref(); |
|
ModuleReader reader = moduleReaderFor(mref); |
|
|
|
try { |
|
ByteBuffer bb = null; |
|
URL csURL = null; |
|
|
|
// locate class file, special handling for patched modules to |
|
|
|
String rn = cn.replace('.', '/').concat(".class"); |
|
if (reader instanceof PatchedModuleReader) { |
|
Resource r = ((PatchedModuleReader)reader).findResource(rn); |
|
if (r != null) { |
|
bb = r.getByteBuffer(); |
|
csURL = r.getCodeSourceURL(); |
|
} |
|
} else { |
|
bb = reader.read(rn).orElse(null); |
|
csURL = loadedModule.codeSourceURL(); |
|
} |
|
|
|
if (bb == null) { |
|
|
|
return null; |
|
} |
|
|
|
CodeSource cs = new CodeSource(csURL, (CodeSigner[]) null); |
|
try { |
|
|
|
return defineClass(cn, bb, cs); |
|
|
|
} finally { |
|
reader.release(bb); |
|
} |
|
|
|
} catch (IOException ioe) { |
|
|
|
return null; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private Class<?> defineClass(String cn, Resource res) throws IOException { |
|
URL url = res.getCodeSourceURL(); |
|
|
|
|
|
int pos = cn.lastIndexOf('.'); |
|
if (pos != -1) { |
|
String pn = cn.substring(0, pos); |
|
Manifest man = res.getManifest(); |
|
defineOrCheckPackage(pn, man, url); |
|
} |
|
|
|
|
|
ByteBuffer bb = res.getByteBuffer(); |
|
if (bb != null) { |
|
CodeSigner[] signers = res.getCodeSigners(); |
|
CodeSource cs = new CodeSource(url, signers); |
|
return defineClass(cn, bb, cs); |
|
} else { |
|
byte[] b = res.getBytes(); |
|
CodeSigner[] signers = res.getCodeSigners(); |
|
CodeSource cs = new CodeSource(url, signers); |
|
return defineClass(cn, b, 0, b.length, cs); |
|
} |
|
} |
|
|
|
|
|
// -- packages |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected Package defineOrCheckPackage(String pn, Manifest man, URL url) { |
|
Package pkg = getAndVerifyPackage(pn, man, url); |
|
if (pkg == null) { |
|
try { |
|
if (man != null) { |
|
pkg = definePackage(pn, man, url); |
|
} else { |
|
pkg = definePackage(pn, null, null, null, null, null, null, null); |
|
} |
|
} catch (IllegalArgumentException iae) { |
|
|
|
pkg = getAndVerifyPackage(pn, man, url); |
|
if (pkg == null) |
|
throw new InternalError("Cannot find package: " + pn); |
|
} |
|
} |
|
return pkg; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private Package getAndVerifyPackage(String pn, Manifest man, URL url) { |
|
Package pkg = getDefinedPackage(pn); |
|
if (pkg != null) { |
|
if (pkg.isSealed()) { |
|
if (!pkg.isSealed(url)) { |
|
throw new SecurityException( |
|
"sealing violation: package " + pn + " is sealed"); |
|
} |
|
} else { |
|
|
|
if ((man != null) && isSealed(pn, man)) { |
|
throw new SecurityException( |
|
"sealing violation: can't seal package " + pn + |
|
": already defined"); |
|
} |
|
} |
|
} |
|
return pkg; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private Package definePackage(String pn, Manifest man, URL url) { |
|
String specTitle = null; |
|
String specVersion = null; |
|
String specVendor = null; |
|
String implTitle = null; |
|
String implVersion = null; |
|
String implVendor = null; |
|
String sealed = null; |
|
URL sealBase = null; |
|
|
|
if (man != null) { |
|
Attributes attr = SharedSecrets.javaUtilJarAccess() |
|
.getTrustedAttributes(man, pn.replace('.', '/').concat("/")); |
|
if (attr != null) { |
|
specTitle = attr.getValue(Attributes.Name.SPECIFICATION_TITLE); |
|
specVersion = attr.getValue(Attributes.Name.SPECIFICATION_VERSION); |
|
specVendor = attr.getValue(Attributes.Name.SPECIFICATION_VENDOR); |
|
implTitle = attr.getValue(Attributes.Name.IMPLEMENTATION_TITLE); |
|
implVersion = attr.getValue(Attributes.Name.IMPLEMENTATION_VERSION); |
|
implVendor = attr.getValue(Attributes.Name.IMPLEMENTATION_VENDOR); |
|
sealed = attr.getValue(Attributes.Name.SEALED); |
|
} |
|
|
|
attr = man.getMainAttributes(); |
|
if (attr != null) { |
|
if (specTitle == null) |
|
specTitle = attr.getValue(Attributes.Name.SPECIFICATION_TITLE); |
|
if (specVersion == null) |
|
specVersion = attr.getValue(Attributes.Name.SPECIFICATION_VERSION); |
|
if (specVendor == null) |
|
specVendor = attr.getValue(Attributes.Name.SPECIFICATION_VENDOR); |
|
if (implTitle == null) |
|
implTitle = attr.getValue(Attributes.Name.IMPLEMENTATION_TITLE); |
|
if (implVersion == null) |
|
implVersion = attr.getValue(Attributes.Name.IMPLEMENTATION_VERSION); |
|
if (implVendor == null) |
|
implVendor = attr.getValue(Attributes.Name.IMPLEMENTATION_VENDOR); |
|
if (sealed == null) |
|
sealed = attr.getValue(Attributes.Name.SEALED); |
|
} |
|
|
|
|
|
if ("true".equalsIgnoreCase(sealed)) |
|
sealBase = url; |
|
} |
|
return definePackage(pn, |
|
specTitle, |
|
specVersion, |
|
specVendor, |
|
implTitle, |
|
implVersion, |
|
implVendor, |
|
sealBase); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private boolean isSealed(String pn, Manifest man) { |
|
Attributes attr = SharedSecrets.javaUtilJarAccess() |
|
.getTrustedAttributes(man, pn.replace('.', '/').concat("/")); |
|
String sealed = null; |
|
if (attr != null) |
|
sealed = attr.getValue(Attributes.Name.SEALED); |
|
if (sealed == null && (attr = man.getMainAttributes()) != null) |
|
sealed = attr.getValue(Attributes.Name.SEALED); |
|
return "true".equalsIgnoreCase(sealed); |
|
} |
|
|
|
// -- permissions |
|
|
|
|
|
|
|
*/ |
|
@Override |
|
protected PermissionCollection getPermissions(CodeSource cs) { |
|
return new LazyCodeSourcePermissionCollection(super.getPermissions(cs), cs); |
|
} |
|
|
|
// -- miscellaneous supporting methods |
|
|
|
|
|
|
|
*/ |
|
private ModuleReader moduleReaderFor(ModuleReference mref) { |
|
ModuleReader reader = moduleToReader.get(mref); |
|
if (reader == null) { |
|
|
|
Function<ModuleReference, ModuleReader> create = new Function<>() { |
|
public ModuleReader apply(ModuleReference moduleReference) { |
|
try { |
|
return mref.open(); |
|
} catch (IOException e) { |
|
// Return a null module reader to avoid a future class |
|
|
|
return new NullModuleReader(); |
|
} |
|
} |
|
}; |
|
reader = moduleToReader.computeIfAbsent(mref, create); |
|
} |
|
return reader; |
|
} |
|
|
|
|
|
|
|
*/ |
|
private static class NullModuleReader implements ModuleReader { |
|
@Override |
|
public Optional<URI> find(String name) { |
|
return Optional.empty(); |
|
} |
|
@Override |
|
public Stream<String> list() { |
|
return Stream.empty(); |
|
} |
|
@Override |
|
public void close() { |
|
throw new InternalError("Should not get here"); |
|
} |
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private boolean isOpen(ModuleReference mref, String pn) { |
|
ModuleDescriptor descriptor = mref.descriptor(); |
|
if (descriptor.isOpen() || descriptor.isAutomatic()) |
|
return true; |
|
for (ModuleDescriptor.Opens opens : descriptor.opens()) { |
|
String source = opens.source(); |
|
if (!opens.isQualified() && source.equals(pn)) { |
|
return true; |
|
} |
|
} |
|
return false; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private static URL checkURL(URL url) { |
|
return URLClassPath.checkURL(url); |
|
} |
|
|
|
|
|
private void resetArchivedStates() { |
|
ucp = null; |
|
} |
|
} |