--- a/jdk/src/java.base/share/classes/java/lang/reflect/Layer.java Wed Nov 23 16:16:35 2016 +0000
+++ b/jdk/src/java.base/share/classes/java/lang/reflect/Layer.java Thu Dec 01 08:57:53 2016 +0000
@@ -27,23 +27,29 @@
import java.lang.module.Configuration;
import java.lang.module.ModuleDescriptor;
-import java.lang.module.ModuleDescriptor.Provides;
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.misc.SharedSecrets;
+import jdk.internal.module.Modules;
import jdk.internal.module.ServicesCatalog;
-import jdk.internal.module.ServicesCatalog.ServiceProvider;
import sun.security.util.SecurityConstants;
@@ -55,7 +61,7 @@
* Creating a layer informs the Java virtual machine about the classes that
* may be loaded from modules so that the Java virtual machine knows which
* module that each class is a member of. Each layer, except the {@link
- * #empty() empty} layer, has a {@link #parent() parent}. </p>
+ * #empty() empty} layer, has at least one {@link #parents() parent}. </p>
*
* <p> Creating a layer creates a {@link Module} object for each {@link
* ResolvedModule} in the configuration. For each resolved module that is
@@ -71,7 +77,11 @@
* 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. </p>
+ * 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 a {@link Layer.Controller
+ * Controller} is needed to control modules in the layer. </p>
*
* <p> 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
@@ -80,7 +90,7 @@
* The modules in the boot layer are mapped to the bootstrap class loader and
* other class loaders that are <a href="../ClassLoader.html#builtinLoaders">
* built-in</a> into the Java virtual machine. The boot layer will often be
- * the {@link #parent() parent} when creating additional layers. </p>
+ * the {@link #parents() parent} when creating additional layers. </p>
*
* <p> As when creating a {@code Configuration},
* {@link ModuleDescriptor#isAutomatic() automatic} modules receive
@@ -123,30 +133,29 @@
// the empty Layer
private static final Layer EMPTY_LAYER
- = new Layer(Configuration.empty(), null, null);
+ = new Layer(Configuration.empty(), List.of(), null);
// the configuration from which this Layer was created
private final Configuration cf;
- // parent layer, null in the case of the empty layer
- private final Layer parent;
+ // parent layers, empty in the case of the empty layer
+ private final List<Layer> parents;
// maps module name to jlr.Module
private final Map<String, Module> nameToModule;
-
/**
* Creates a new Layer from the modules in the given configuration.
*/
private Layer(Configuration cf,
- Layer parent,
+ List<Layer> parents,
Function<String, ClassLoader> clf)
{
this.cf = cf;
- this.parent = parent;
+ this.parents = parents; // no need to do defensive copy
Map<String, Module> map;
- if (parent == null) {
+ if (parents.isEmpty()) {
map = Collections.emptyMap();
} else {
map = Module.defineModules(cf, clf, this);
@@ -154,12 +163,230 @@
this.nameToModule = map; // no need to do defensive copy
}
+ /**
+ * Controls a layer. The static methods defined by {@link Layer} to create
+ * module layers return a {@code Controller} that can be used to control
+ * modules in the layer.
+ *
+ * @apiNote Care should be taken with {@code Controller} objects, they
+ * should never be shared with untrusted code.
+ *
+ * @since 9
+ */
+ public static final class Controller {
+ private final Layer layer;
+
+ Controller(Layer layer) {
+ this.layer = layer;
+ }
+
+ /**
+ * Returns the layer that this object controls.
+ *
+ * @return the layer
+ */
+ public Layer layer() {
+ return layer;
+ }
+
+ private void ensureInLayer(Module source) {
+ if (!layer.modules().contains(source))
+ 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 <em>Read edges</em> added by this method are <em>weak</em>
+ * 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 layer
+ *
+ * @see Module#addReads
+ */
+ public Controller addReads(Module source, Module target) {
+ Objects.requireNonNull(source);
+ Objects.requireNonNull(target);
+ ensureInLayer(source);
+ Modules.addReads(source, 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 to read
+ *
+ * @return This controller
+ *
+ * @throws IllegalArgumentException
+ * If {@code source} is not in the layer or the package is not
+ * in the source module
+ *
+ * @see Module#addOpens
+ */
+ public Controller addOpens(Module source, String pn, Module target) {
+ Objects.requireNonNull(source);
+ Objects.requireNonNull(source);
+ Objects.requireNonNull(target);
+ ensureInLayer(source);
+ Modules.addOpens(source, pn, target);
+ return this;
+ }
+ }
+
/**
* Creates a new 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.
+ * 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:
+ * <pre> {@code
+ * Layer.defineModulesWithOneLoader(cf, List.of(thisLayer), parentLoader).layer();
+ * }</pre>
+ *
+ * @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 parent of the given configuration is not the configuration
+ * for this layer
+ * @throws LayerInstantiationException
+ * If all modules cannot be defined to the same class loader for any
+ * of the reasons listed above or the layer cannot be created because
+ * the configuration contains a module named "{@code java.base}" or
+ * a module with a package name starting with "{@code java.}"
+ * @throws SecurityException
+ * If {@code RuntimePermission("createClassLoader")} or
+ * {@code RuntimePermission("getClassLoader")} is denied by
+ * the security manager
+ *
+ * @see #findLoader
+ */
+ public Layer defineModulesWithOneLoader(Configuration cf,
+ ClassLoader parentLoader) {
+ return defineModulesWithOneLoader(cf, List.of(this), parentLoader).layer();
+ }
+
+
+ /**
+ * Creates a new 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:
+ * <pre> {@code
+ * Layer.defineModulesWithManyLoaders(cf, List.of(thisLayer), parentLoader).layer();
+ * }</pre>
+ *
+ * @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 parent of the given configuration is not the configuration
+ * for this layer
+ * @throws LayerInstantiationException
+ * If the layer cannot be created because the configuration contains
+ * a module named "{@code java.base}" or a module with a package
+ * name starting with "{@code java.}"
+ * @throws SecurityException
+ * If {@code RuntimePermission("createClassLoader")} or
+ * {@code RuntimePermission("getClassLoader")} is denied by
+ * the security manager
+ *
+ * @see #findLoader
+ */
+ public Layer defineModulesWithManyLoaders(Configuration cf,
+ ClassLoader parentLoader) {
+ return defineModulesWithManyLoaders(cf, List.of(this), parentLoader).layer();
+ }
+
+
+ /**
+ * Creates a new 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:
+ * <pre> {@code
+ * Layer.defineModules(cf, List.of(thisLayer), clf).layer();
+ * }</pre>
+ *
+ * @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 parent of the given configuration is not the configuration
+ * for this layer
+ * @throws LayerInstantiationException
+ * If creating the {@code Layer} fails for any of the reasons
+ * listed above, the layer cannot be created because the
+ * configuration contains a module named "{@code java.base}",
+ * a module with a package name starting with "{@code java.}" is
+ * mapped to a class loader other than the {@link
+ * ClassLoader#getPlatformClassLoader() platform class loader},
+ * or the function to map a module name to a class loader returns
+ * {@code null}
+ * @throws SecurityException
+ * If {@code RuntimePermission("getClassLoader")} is denied by
+ * the security manager
+ */
+ public Layer defineModules(Configuration cf,
+ Function<String, ClassLoader> clf) {
+ return defineModules(cf, List.of(this), clf).layer();
+ }
+
+ /**
+ * Creates a new 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.
*
* <p> The class loader created by this method implements <em>direct
* delegation</em> when loading types from modules. When its {@link
@@ -180,7 +407,7 @@
* <ul>
*
* <li><p> <em>Overlapping packages</em>: Two or more modules in the
- * configuration have the same package (exported or concealed). </p></li>
+ * configuration have the same package. </p></li>
*
* <li><p> <em>Split delegation</em>: The resulting class loader would
* need to delegate to more than one class loader in order to load types
@@ -194,19 +421,22 @@
*
* @param cf
* The configuration for the layer
+ * @param parentLayers
+ * The list 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 The newly created layer
+ * @return A controller that controls the newly created layer
*
* @throws IllegalArgumentException
- * If the parent of the given configuration is not the configuration
- * for this layer
+ * If the parent configurations 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 or the layer cannot be created because
- * the configuration contains a module named "{@code java.base}"
+ * the configuration contains a module named "{@code java.base}" or
+ * a module with a package name starting with "{@code java.}"
* @throws SecurityException
* If {@code RuntimePermission("createClassLoader")} or
* {@code RuntimePermission("getClassLoader")} is denied by
@@ -214,29 +444,32 @@
*
* @see #findLoader
*/
- public Layer defineModulesWithOneLoader(Configuration cf,
- ClassLoader parentLoader)
+ public static Controller defineModulesWithOneLoader(Configuration cf,
+ List<Layer> parentLayers,
+ ClassLoader parentLoader)
{
- checkConfiguration(cf);
+ List<Layer> parents = new ArrayList<>(parentLayers);
+ checkConfiguration(cf, parents);
+
checkCreateClassLoaderPermission();
checkGetClassLoaderPermission();
try {
Loader loader = new Loader(cf.modules(), parentLoader);
- loader.initRemotePackageMap(cf, this);
- return new Layer(cf, this, mn -> loader);
+ loader.initRemotePackageMap(cf, parents);
+ Layer layer = new Layer(cf, parents, mn -> loader);
+ return new Controller(layer);
} catch (IllegalArgumentException e) {
throw new LayerInstantiationException(e.getMessage());
}
}
-
/**
- * Creates a new 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.
+ * Creates a new 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.
*
* <p> The class loaders created by this method implement <em>direct
* delegation</em> when loading types from modules. When {@link
@@ -258,18 +491,21 @@
*
* @param cf
* The configuration for the layer
+ * @param parentLayers
+ * The list 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 The newly created layer
+ * @return A controller that controls the newly created layer
*
* @throws IllegalArgumentException
- * If the parent of the given configuration is not the configuration
- * for this layer
+ * If the parent configurations 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}"
+ * a module named "{@code java.base}" or a module with a package
+ * name starting with "{@code java.}"
* @throws SecurityException
* If {@code RuntimePermission("createClassLoader")} or
* {@code RuntimePermission("getClassLoader")} is denied by
@@ -277,37 +513,43 @@
*
* @see #findLoader
*/
- public Layer defineModulesWithManyLoaders(Configuration cf,
- ClassLoader parentLoader)
+ public static Controller defineModulesWithManyLoaders(Configuration cf,
+ List<Layer> parentLayers,
+ ClassLoader parentLoader)
{
- checkConfiguration(cf);
+ List<Layer> parents = new ArrayList<>(parentLayers);
+ checkConfiguration(cf, parents);
+
checkCreateClassLoaderPermission();
checkGetClassLoaderPermission();
- LoaderPool pool = new LoaderPool(cf, this, parentLoader);
+ LoaderPool pool = new LoaderPool(cf, parents, parentLoader);
try {
- return new Layer(cf, this, pool::loaderFor);
+ Layer layer = new Layer(cf, parents, pool::loaderFor);
+ return new Controller(layer);
} catch (IllegalArgumentException e) {
throw new LayerInstantiationException(e.getMessage());
}
}
-
/**
- * Creates a new layer, with this layer as its parent, by defining the
- * modules in the given {@code Configuration} to the Java virtual machine.
+ * Creates a new layer 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. The class loader delegation implemented by these class
- * loaders must respect module readability. In addition, the caller needs
- * to arrange that the class loaders are ready to load from these module
- * before there are any attempts to load classes or resources.
+ * 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 are
+ * ready to load from these module before there are any attempts to load
+ * classes or resources.
*
* <p> Creating a {@code Layer} can fail for the following reasons: </p>
*
* <ul>
*
- * <li><p> Two or more modules with the same package (exported or
- * concealed) are mapped to the same class loader. </p></li>
+ * <li><p> Two or more modules with the same package are mapped to the
+ * same class loader. </p></li>
*
* <li><p> A module is mapped to a class loader that already has a
* module of the same name defined to it. </p></li>
@@ -328,26 +570,35 @@
*
* @param cf
* The configuration for the layer
+ * @param parentLayers
+ * The list parent layers in search order
* @param clf
* The function to map a module name to a class loader
*
- * @return The newly created layer
+ * @return A controller that controls the newly created layer
*
* @throws IllegalArgumentException
- * If the parent of the given configuration is not the configuration
- * for this layer
+ * If the parent configurations do not match the configuration of
+ * the parent layers, including order
* @throws LayerInstantiationException
* If creating the {@code Layer} fails for any of the reasons
- * listed above or the layer cannot be created because the
- * configuration contains a module named "{@code java.base}"
+ * listed above, the layer cannot be created because the
+ * configuration contains a module named "{@code java.base}",
+ * a module with a package name starting with "{@code java.}" is
+ * mapped to a class loader other than the {@link
+ * ClassLoader#getPlatformClassLoader() platform class loader},
+ * or the function to map a module name to a class loader returns
+ * {@code null}
* @throws SecurityException
* If {@code RuntimePermission("getClassLoader")} is denied by
* the security manager
*/
- public Layer defineModules(Configuration cf,
- Function<String, ClassLoader> clf)
+ public static Controller defineModules(Configuration cf,
+ List<Layer> parentLayers,
+ Function<String, ClassLoader> clf)
{
- checkConfiguration(cf);
+ List<Layer> parents = new ArrayList<>(parentLayers);
+ checkConfiguration(cf, parents);
Objects.requireNonNull(clf);
checkGetClassLoaderPermission();
@@ -362,7 +613,8 @@
}
try {
- return new Layer(cf, this, clf);
+ Layer layer = new Layer(cf, parents, clf);
+ return new Controller(layer);
} catch (IllegalArgumentException iae) {
// IAE is thrown by VM when defining the module fails
throw new LayerInstantiationException(iae.getMessage());
@@ -370,13 +622,26 @@
}
- private void checkConfiguration(Configuration cf) {
+ /**
+ * Checks that the parent configurations match the configuration of
+ * the parent layers.
+ */
+ private static void checkConfiguration(Configuration cf,
+ List<Layer> parentLayers)
+ {
Objects.requireNonNull(cf);
- Optional<Configuration> oparent = cf.parent();
- if (!oparent.isPresent() || oparent.get() != this.configuration()) {
- throw new IllegalArgumentException(
- "Parent of configuration != configuration of this Layer");
+ List<Configuration> parentConfigurations = cf.parents();
+ if (parentLayers.size() != parentConfigurations.size())
+ throw new IllegalArgumentException("wrong number of parents");
+
+ int index = 0;
+ for (Layer parent : parentLayers) {
+ if (parent.configuration() != parentConfigurations.get(index)) {
+ throw new IllegalArgumentException(
+ "Parent of configuration != configuration of this Layer");
+ }
+ index++;
}
}
@@ -463,18 +728,57 @@
/**
- * Returns this layer's parent unless this is the {@linkplain #empty empty
- * layer}, which has no parent.
+ * 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 This layer's parent
+ * @return The list of this layer's parents
*/
- public Optional<Layer> parent() {
- return Optional.ofNullable(parent);
+ public List<Layer> parents() {
+ return parents;
}
/**
- * Returns a set of the modules in this layer.
+ * Returns an ordered stream of layers. The first element is 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<Layer> layers() {
+ List<Layer> allLayers = this.allLayers;
+ if (allLayers != null)
+ return allLayers.stream();
+
+ allLayers = new ArrayList<>();
+ Set<Layer> visited = new HashSet<>();
+ Deque<Layer> stack = new ArrayDeque<>();
+ visited.add(this);
+ stack.push(this);
+
+ while (!stack.isEmpty()) {
+ Layer layer = stack.pop();
+ allLayers.add(layer);
+
+ // push in reverse order
+ for (int i = layer.parents.size() - 1; i >= 0; i--) {
+ Layer 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<Layer> allLayers;
+
+ /**
+ * Returns the set of the modules in this layer.
*
* @return A possibly-empty unmodifiable set of the modules in this layer
*/
@@ -486,7 +790,11 @@
/**
* Returns the module with the given name in this layer, or if not in this
- * layer, the {@linkplain #parent parent} layer.
+ * layer, the {@linkplain #parents parents} 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 <em>tree of layers</em> then this is equivalent to
+ * a depth-first search.
*
* @param name
* The name of the module to find
@@ -496,17 +804,25 @@
* parent layer
*/
public Optional<Module> findModule(String name) {
- Module m = nameToModule.get(Objects.requireNonNull(name));
+ Objects.requireNonNull(name);
+ Module m = nameToModule.get(name);
if (m != null)
return Optional.of(m);
- return parent().flatMap(l -> l.findModule(name));
+
+ return layers()
+ .skip(1) // skip this layer
+ .map(l -> l.nameToModule)
+ .filter(map -> map.containsKey(name))
+ .map(map -> map.get(name))
+ .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 #parent}
- * layer is checked.
+ * 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}.
*
* <p> If there is a security manager then its {@code checkPermission}
* method is called with a {@code RuntimePermission("getClassLoader")}
@@ -527,20 +843,32 @@
* @throws SecurityException if denied by the security manager
*/
public ClassLoader findLoader(String name) {
- Module m = nameToModule.get(Objects.requireNonNull(name));
- if (m != null)
- return m.getClassLoader();
- Optional<Layer> ol = parent();
- if (ol.isPresent())
- return ol.get().findLoader(name);
- throw new IllegalArgumentException("Module " + name
- + " not known to this layer");
+ Optional<Module> 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 layer.
+ *
+ * @return A possibly empty string describing this layer
+ */
+ @Override
+ public String toString() {
+ return modules().stream()
+ .map(Module::getName)
+ .collect(Collectors.joining(", "));
+ }
/**
* Returns the <em>empty</em> layer. There are no modules in the empty
- * layer. It has no parent.
+ * layer. It has no parents.
*
* @return The empty layer
*/
@@ -572,39 +900,12 @@
if (servicesCatalog != null)
return servicesCatalog;
- Map<String, Set<ServiceProvider>> map = new HashMap<>();
- for (Module m : nameToModule.values()) {
- ModuleDescriptor descriptor = m.getDescriptor();
- for (Provides provides : descriptor.provides().values()) {
- String service = provides.service();
- Set<ServiceProvider> providers
- = map.computeIfAbsent(service, k -> new HashSet<>());
- for (String pn : provides.providers()) {
- providers.add(new ServiceProvider(m, pn));
- }
- }
- }
-
- ServicesCatalog catalog = new ServicesCatalog() {
- @Override
- public void register(Module module) {
- throw new UnsupportedOperationException();
- }
- @Override
- public Set<ServiceProvider> findServices(String service) {
- Set<ServiceProvider> providers = map.get(service);
- if (providers == null) {
- return Collections.emptySet();
- } else {
- return Collections.unmodifiableSet(providers);
- }
- }
- };
-
synchronized (this) {
servicesCatalog = this.servicesCatalog;
if (servicesCatalog == null) {
- this.servicesCatalog = servicesCatalog = catalog;
+ servicesCatalog = ServicesCatalog.create();
+ nameToModule.values().forEach(servicesCatalog::register);
+ this.servicesCatalog = servicesCatalog;
}
}
@@ -612,4 +913,36 @@
}
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<Layer> list = CLV.get(loader);
+ if (list == null) {
+ list = new CopyOnWriteArrayList<>();
+ List<Layer> 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<Layer> layers(ClassLoader loader) {
+ List<Layer> 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<List<Layer>> CLV = new ClassLoaderValue<>();
}