< prev index next >
src/java.base/share/classes/java/lang/reflect/Module.java
Print this page
*** 1,7 ****
/*
! * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
--- 1,7 ----
/*
! * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
*** 37,47 ****
import java.lang.module.ResolvedModule;
import java.net.URI;
import java.net.URL;
import java.security.AccessController;
import java.security.PrivilegedAction;
- import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
--- 37,46 ----
*** 72,98 ****
*
* <p> Named modules have a {@link #getName() name} and are constructed by the
* Java Virtual Machine when a graph of modules is defined to the Java virtual
* machine to create a module {@link Layer Layer}. </p>
*
! * <p> An unnamed module does not have a name. There is an unnamed module
! * per {@link ClassLoader ClassLoader} that is obtained by invoking the class
! * loader's {@link ClassLoader#getUnnamedModule() getUnnamedModule} method. The
! * {@link Class#getModule() getModule} method of all types defined by a class
! * loader that are not in a named module return the class loader's unnamed
* module. </p>
*
* <p> The package names that are parameters or returned by methods defined in
* this class are the fully-qualified names of the packages as defined in
! * section 6.5.3 of <cite>The Java™ Language Specification </cite>, for
* example, {@code "java.lang"}. </p>
*
* <p> Unless otherwise specified, passing a {@code null} argument to a method
* in this class causes a {@link NullPointerException NullPointerException} to
* be thrown. </p>
*
* @since 9
* @see java.lang.Class#getModule
*/
public final class Module implements AnnotatedElement {
--- 71,97 ----
*
* <p> Named modules have a {@link #getName() name} and are constructed by the
* Java Virtual Machine when a graph of modules is defined to the Java virtual
* machine to create a module {@link Layer Layer}. </p>
*
! * <p> An unnamed module does not have a name. There is an unnamed module for
! * each {@link ClassLoader ClassLoader}, obtained by invoking its {@link
! * ClassLoader#getUnnamedModule() getUnnamedModule} method. All types that are
! * not in a named module are members of their defining class loader's unnamed
* module. </p>
*
* <p> The package names that are parameters or returned by methods defined in
* this class are the fully-qualified names of the packages as defined in
! * section 6.5.3 of <cite>The Java™ Language Specification</cite>, for
* example, {@code "java.lang"}. </p>
*
* <p> Unless otherwise specified, passing a {@code null} argument to a method
* in this class causes a {@link NullPointerException NullPointerException} to
* be thrown. </p>
*
* @since 9
+ * @spec JPMS
* @see java.lang.Class#getModule
*/
public final class Module implements AnnotatedElement {
*** 126,143 ****
boolean isOpen = descriptor.isOpen();
Version version = descriptor.version().orElse(null);
String vs = Objects.toString(version, null);
String loc = Objects.toString(uri, null);
! Set<String> packages = descriptor.packages();
! int n = packages.size();
! String[] array = new String[n];
! int i = 0;
! for (String pn : packages) {
! array[i++] = pn.replace('.', '/');
! }
! defineModule0(this, isOpen, vs, loc, array);
}
/**
* Create the unnamed Module for the given ClassLoader.
--- 125,136 ----
boolean isOpen = descriptor.isOpen();
Version version = descriptor.version().orElse(null);
String vs = Objects.toString(version, null);
String loc = Objects.toString(uri, null);
! String[] packages = descriptor.packages().toArray(new String[0]);
! defineModule0(this, isOpen, vs, loc, packages);
}
/**
* Create the unnamed Module for the given ClassLoader.
*** 331,352 ****
* @param other
* The other module
*
* @return this module
*
! * @throws IllegalStateException
! * If this is a named module and the caller is not this module
*
* @see #canRead
*/
@CallerSensitive
public Module addReads(Module other) {
Objects.requireNonNull(other);
if (this.isNamed()) {
Module caller = Reflection.getCallerClass().getModule();
if (caller != this) {
! throw new IllegalStateException(caller + " != " + this);
}
implAddReads(other, true);
}
return this;
}
--- 324,346 ----
* @param other
* The other module
*
* @return this module
*
! * @throws IllegalCallerException
! * If this is a named module and the caller's module is not this
! * module
*
* @see #canRead
*/
@CallerSensitive
public Module addReads(Module other) {
Objects.requireNonNull(other);
if (this.isNamed()) {
Module caller = Reflection.getCallerClass().getModule();
if (caller != this) {
! throw new IllegalCallerException(caller + " != " + this);
}
implAddReads(other, true);
}
return this;
}
*** 537,548 ****
// all packages are exported/open to self
if (other == this && containsPackage(pn))
return true;
! // all packages in open modules are open
! if (descriptor.isOpen())
return containsPackage(pn);
// exported/opened via module declaration/descriptor
if (isStaticallyExportedOrOpen(pn, other, open))
return true;
--- 531,542 ----
// all packages are exported/open to self
if (other == this && containsPackage(pn))
return true;
! // all packages in open and automatic modules are open
! if (descriptor.isOpen() || descriptor.isAutomatic())
return containsPackage(pn);
// exported/opened via module declaration/descriptor
if (isStaticallyExportedOrOpen(pn, other, open))
return true;
*** 638,649 ****
/**
* If the caller's module is this module then update this module to export
* the given package to the given module.
*
* <p> This method has no effect if the package is already exported (or
! * <em>open</em>) to the given module. It also has no effect if
! * invoked on an {@link ModuleDescriptor#isOpen open} module. </p>
*
* @apiNote As specified in section 5.4.3 of the <cite>The Java™
* Virtual Machine Specification </cite>, if an attempt to resolve a
* symbolic reference fails because of a linkage error, then subsequent
* attempts to resolve the reference always fail with the same error that
--- 632,642 ----
/**
* If the caller's module is this module then update this module to export
* the given package to the given module.
*
* <p> This method has no effect if the package is already exported (or
! * <em>open</em>) to the given module. </p>
*
* @apiNote As specified in section 5.4.3 of the <cite>The Java™
* Virtual Machine Specification </cite>, if an attempt to resolve a
* symbolic reference fails because of a linkage error, then subsequent
* attempts to resolve the reference always fail with the same error that
*** 657,682 ****
* @return this module
*
* @throws IllegalArgumentException
* If {@code pn} is {@code null}, or this is a named module and the
* package {@code pn} is not a package in this module
! * @throws IllegalStateException
! * If this is a named module and the caller is not this module
*
* @jvms 5.4.3 Resolution
* @see #isExported(String,Module)
*/
@CallerSensitive
public Module addExports(String pn, Module other) {
if (pn == null)
throw new IllegalArgumentException("package is null");
Objects.requireNonNull(other);
! if (isNamed() && !descriptor.isOpen()) {
Module caller = Reflection.getCallerClass().getModule();
if (caller != this) {
! throw new IllegalStateException(caller + " != " + this);
}
implAddExportsOrOpens(pn, other, /*open*/false, /*syncVM*/true);
}
return this;
--- 650,676 ----
* @return this module
*
* @throws IllegalArgumentException
* If {@code pn} is {@code null}, or this is a named module and the
* package {@code pn} is not a package in this module
! * @throws IllegalCallerException
! * If this is a named module and the caller's module is not this
! * module
*
* @jvms 5.4.3 Resolution
* @see #isExported(String,Module)
*/
@CallerSensitive
public Module addExports(String pn, Module other) {
if (pn == null)
throw new IllegalArgumentException("package is null");
Objects.requireNonNull(other);
! if (isNamed()) {
Module caller = Reflection.getCallerClass().getModule();
if (caller != this) {
! throw new IllegalCallerException(caller + " != " + this);
}
implAddExportsOrOpens(pn, other, /*open*/false, /*syncVM*/true);
}
return this;
*** 690,701 ****
* to be reflected on by the given module when using APIs that support
* private access or a way to bypass or suppress default Java language
* access control checks.
*
* <p> This method has no effect if the package is already <em>open</em>
! * to the given module. It also has no effect if invoked on an {@link
! * ModuleDescriptor#isOpen open} module. </p>
*
* @param pn
* The package name
* @param other
* The module
--- 684,694 ----
* to be reflected on by the given module when using APIs that support
* private access or a way to bypass or suppress default Java language
* access control checks.
*
* <p> This method has no effect if the package is already <em>open</em>
! * to the given module. </p>
*
* @param pn
* The package name
* @param other
* The module
*** 703,715 ****
* @return this module
*
* @throws IllegalArgumentException
* If {@code pn} is {@code null}, or this is a named module and the
* package {@code pn} is not a package in this module
! * @throws IllegalStateException
* If this is a named module and this module has not opened the
! * package to at least the caller
*
* @see #isOpen(String,Module)
* @see AccessibleObject#setAccessible(boolean)
* @see java.lang.invoke.MethodHandles#privateLookupIn
*/
--- 696,708 ----
* @return this module
*
* @throws IllegalArgumentException
* If {@code pn} is {@code null}, or this is a named module and the
* package {@code pn} is not a package in this module
! * @throws IllegalCallerException
* If this is a named module and this module has not opened the
! * package to at least the caller's module
*
* @see #isOpen(String,Module)
* @see AccessibleObject#setAccessible(boolean)
* @see java.lang.invoke.MethodHandles#privateLookupIn
*/
*** 717,730 ****
public Module addOpens(String pn, Module other) {
if (pn == null)
throw new IllegalArgumentException("package is null");
Objects.requireNonNull(other);
! if (isNamed() && !descriptor.isOpen()) {
Module caller = Reflection.getCallerClass().getModule();
if (caller != this && !isOpen(pn, caller))
! throw new IllegalStateException(pn + " is not open to " + caller);
implAddExportsOrOpens(pn, other, /*open*/true, /*syncVM*/true);
}
return this;
}
--- 710,723 ----
public Module addOpens(String pn, Module other) {
if (pn == null)
throw new IllegalArgumentException("package is null");
Objects.requireNonNull(other);
! if (isNamed()) {
Module caller = Reflection.getCallerClass().getModule();
if (caller != this && !isOpen(pn, caller))
! throw new IllegalCallerException(pn + " is not open to " + caller);
implAddExportsOrOpens(pn, other, /*open*/true, /*syncVM*/true);
}
return this;
}
*** 771,782 ****
boolean open,
boolean syncVM) {
Objects.requireNonNull(other);
Objects.requireNonNull(pn);
! // all packages are open in unnamed and open modules
! if (!isNamed() || descriptor.isOpen())
return;
// nothing to do if already exported/open to other
if (implIsExportedOrOpen(pn, other, open))
return;
--- 764,775 ----
boolean open,
boolean syncVM) {
Objects.requireNonNull(other);
Objects.requireNonNull(pn);
! // all packages are open in unnamed, open, and automatic modules
! if (!isNamed() || descriptor.isOpen() || descriptor.isAutomatic())
return;
// nothing to do if already exported/open to other
if (implIsExportedOrOpen(pn, other, open))
return;
*** 787,803 ****
+ " not in contents");
}
// update VM first, just in case it fails
if (syncVM) {
- String pkgInternalForm = pn.replace('.', '/');
if (other == EVERYONE_MODULE) {
! addExportsToAll0(this, pkgInternalForm);
} else if (other == ALL_UNNAMED_MODULE) {
! addExportsToAllUnnamed0(this, pkgInternalForm);
} else {
! addExports0(this, pkgInternalForm, other);
}
}
// add package name to reflectivelyExports if absent
Map<String, Boolean> map = reflectivelyExports
--- 780,795 ----
+ " not in contents");
}
// update VM first, just in case it fails
if (syncVM) {
if (other == EVERYONE_MODULE) {
! addExportsToAll0(this, pn);
} else if (other == ALL_UNNAMED_MODULE) {
! addExportsToAllUnnamed0(this, pn);
} else {
! addExports0(this, pn, other);
}
}
// add package name to reflectivelyExports if absent
Map<String, Boolean> map = reflectivelyExports
*** 824,844 ****
* for use by frameworks that invoke {@link java.util.ServiceLoader
* ServiceLoader} on behalf of other modules or where the framework is
* passed a reference to the service type by other code. This method is
* a no-op when invoked on an unnamed module or an automatic module.
*
! * <p> This method does not cause {@link
! * Configuration#resolveRequiresAndUses resolveRequiresAndUses} to be
! * re-run. </p>
*
* @param service
* The service type
*
* @return this module
*
! * @throws IllegalStateException
! * If this is a named module and the caller is not this module
*
* @see #canUse(Class)
* @see ModuleDescriptor#uses()
*/
@CallerSensitive
--- 816,836 ----
* for use by frameworks that invoke {@link java.util.ServiceLoader
* ServiceLoader} on behalf of other modules or where the framework is
* passed a reference to the service type by other code. This method is
* a no-op when invoked on an unnamed module or an automatic module.
*
! * <p> This method does not cause {@link Configuration#resolveAndBind
! * resolveAndBind} to be re-run. </p>
*
* @param service
* The service type
*
* @return this module
*
! * @throws IllegalCallerException
! * If this is a named module and the caller's module is not this
! * module
*
* @see #canUse(Class)
* @see ModuleDescriptor#uses()
*/
@CallerSensitive
*** 846,856 ****
Objects.requireNonNull(service);
if (isNamed() && !descriptor.isAutomatic()) {
Module caller = Reflection.getCallerClass().getModule();
if (caller != this) {
! throw new IllegalStateException(caller + " != " + this);
}
implAddUses(service);
}
return this;
--- 838,848 ----
Objects.requireNonNull(service);
if (isNamed() && !descriptor.isAutomatic()) {
Module caller = Reflection.getCallerClass().getModule();
if (caller != this) {
! throw new IllegalCallerException(caller + " != " + this);
}
implAddUses(service);
}
return this;
*** 899,916 ****
// -- packages --
// Additional packages that are added to the module at run-time.
! // The field is volatile as it may be replaced at run-time
! private volatile Set<String> extraPackages;
private boolean containsPackage(String pn) {
if (descriptor.packages().contains(pn))
return true;
! Set<String> extraPackages = this.extraPackages;
! if (extraPackages != null && extraPackages.contains(pn))
return true;
return false;
}
--- 891,907 ----
// -- packages --
// Additional packages that are added to the module at run-time.
! private volatile Map<String, Boolean> extraPackages;
private boolean containsPackage(String pn) {
if (descriptor.packages().contains(pn))
return true;
! Map<String, Boolean> extraPackages = this.extraPackages;
! if (extraPackages != null && extraPackages.containsKey(pn))
return true;
return false;
}
*** 920,930 ****
* <p> For named modules, the returned array contains an element for each
* package in the module. It may contain elements corresponding to packages
* added to the module, <a href="Proxy.html#dynamicmodule">dynamic modules</a>
* for example, after it was loaded.
*
! * <p> For unnamed modules, this method is the equivalent of invoking the
* {@link ClassLoader#getDefinedPackages() getDefinedPackages} method of
* this module's class loader and returning the array of package names. </p>
*
* <p> A package name appears at most once in the returned array. </p>
*
--- 911,921 ----
* <p> For named modules, the returned array contains an element for each
* package in the module. It may contain elements corresponding to packages
* added to the module, <a href="Proxy.html#dynamicmodule">dynamic modules</a>
* for example, after it was loaded.
*
! * <p> For unnamed modules, this method is the equivalent to invoking the
* {@link ClassLoader#getDefinedPackages() getDefinedPackages} method of
* this module's class loader and returning the array of package names. </p>
*
* <p> A package name appears at most once in the returned array. </p>
*
*** 935,950 ****
*/
public String[] getPackages() {
if (isNamed()) {
Set<String> packages = descriptor.packages();
! Set<String> extraPackages = this.extraPackages;
if (extraPackages == null) {
return packages.toArray(new String[0]);
} else {
return Stream.concat(packages.stream(),
! extraPackages.stream())
.toArray(String[]::new);
}
} else {
// unnamed module
--- 926,941 ----
*/
public String[] getPackages() {
if (isNamed()) {
Set<String> packages = descriptor.packages();
! Map<String, Boolean> extraPackages = this.extraPackages;
if (extraPackages == null) {
return packages.toArray(new String[0]);
} else {
return Stream.concat(packages.stream(),
! extraPackages.keySet().stream())
.toArray(String[]::new);
}
} else {
// unnamed module
*** 960,973 ****
/**
* Add a package to this module.
*
* @apiNote This method is for Proxy use.
- *
- * @apiNote This is an expensive operation, not expected to be used often.
- * At this time then it does not validate that the package name is a
- * valid java identifier.
*/
void addPackage(String pn) {
implAddPackage(pn, true);
}
--- 951,960 ----
*** 981,1034 ****
}
/**
* Add a package to this module.
*
! * If {@code syncVM} is {@code true} then the VM is notified.
*/
private void implAddPackage(String pn, boolean syncVM) {
if (!isNamed())
- throw new InternalError("adding package to unnamed module?");
- if (descriptor.isOpen())
- throw new InternalError("adding package to open module?");
- if (pn.isEmpty())
- throw new InternalError("adding <unnamed> package to module?");
-
- if (descriptor.packages().contains(pn)) {
- // already in module
return;
- }
! Set<String> extraPackages = this.extraPackages;
! if (extraPackages != null && extraPackages.contains(pn)) {
! // already added
return;
}
synchronized (this) {
- // recheck under lock
extraPackages = this.extraPackages;
! if (extraPackages != null) {
! if (extraPackages.contains(pn)) {
! // already added
! return;
}
-
- // copy the set
- extraPackages = new HashSet<>(extraPackages);
- extraPackages.add(pn);
- } else {
- extraPackages = Collections.singleton(pn);
}
! // update VM first, just in case it fails
! if (syncVM)
! addPackage0(this, pn.replace('.', '/'));
!
! // replace with new set
! this.extraPackages = extraPackages; // volatile write
}
}
// -- creating Module objects --
/**
--- 968,1024 ----
}
/**
* Add a package to this module.
*
! * If {@code syncVM} is {@code true} then the VM is notified. This method is
! * a no-op if this is an unnamed module or the module already contains the
! * package.
! *
! * @throws IllegalArgumentException if the package name is not legal
! * @throws IllegalStateException if the package is defined to another module
*/
private void implAddPackage(String pn, boolean syncVM) {
+ // no-op if unnamed module
if (!isNamed())
return;
! // no-op if module contains the package
! if (containsPackage(pn))
return;
+
+ // check package name is legal for named modules
+ if (pn.isEmpty())
+ throw new IllegalArgumentException("Cannot add <unnamed> package");
+ for (int i=0; i<pn.length(); i++) {
+ char c = pn.charAt(i);
+ if (c == '/' || c == ';' || c == '[') {
+ throw new IllegalArgumentException("Illegal character: " + c);
}
+ }
+
+ // create extraPackages if needed
+ Map<String, Boolean> extraPackages = this.extraPackages;
+ if (extraPackages == null) {
synchronized (this) {
extraPackages = this.extraPackages;
! if (extraPackages == null)
! this.extraPackages = extraPackages = new ConcurrentHashMap<>();
}
}
! // update VM first in case it fails. This is a no-op if another thread
! // beats us to add the package first
! if (syncVM) {
! // throws IllegalStateException if defined to another module
! addPackage0(this, pn);
! if (descriptor.isOpen() || descriptor.isAutomatic()) {
! addExportsToAll0(this, pn);
}
}
+ extraPackages.putIfAbsent(pn, Boolean.TRUE);
+ }
// -- creating Module objects --
/**
*** 1174,1226 ****
*/
private static void initExportsAndOpens(ModuleDescriptor descriptor,
Map<String, Module> nameToModule,
Module m)
{
! // The VM doesn't know about open modules so need to export all packages
! if (descriptor.isOpen()) {
assert descriptor.opens().isEmpty();
for (String source : descriptor.packages()) {
! String sourceInternalForm = source.replace('.', '/');
! addExportsToAll0(m, sourceInternalForm);
}
return;
}
Map<String, Set<Module>> openPackages = new HashMap<>();
Map<String, Set<Module>> exportedPackages = new HashMap<>();
// process the open packages first
for (Opens opens : descriptor.opens()) {
String source = opens.source();
- String sourceInternalForm = source.replace('.', '/');
if (opens.isQualified()) {
// qualified opens
Set<Module> targets = new HashSet<>();
for (String target : opens.targets()) {
// only open to modules that are in this configuration
Module m2 = nameToModule.get(target);
if (m2 != null) {
! addExports0(m, sourceInternalForm, m2);
targets.add(m2);
}
}
if (!targets.isEmpty()) {
openPackages.put(source, targets);
}
} else {
// unqualified opens
! addExportsToAll0(m, sourceInternalForm);
openPackages.put(source, EVERYONE_SET);
}
}
// next the exports, skipping exports when the package is open
for (Exports exports : descriptor.exports()) {
String source = exports.source();
- String sourceInternalForm = source.replace('.', '/');
// skip export if package is already open to everyone
Set<Module> openToTargets = openPackages.get(source);
if (openToTargets != null && openToTargets.contains(EVERYONE_MODULE))
continue;
--- 1164,1214 ----
*/
private static void initExportsAndOpens(ModuleDescriptor descriptor,
Map<String, Module> nameToModule,
Module m)
{
! // The VM doesn't special case open or automatic modules so need to
! // export all packages
! if (descriptor.isOpen() || descriptor.isAutomatic()) {
assert descriptor.opens().isEmpty();
for (String source : descriptor.packages()) {
! addExportsToAll0(m, source);
}
return;
}
Map<String, Set<Module>> openPackages = new HashMap<>();
Map<String, Set<Module>> exportedPackages = new HashMap<>();
// process the open packages first
for (Opens opens : descriptor.opens()) {
String source = opens.source();
if (opens.isQualified()) {
// qualified opens
Set<Module> targets = new HashSet<>();
for (String target : opens.targets()) {
// only open to modules that are in this configuration
Module m2 = nameToModule.get(target);
if (m2 != null) {
! addExports0(m, source, m2);
targets.add(m2);
}
}
if (!targets.isEmpty()) {
openPackages.put(source, targets);
}
} else {
// unqualified opens
! addExportsToAll0(m, source);
openPackages.put(source, EVERYONE_SET);
}
}
// next the exports, skipping exports when the package is open
for (Exports exports : descriptor.exports()) {
String source = exports.source();
// skip export if package is already open to everyone
Set<Module> openToTargets = openPackages.get(source);
if (openToTargets != null && openToTargets.contains(EVERYONE_MODULE))
continue;
*** 1232,1253 ****
// only export to modules that are in this configuration
Module m2 = nameToModule.get(target);
if (m2 != null) {
// skip qualified export if already open to m2
if (openToTargets == null || !openToTargets.contains(m2)) {
! addExports0(m, sourceInternalForm, m2);
targets.add(m2);
}
}
}
if (!targets.isEmpty()) {
exportedPackages.put(source, targets);
}
} else {
// unqualified exports
! addExportsToAll0(m, sourceInternalForm);
exportedPackages.put(source, EVERYONE_SET);
}
}
if (!openPackages.isEmpty())
--- 1220,1241 ----
// only export to modules that are in this configuration
Module m2 = nameToModule.get(target);
if (m2 != null) {
// skip qualified export if already open to m2
if (openToTargets == null || !openToTargets.contains(m2)) {
! addExports0(m, source, m2);
targets.add(m2);
}
}
}
if (!targets.isEmpty()) {
exportedPackages.put(source, targets);
}
} else {
// unqualified exports
! addExportsToAll0(m, source);
exportedPackages.put(source, EVERYONE_SET);
}
}
if (!openPackages.isEmpty())
*** 1383,1435 ****
// -- misc --
/**
! * Returns an input stream for reading a resource in this module. The
! * {@code name} parameter is a {@code '/'}-separated path name that
! * identifies the resource.
*
! * <p> A resource in a named modules may be <em>encapsulated</em> so that
* it cannot be located by code in other modules. Whether a resource can be
! * located or not is determined as follows:
*
* <ul>
! * <li> The <em>package name</em> of the resource is derived from the
! * subsequence of characters that precedes the last {@code '/'} and then
! * replacing each {@code '/'} character in the subsequence with
! * {@code '.'}. For example, the package name derived for a resource
! * named "{@code a/b/c/foo.properties}" is "{@code a.b.c}". </li>
! *
! * <li> If the package name is a package in the module then the package
! * must be {@link #isOpen open} the module of the caller of this method.
! * If the package is not in the module then the resource is not
! * encapsulated. Resources in the unnamed package or "{@code META-INF}",
! * for example, are never encapsulated because they can never be
! * packages in a named module. </li>
*
! * <li> As a special case, resources ending with "{@code .class}" are
! * never encapsulated. </li>
* </ul>
*
* <p> This method returns {@code null} if the resource is not in this
* module, the resource is encapsulated and cannot be located by the caller,
! * or access to the resource is denied by the security manager.
*
* @param name
* The resource name
*
* @return An input stream for reading the resource or {@code null}
*
* @throws IOException
* If an I/O error occurs
*
! * @see java.lang.module.ModuleReader#open(String)
*/
@CallerSensitive
public InputStream getResourceAsStream(String name) throws IOException {
! Objects.requireNonNull(name);
if (isNamed() && !ResourceHelper.isSimpleResource(name)) {
Module caller = Reflection.getCallerClass().getModule();
if (caller != this && caller != Object.class.getModule()) {
// ignore packages added for proxies via addPackage
--- 1371,1434 ----
// -- misc --
/**
! * Returns an input stream for reading a resource in this module.
! * The {@code name} parameter is a {@code '/'}-separated path name that
! * identifies the resource. As with {@link Class#getResourceAsStream
! * Class.getResourceAsStream}, this method delegates to the module's class
! * loader {@link ClassLoader#findResource(String,String)
! * findResource(String,String)} method, invoking it with the module name
! * (or {@code null} when the module is unnamed) and the name of the
! * resource. If the resource name has a leading slash then it is dropped
! * before delegation.
*
! * <p> A resource in a named module may be <em>encapsulated</em> so that
* it cannot be located by code in other modules. Whether a resource can be
! * located or not is determined as follows: </p>
*
* <ul>
! * <li> If the resource name ends with "{@code .class}" then it is not
! * encapsulated. </li>
*
! * <li> A <em>package name</em> is derived from the resource name. If
! * the package name is a {@link #getPackages() package} in the module
! * then the resource can only be located by the caller of this method
! * when the package is {@link #isOpen(String,Module) open} to at least
! * the caller's module. If the resource is not in a package in the module
! * then the resource is not encapsulated. </li>
* </ul>
*
+ * <p> In the above, the <em>package name</em> for a resource is derived
+ * from the subsequence of characters that precedes the last {@code '/'} in
+ * the name and then replacing each {@code '/'} character in the subsequence
+ * with {@code '.'}. A leading slash is ignored when deriving the package
+ * name. As an example, the package name derived for a resource named
+ * "{@code a/b/c/foo.properties}" is "{@code a.b.c}". A resource name
+ * with the name "{@code META-INF/MANIFEST.MF}" is never encapsulated
+ * because "{@code META-INF}" is not a legal package name. </p>
+ *
* <p> This method returns {@code null} if the resource is not in this
* module, the resource is encapsulated and cannot be located by the caller,
! * or access to the resource is denied by the security manager. </p>
*
* @param name
* The resource name
*
* @return An input stream for reading the resource or {@code null}
*
* @throws IOException
* If an I/O error occurs
*
! * @see Class#getResourceAsStream(String)
*/
@CallerSensitive
public InputStream getResourceAsStream(String name) throws IOException {
! if (name.startsWith("/")) {
! name = name.substring(1);
! }
if (isNamed() && !ResourceHelper.isSimpleResource(name)) {
Module caller = Reflection.getCallerClass().getModule();
if (caller != this && caller != Object.class.getModule()) {
// ignore packages added for proxies via addPackage
< prev index next >