< 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&trade; 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&trade; 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&trade; * 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&trade; * 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 >