/* * 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 * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package java.lang; import java.lang.module.Configuration; import java.lang.module.ModuleDescriptor; import java.lang.module.ResolvedModule; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collections; import java.util.Deque; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; import jdk.internal.loader.ClassLoaderValue; import jdk.internal.loader.Loader; import jdk.internal.loader.LoaderPool; import jdk.internal.module.ServicesCatalog; import sun.security.util.SecurityConstants; /** * A layer of modules in the Java virtual machine. * *

A layer is created from a graph of modules in a {@link Configuration} * and a function that maps each module to a {@link ClassLoader}. * Creating a layer informs the Java virtual machine about the classes that * may be loaded from the modules so that the Java virtual machine knows which * module that each class is a member of.

* *

Creating a layer creates a {@link Module} object for each {@link * ResolvedModule} in the configuration. For each resolved module that is * {@link ResolvedModule#reads() read}, the {@code Module} {@link * Module#canRead reads} the corresponding run-time {@code Module}, which may * be in the same layer or a {@link #parents() parent} layer.

* *

The {@link #defineModulesWithOneLoader defineModulesWithOneLoader} and * {@link #defineModulesWithManyLoaders defineModulesWithManyLoaders} methods * provide convenient ways to create a module layer where all modules are * mapped to a single class loader or where each module is mapped to its own * class loader. The {@link #defineModules defineModules} method is for more * advanced cases where modules are mapped to custom class loaders by means of * a function specified to the method. Each of these methods has an instance * and static variant. The instance methods create a layer with the receiver * as the parent layer. The static methods are for more advanced cases where * there can be more than one parent layer or where a {@link * ModuleLayer.Controller Controller} is needed to control modules in the layer *

* *

A Java virtual machine has at least one non-empty layer, the {@link * #boot() boot} layer, that is created when the Java virtual machine is * started. The boot layer contains module {@code java.base} and is the only * layer in the Java virtual machine with a module named "{@code java.base}". * The modules in the boot layer are mapped to the bootstrap class loader and * other class loaders that are * built-in into the Java virtual machine. The boot layer will often be * the {@link #parents() parent} when creating additional layers.

* *

Each {@code Module} in a layer is created so that it {@link * Module#isExported(String) exports} and {@link Module#isOpen(String) opens} * the packages described by its {@link ModuleDescriptor}. Qualified exports * (where a package is exported to a set of target modules rather than all * modules) are reified when creating the layer as follows:

* * *

Qualified opens are handled in same way as qualified exports.

* *

As when creating a {@code Configuration}, * {@link ModuleDescriptor#isAutomatic() automatic} modules receive special * treatment when creating a layer. An automatic module is created in the * Java virtual machine as a {@code Module} that reads every unnamed {@code * Module} in the Java virtual machine.

* *

Unless otherwise specified, passing a {@code null} argument to a method * in this class causes a {@link NullPointerException NullPointerException} to * be thrown.

* *

Example usage:

* *

This example creates a configuration by resolving a module named * "{@code myapp}" with the configuration for the boot layer as the parent. It * then creates a new layer with the modules in this configuration. All modules * are defined to the same class loader.

* *
{@code
 *     ModuleFinder finder = ModuleFinder.of(dir1, dir2, dir3);
 *
 *     ModuleLayer parent = ModuleLayer.boot();
 *
 *     Configuration cf = parent.configuration().resolve(finder, ModuleFinder.of(), Set.of("myapp"));
 *
 *     ClassLoader scl = ClassLoader.getSystemClassLoader();
 *
 *     ModuleLayer layer = parent.defineModulesWithOneLoader(cf, scl);
 *
 *     Class c = layer.findLoader("myapp").loadClass("app.Main");
 * }
* * @since 9 * @spec JPMS * @see Module#getLayer() */ public final class ModuleLayer { // the empty layer private static final ModuleLayer EMPTY_LAYER = new ModuleLayer(Configuration.empty(), List.of(), null); // the configuration from which this layer was created private final Configuration cf; // parent layers, empty in the case of the empty layer private final List parents; // maps module name to jlr.Module private final Map nameToModule; /** * Creates a new module layer from the modules in the given configuration. */ private ModuleLayer(Configuration cf, List parents, Function clf) { this.cf = cf; this.parents = parents; // no need to do defensive copy Map map; if (parents.isEmpty()) { map = Collections.emptyMap(); } else { map = Module.defineModules(cf, clf, this); } this.nameToModule = map; // no need to do defensive copy } /** * Controls a module layer. The static methods defined by {@link ModuleLayer} * to create module layers return a {@code Controller} that can be used to * control modules in the layer. * *

Unless otherwise specified, passing a {@code null} argument to a * method in this class causes a {@link NullPointerException * NullPointerException} to be thrown.

* * @apiNote Care should be taken with {@code Controller} objects, they * should never be shared with untrusted code. * * @since 9 * @spec JPMS */ public static final class Controller { private final ModuleLayer layer; Controller(ModuleLayer layer) { this.layer = layer; } /** * Returns the layer that this object controls. * * @return the module layer */ public ModuleLayer layer() { return layer; } private void ensureInLayer(Module source) { if (source.getLayer() != layer) throw new IllegalArgumentException(source + " not in layer"); } /** * Updates module {@code source} in the layer to read module * {@code target}. This method is a no-op if {@code source} already * reads {@code target}. * * @implNote Read edges added by this method are weak * and do not prevent {@code target} from being GC'ed when {@code source} * is strongly reachable. * * @param source * The source module * @param target * The target module to read * * @return This controller * * @throws IllegalArgumentException * If {@code source} is not in the module layer * * @see Module#addReads */ public Controller addReads(Module source, Module target) { ensureInLayer(source); source.implAddReads(target); return this; } /** * Updates module {@code source} in the layer to export a package to * module {@code target}. This method is a no-op if {@code source} * already exports the package to at least {@code target}. * * @param source * The source module * @param pn * The package name * @param target * The target module * * @return This controller * * @throws IllegalArgumentException * If {@code source} is not in the module layer or the package * is not in the source module * * @see Module#addExports */ public Controller addExports(Module source, String pn, Module target) { ensureInLayer(source); source.implAddExports(pn, target); return this; } /** * Updates module {@code source} in the layer to open a package to * module {@code target}. This method is a no-op if {@code source} * already opens the package to at least {@code target}. * * @param source * The source module * @param pn * The package name * @param target * The target module * * @return This controller * * @throws IllegalArgumentException * If {@code source} is not in the module layer or the package * is not in the source module * * @see Module#addOpens */ public Controller addOpens(Module source, String pn, Module target) { ensureInLayer(source); source.implAddOpens(pn, target); return this; } } /** * Creates a new module layer, with this layer as its parent, by defining the * modules in the given {@code Configuration} to the Java virtual machine. * This method creates one class loader and defines all modules to that * class loader. The {@link ClassLoader#getParent() parent} of each class * loader is the given parent class loader. This method works exactly as * specified by the static {@link * #defineModulesWithOneLoader(Configuration,List,ClassLoader) * defineModulesWithOneLoader} method when invoked with this layer as the * parent. In other words, if this layer is {@code thisLayer} then this * method is equivalent to invoking: *
 {@code
     *     ModuleLayer.defineModulesWithOneLoader(cf, List.of(thisLayer), parentLoader).layer();
     * }
* * @param cf * The configuration for the layer * @param parentLoader * The parent class loader for the class loader created by this * method; may be {@code null} for the bootstrap class loader * * @return The newly created layer * * @throws IllegalArgumentException * If the given configuration has more than one parent or the parent * of the configuration is not the configuration for this layer * @throws LayerInstantiationException * If the layer cannot be created for any of the reasons specified * by the static {@code defineModulesWithOneLoader} method * @throws SecurityException * If {@code RuntimePermission("createClassLoader")} or * {@code RuntimePermission("getClassLoader")} is denied by * the security manager * * @see #findLoader */ public ModuleLayer defineModulesWithOneLoader(Configuration cf, ClassLoader parentLoader) { return defineModulesWithOneLoader(cf, List.of(this), parentLoader).layer(); } /** * Creates a new module layer, with this layer as its parent, by defining the * modules in the given {@code Configuration} to the Java virtual machine. * Each module is defined to its own {@link ClassLoader} created by this * method. The {@link ClassLoader#getParent() parent} of each class loader * is the given parent class loader. This method works exactly as specified * by the static {@link * #defineModulesWithManyLoaders(Configuration,List,ClassLoader) * defineModulesWithManyLoaders} method when invoked with this layer as the * parent. In other words, if this layer is {@code thisLayer} then this * method is equivalent to invoking: *
 {@code
     *     ModuleLayer.defineModulesWithManyLoaders(cf, List.of(thisLayer), parentLoader).layer();
     * }
* * @param cf * The configuration for the layer * @param parentLoader * The parent class loader for each of the class loaders created by * this method; may be {@code null} for the bootstrap class loader * * @return The newly created layer * * @throws IllegalArgumentException * If the given configuration has more than one parent or the parent * of the configuration is not the configuration for this layer * @throws LayerInstantiationException * If the layer cannot be created for any of the reasons specified * by the static {@code defineModulesWithManyLoaders} method * @throws SecurityException * If {@code RuntimePermission("createClassLoader")} or * {@code RuntimePermission("getClassLoader")} is denied by * the security manager * * @see #findLoader */ public ModuleLayer defineModulesWithManyLoaders(Configuration cf, ClassLoader parentLoader) { return defineModulesWithManyLoaders(cf, List.of(this), parentLoader).layer(); } /** * Creates a new module layer, with this layer as its parent, by defining the * modules in the given {@code Configuration} to the Java virtual machine. * Each module is mapped, by name, to its class loader by means of the * given function. This method works exactly as specified by the static * {@link #defineModules(Configuration,List,Function) defineModules} * method when invoked with this layer as the parent. In other words, if * this layer is {@code thisLayer} then this method is equivalent to * invoking: *
 {@code
     *     ModuleLayer.defineModules(cf, List.of(thisLayer), clf).layer();
     * }
* * @param cf * The configuration for the layer * @param clf * The function to map a module name to a class loader * * @return The newly created layer * * @throws IllegalArgumentException * If the given configuration has more than one parent or the parent * of the configuration is not the configuration for this layer * @throws LayerInstantiationException * If the layer cannot be created for any of the reasons specified * by the static {@code defineModules} method * @throws SecurityException * If {@code RuntimePermission("getClassLoader")} is denied by * the security manager */ public ModuleLayer defineModules(Configuration cf, Function clf) { return defineModules(cf, List.of(this), clf).layer(); } /** * Creates a new module layer by defining the modules in the given {@code * Configuration} to the Java virtual machine. This method creates one * class loader and defines all modules to that class loader. * *

The class loader created by this method implements direct * delegation when loading classes from modules. If the {@link * ClassLoader#loadClass(String, boolean) loadClass} method is invoked to * load a class then it uses the package name of the class to map it to a * module. This may be a module in this layer and hence defined to the same * class loader. It may be a package in a module in a parent layer that is * exported to one or more of the modules in this layer. The class * loader delegates to the class loader of the module, throwing {@code * ClassNotFoundException} if not found by that class loader. * When {@code loadClass} is invoked to load classes that do not map to a * module then it delegates to the parent class loader.

* *

The class loader created by this method locates resources * ({@link ClassLoader#getResource(String) getResource}, {@link * ClassLoader#getResources(String) getResources}, and other resource * methods) in all modules in the layer before searching the parent class * loader.

* *

Attempting to create a layer with all modules defined to the same * class loader can fail for the following reasons: * *

    * *
  • Overlapping packages: Two or more modules in the * configuration have the same package.

  • * *
  • Split delegation: The resulting class loader would * need to delegate to more than one class loader in order to load * classes in a specific package.

  • * *
* *

In addition, a layer cannot be created if the configuration contains * a module named "{@code java.base}", or a module contains a package named * "{@code java}" or a package with a name starting with "{@code java.}".

* *

If there is a security manager then the class loader created by * this method will load classes and resources with privileges that are * restricted by the calling context of this method.

* * @param cf * The configuration for the layer * @param parentLayers * The list of parent layers in search order * @param parentLoader * The parent class loader for the class loader created by this * method; may be {@code null} for the bootstrap class loader * * @return A controller that controls the newly created layer * * @throws IllegalArgumentException * If the parent(s) of the given configuration do not match the * configuration of the parent layers, including order * @throws LayerInstantiationException * If all modules cannot be defined to the same class loader for any * of the reasons listed above * @throws SecurityException * If {@code RuntimePermission("createClassLoader")} or * {@code RuntimePermission("getClassLoader")} is denied by * the security manager * * @see #findLoader */ public static Controller defineModulesWithOneLoader(Configuration cf, List parentLayers, ClassLoader parentLoader) { List parents = new ArrayList<>(parentLayers); checkConfiguration(cf, parents); checkCreateClassLoaderPermission(); checkGetClassLoaderPermission(); try { Loader loader = new Loader(cf.modules(), parentLoader); loader.initRemotePackageMap(cf, parents); ModuleLayer layer = new ModuleLayer(cf, parents, mn -> loader); return new Controller(layer); } catch (IllegalArgumentException | IllegalStateException e) { throw new LayerInstantiationException(e.getMessage()); } } /** * Creates a new module layer by defining the modules in the given {@code * Configuration} to the Java virtual machine. Each module is defined to * its own {@link ClassLoader} created by this method. The {@link * ClassLoader#getParent() parent} of each class loader is the given parent * class loader. * *

The class loaders created by this method implement direct * delegation when loading classes from modules. If the {@link * ClassLoader#loadClass(String, boolean) loadClass} method is invoked to * load a class then it uses the package name of the class to map it to a * module. The package may be in the module defined to the class loader. * The package may be exported by another module in this layer to the * module defined to the class loader. It may be in a package exported by a * module in a parent layer. The class loader delegates to the class loader * of the module, throwing {@code ClassNotFoundException} if not found by * that class loader. When {@code loadClass} is invoked to load a class * that does not map to a module then it delegates to the parent class * loader.

* *

The class loaders created by this method locate resources * ({@link ClassLoader#getResource(String) getResource}, {@link * ClassLoader#getResources(String) getResources}, and other resource * methods) in the module defined to the class loader before searching * the parent class loader.

* *

If there is a security manager then the class loaders created by * this method will load classes and resources with privileges that are * restricted by the calling context of this method.

* * @param cf * The configuration for the layer * @param parentLayers * The list of parent layers in search order * @param parentLoader * The parent class loader for each of the class loaders created by * this method; may be {@code null} for the bootstrap class loader * * @return A controller that controls the newly created layer * * @throws IllegalArgumentException * If the parent(s) of the given configuration do not match the * configuration of the parent layers, including order * @throws LayerInstantiationException * If the layer cannot be created because the configuration contains * a module named "{@code java.base}" or a module contains a package * named "{@code java}" or a package with a name starting with * "{@code java.}" * * @throws SecurityException * If {@code RuntimePermission("createClassLoader")} or * {@code RuntimePermission("getClassLoader")} is denied by * the security manager * * @see #findLoader */ public static Controller defineModulesWithManyLoaders(Configuration cf, List parentLayers, ClassLoader parentLoader) { List parents = new ArrayList<>(parentLayers); checkConfiguration(cf, parents); checkCreateClassLoaderPermission(); checkGetClassLoaderPermission(); LoaderPool pool = new LoaderPool(cf, parents, parentLoader); try { ModuleLayer layer = new ModuleLayer(cf, parents, pool::loaderFor); return new Controller(layer); } catch (IllegalArgumentException | IllegalStateException e) { throw new LayerInstantiationException(e.getMessage()); } } /** * Creates a new module layer by defining the modules in the given {@code * Configuration} to the Java virtual machine. The given function maps each * module in the configuration, by name, to a class loader. Creating the * layer informs the Java virtual machine about the classes that may be * loaded so that the Java virtual machine knows which module that each * class is a member of. * *

The class loader delegation implemented by the class loaders must * respect module readability. The class loaders should be * {@link ClassLoader#registerAsParallelCapable parallel-capable} so as to * avoid deadlocks during class loading. In addition, the entity creating * a new layer with this method should arrange that the class loaders be * ready to load from these modules before there are any attempts to load * classes or resources.

* *

Creating a layer can fail for the following reasons:

* *
    * *
  • Two or more modules with the same package are mapped to the * same class loader.

  • * *
  • A module is mapped to a class loader that already has a * module of the same name defined to it.

  • * *
  • A module is mapped to a class loader that has already * defined types in any of the packages in the module.

  • * *
* *

In addition, a layer cannot be created if the configuration contains * a module named "{@code java.base}", a configuration contains a module * with a package named "{@code java}" or a package name starting with * "{@code java.}", or the function to map a module name to a class loader * returns {@code null} or the {@linkplain ClassLoader#getPlatformClassLoader() * platform class loader}.

* *

If the function to map a module name to class loader throws an error * or runtime exception then it is propagated to the caller of this method. *

* * @apiNote It is implementation specific as to whether creating a layer * with this method is an atomic operation or not. Consequentially it is * possible for this method to fail with some modules, but not all, defined * to the Java virtual machine. * * @param cf * The configuration for the layer * @param parentLayers * The list of parent layers in search order * @param clf * The function to map a module name to a class loader * * @return A controller that controls the newly created layer * * @throws IllegalArgumentException * If the parent(s) of the given configuration do not match the * configuration of the parent layers, including order * @throws LayerInstantiationException * If creating the layer fails for any of the reasons listed above * @throws SecurityException * If {@code RuntimePermission("getClassLoader")} is denied by * the security manager */ public static Controller defineModules(Configuration cf, List parentLayers, Function clf) { List parents = new ArrayList<>(parentLayers); checkConfiguration(cf, parents); Objects.requireNonNull(clf); checkGetClassLoaderPermission(); // The boot layer is checked during module system initialization if (boot() != null) { checkForDuplicatePkgs(cf, clf); } try { ModuleLayer layer = new ModuleLayer(cf, parents, clf); return new Controller(layer); } catch (IllegalArgumentException | IllegalStateException e) { throw new LayerInstantiationException(e.getMessage()); } } /** * Checks that the parent configurations match the configuration of * the parent layers. */ private static void checkConfiguration(Configuration cf, List parentLayers) { Objects.requireNonNull(cf); List parentConfigurations = cf.parents(); if (parentLayers.size() != parentConfigurations.size()) throw new IllegalArgumentException("wrong number of parents"); int index = 0; for (ModuleLayer parent : parentLayers) { if (parent.configuration() != parentConfigurations.get(index)) { throw new IllegalArgumentException( "Parent of configuration != configuration of this Layer"); } index++; } } private static void checkCreateClassLoaderPermission() { SecurityManager sm = System.getSecurityManager(); if (sm != null) sm.checkPermission(SecurityConstants.CREATE_CLASSLOADER_PERMISSION); } private static void checkGetClassLoaderPermission() { SecurityManager sm = System.getSecurityManager(); if (sm != null) sm.checkPermission(SecurityConstants.GET_CLASSLOADER_PERMISSION); } /** * Checks a configuration and the module-to-loader mapping to ensure that * no two modules mapped to the same class loader have the same package. * It also checks that no two automatic modules have the same package. * * @throws LayerInstantiationException */ private static void checkForDuplicatePkgs(Configuration cf, Function clf) { // HashMap allows null keys Map> loaderToPackages = new HashMap<>(); for (ResolvedModule resolvedModule : cf.modules()) { ModuleDescriptor descriptor = resolvedModule.reference().descriptor(); ClassLoader loader = clf.apply(descriptor.name()); Set loaderPackages = loaderToPackages.computeIfAbsent(loader, k -> new HashSet<>()); for (String pkg : descriptor.packages()) { boolean added = loaderPackages.add(pkg); if (!added) { throw fail("More than one module with package %s mapped" + " to the same class loader", pkg); } } } } /** * Creates a LayerInstantiationException with the a message formatted from * the given format string and arguments. */ private static LayerInstantiationException fail(String fmt, Object ... args) { String msg = String.format(fmt, args); return new LayerInstantiationException(msg); } /** * Returns the configuration for this layer. * * @return The configuration for this layer */ public Configuration configuration() { return cf; } /** * Returns the list of this layer's parents unless this is the * {@linkplain #empty empty layer}, which has no parents and so an * empty list is returned. * * @return The list of this layer's parents */ public List parents() { return parents; } /** * Returns an ordered stream of layers. The first element is this layer, * the remaining elements are the parent layers in DFS order. * * @implNote For now, the assumption is that the number of elements will * be very low and so this method does not use a specialized spliterator. */ Stream layers() { List allLayers = this.allLayers; if (allLayers != null) return allLayers.stream(); allLayers = new ArrayList<>(); Set visited = new HashSet<>(); Deque stack = new ArrayDeque<>(); visited.add(this); stack.push(this); while (!stack.isEmpty()) { ModuleLayer layer = stack.pop(); allLayers.add(layer); // push in reverse order for (int i = layer.parents.size() - 1; i >= 0; i--) { ModuleLayer parent = layer.parents.get(i); if (!visited.contains(parent)) { visited.add(parent); stack.push(parent); } } } this.allLayers = allLayers = Collections.unmodifiableList(allLayers); return allLayers.stream(); } private volatile List allLayers; /** * Returns the set of the modules in this layer. * * @return A possibly-empty unmodifiable set of the modules in this layer */ public Set modules() { Set modules = this.modules; if (modules == null) { this.modules = modules = Collections.unmodifiableSet(new HashSet<>(nameToModule.values())); } return modules; } private volatile Set modules; /** * Returns the module with the given name in this layer, or if not in this * layer, the {@linkplain #parents() parent} layers. Finding a module in * parent layers is equivalent to invoking {@code findModule} on each * parent, in search order, until the module is found or all parents have * been searched. In a tree of layers then this is equivalent to * a depth-first search. * * @param name * The name of the module to find * * @return The module with the given name or an empty {@code Optional} * if there isn't a module with this name in this layer or any * parent layer */ public Optional findModule(String name) { Objects.requireNonNull(name); if (this == EMPTY_LAYER) return Optional.empty(); Module m = nameToModule.get(name); if (m != null) return Optional.of(m); return layers() .skip(1) // skip this layer .map(l -> l.nameToModule.get(name)) .filter(Objects::nonNull) .findAny(); } /** * Returns the {@code ClassLoader} for the module with the given name. If * a module of the given name is not in this layer then the {@link #parents() * parent} layers are searched in the manner specified by {@link * #findModule(String) findModule}. * *

If there is a security manager then its {@code checkPermission} * method is called with a {@code RuntimePermission("getClassLoader")} * permission to check that the caller is allowed to get access to the * class loader.

* * @apiNote This method does not return an {@code Optional} * because `null` must be used to represent the bootstrap class loader. * * @param name * The name of the module to find * * @return The ClassLoader that the module is defined to * * @throws IllegalArgumentException if a module of the given name is not * defined in this layer or any parent of this layer * * @throws SecurityException if denied by the security manager */ public ClassLoader findLoader(String name) { Optional om = findModule(name); // can't use map(Module::getClassLoader) as class loader can be null if (om.isPresent()) { return om.get().getClassLoader(); } else { throw new IllegalArgumentException("Module " + name + " not known to this layer"); } } /** * Returns a string describing this module layer. * * @return A possibly empty string describing this module layer */ @Override public String toString() { return modules().stream() .map(Module::getName) .collect(Collectors.joining(", ")); } /** * Returns the empty layer. There are no modules in the empty * layer. It has no parents. * * @return The empty layer */ public static ModuleLayer empty() { return EMPTY_LAYER; } /** * Returns the boot layer. The boot layer contains at least one module, * {@code java.base}. Its parent is the {@link #empty() empty} layer. * * @apiNote This method returns {@code null} during startup and before * the boot layer is fully initialized. * * @return The boot layer */ public static ModuleLayer boot() { return System.bootLayer; } /** * Returns the ServicesCatalog for this Layer, creating it if not * already created. */ ServicesCatalog getServicesCatalog() { ServicesCatalog servicesCatalog = this.servicesCatalog; if (servicesCatalog != null) return servicesCatalog; synchronized (this) { servicesCatalog = this.servicesCatalog; if (servicesCatalog == null) { servicesCatalog = ServicesCatalog.create(); nameToModule.values().forEach(servicesCatalog::register); this.servicesCatalog = servicesCatalog; } } return servicesCatalog; } private volatile ServicesCatalog servicesCatalog; /** * Record that this layer has at least one module defined to the given * class loader. */ void bindToLoader(ClassLoader loader) { // CLV.computeIfAbsent(loader, (cl, clv) -> new CopyOnWriteArrayList<>()) List list = CLV.get(loader); if (list == null) { list = new CopyOnWriteArrayList<>(); List previous = CLV.putIfAbsent(loader, list); if (previous != null) list = previous; } list.add(this); } /** * Returns a stream of the layers that have at least one module defined to * the given class loader. */ static Stream layers(ClassLoader loader) { List list = CLV.get(loader); if (list != null) { return list.stream(); } else { return Stream.empty(); } } // the list of layers with modules defined to a class loader private static final ClassLoaderValue> CLV = new ClassLoaderValue<>(); }