jdk/src/java.base/share/classes/java/lang/module/Configuration.java
changeset 36511 9d0388c6b336
child 37779 7c84df693837
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/java/lang/module/Configuration.java	Thu Mar 17 19:04:16 2016 +0000
@@ -0,0 +1,431 @@
+/*
+ * 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
+ * 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.module;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * The configuration that is the result of resolution or resolution with
+ * service binding.
+ *
+ * <h2><a name="resolution">Resolution</a></h2>
+ *
+ * <p> Resolution is the process of computing the transitive closure of a set
+ * of root modules over a set of observable modules by resolving the
+ * dependences expressed by {@code requires} clauses.
+ *
+ * The <em>dependence graph</em> is augmented with edges that take account of
+ * implicitly declared dependences ({@code requires public}) to create a
+ * <em>readability graph</em>. A {@code Configuration} encapsulates the
+ * resulting graph of {@link ResolvedModule resolved modules}.
+ *
+ * <p> Suppose we have the following observable modules: </p>
+ * <pre> {@code
+ *     module m1 { requires m2; }
+ *     module m2 { requires public m3; }
+ *     module m3 { }
+ *     module m4 { }
+ * } </pre>
+ *
+ * <p> If the module {@code m1} is resolved then the resulting configuration
+ * contains three modules ({@code m1}, {@code m2}, {@code m3}). The edges in
+ * its readability graph are: </p>
+ * <pre> {@code
+ *     m1 --> m2  (meaning m1 reads m2)
+ *     m1 --> m3
+ *     m2 --> m3
+ * } </pre>
+ *
+ * <p> Resolution is an additive process. When computing the transitive closure
+ * then the dependence relation may include dependences on modules in parent
+ * configurations. The result is a <em>relative configuration</em> that is
+ * relative to a parent configuration and where the readability graph may have
+ * edges from modules in the configuration to modules in a parent configuration.
+ * </p>
+ *
+ * <p> Suppose we have the following observable modules: </p>
+ * <pre> {@code
+ *     module m1 { requires m2; requires java.xml; }
+ *     module m2 { }
+ * } </pre>
+ *
+ * <p> If module {@code m1} is resolved with the configuration for the {@link
+ * java.lang.reflect.Layer#boot() boot} layer as the parent then the resulting
+ * configuration contains two modules ({@code m1}, {@code m2}). The edges in
+ * its readability graph are:
+ * <pre> {@code
+ *     m1 --> m2
+ *     m1 --> java.xml
+ * } </pre>
+ * where module {@code java.xml} is in the parent configuration. For
+ * simplicity, this example omits the implicitly declared dependence on the
+ * {@code java.base} module.
+ *
+ * <a name="automaticmoduleresolution"></a>
+ * <p> {@link ModuleDescriptor#isAutomatic() Automatic} modules receive special
+ * treatment during resolution. Each automatic module is resolved so that it
+ * reads all other modules in the configuration and all parent configurations.
+ * Each automatic module is also resolved as if it {@code requires public} all
+ * other automatic modules in the configuration (and all automatic modules in
+ * parent configurations). </p>
+
+ * <h2><a name="servicebinding">Service binding</a></h2>
+ *
+ * <p> Service binding is the process of augmenting a graph of resolved modules
+ * from the set of observable modules induced by the service-use dependence
+ * ({@code uses} and {@code provides} clauses). Any module that was not
+ * previously in the graph requires resolution to compute its transitive
+ * closure. Service binding is an iterative process in that adding a module
+ * that satisfies some service-use dependence may introduce new service-use
+ * dependences. </p>
+ *
+ * <p> Suppose we have the following observable modules: </p>
+ * <pre> {@code
+ *     module m1 { exports p; uses p.S; }
+ *     module m2 { requires m1; provides p.S with p2.S2; }
+ *     module m3 { requires m1; requires m4; provides p.S with p3.S3; }
+ *     module m4 { }
+ * } </pre>
+ *
+ * <p> If the module {@code m1} is resolved then the resulting graph of modules
+ * has one module ({@code m1}). If the graph is augmented with modules induced
+ * by the service-use dependence relation then the configuration will contain
+ * four modules ({@code m1}, {@code m2}, {@code m3}, {@code m4}). The edges in
+ * its readability graph are: </p>
+ * <pre> {@code
+ *     m2 --> m1
+ *     m3 --> m1
+ *     m3 --> m4
+ * } </pre>
+ * <p> The edges in the conceptual service-use graph are: </p>
+ * <pre> {@code
+ *     m1 --> m2  (meaning m1 uses a service that is provided by m2)
+ *     m1 --> m3
+ * } </pre>
+ *
+ * <p> If this configuration is instantiated as a {@code Layer}, and if code in
+ * module {@code m1} uses {@link java.util.ServiceLoader ServiceLoader} to
+ * iterate over implementations of {@code p.S.class}, then it will iterate over
+ * an instance of {@code p2.S2} and {@code p3.S3}. </p>
+ *
+ * <h3> Example </h3>
+ *
+ * <p> The following example uses the {@code resolveRequires} method to resolve
+ * a module named <em>myapp</em> with the configuration for the boot layer as
+ * the parent configuration. It prints the name of each resolved module and
+ * the names of the modules that each module reads. </p>
+ *
+ * <pre>{@code
+ *    ModuleFinder finder = ModuleFinder.of(dir1, dir2, dir3);
+ *
+ *    Configuration parent = Layer.boot().configuration();
+ *
+ *    Configuration cf = parent.resolveRequires(finder,
+ *                                              ModuleFinder.empty(),
+ *                                              Set.of("myapp"));
+ *    cf.modules().forEach(m -> {
+ *        System.out.format("%s -> %s%n",
+ *            m.name(),
+ *            m.reads().stream()
+ *                .map(ResolvedModule::name)
+ *                .collect(Collectors.joining(", ")));
+ *    });
+ * }</pre>
+ *
+ * @since 9
+ * @see java.lang.reflect.Layer
+ */
+public final class Configuration {
+
+    // @see Configuration#empty()
+    private static final Configuration EMPTY_CONFIGURATION = new Configuration();
+
+    private final Configuration parent;
+
+    private final Map<ResolvedModule, Set<ResolvedModule>> graph;
+    private final Set<ResolvedModule> modules;
+    private final Map<String, ResolvedModule> nameToModule;
+
+    private Configuration() {
+        this.parent = null;
+        this.graph = Collections.emptyMap();
+        this.modules = Collections.emptySet();
+        this.nameToModule = Collections.emptyMap();
+    }
+
+    private Configuration(Configuration parent, Resolver resolver) {
+        Map<ResolvedModule, Set<ResolvedModule>> graph = resolver.finish(this);
+
+        Map<String, ResolvedModule> nameToModule = new HashMap<>();
+        for (ResolvedModule resolvedModule : graph.keySet()) {
+            nameToModule.put(resolvedModule.name(), resolvedModule);
+        }
+
+        this.parent = parent;
+        this.graph = graph;
+        this.modules = Collections.unmodifiableSet(graph.keySet());
+        this.nameToModule = Collections.unmodifiableMap(nameToModule);
+    }
+
+
+    /**
+     * Resolves a collection of root modules, with this configuration as its
+     * parent, to create a new configuration.
+     *
+     * <p> Each root module is located using the given {@code before} module
+     * finder. If a module is not found then it is located in the parent
+     * configuration as if by invoking the {@link #findModule(String)
+     * findModule} method. If not found then the module is located using the
+     * given {@code after} module finder. The same search order is used to
+     * locate transitive dependences. Root modules or dependences that are
+     * located in a parent configuration are resolved no further and are not
+     * included in the resulting configuration. </p>
+     *
+     * <p> When all modules have been resolved then the resulting dependency
+     * graph is checked to ensure that it does not contain cycles. A
+     * readability graph is constructed and then, in conjunction with the
+     * module exports and service use, checked for consistency. </p>
+     *
+     * <p> Resolution and the (post-resolution) consistency checks may fail for
+     * following reasons: </p>
+     *
+     * <ul>
+     *     <li><p> A root module, or a direct or transitive dependency, is not
+     *     found. </p></li>
+     *
+     *     <li><p> An error occurs when attempting to find a module.
+     *     Possible errors include I/O errors, errors detected parsing a module
+     *     descriptor ({@code module-info.class}) or two versions of the same
+     *     module are found in the same directory. </p></li>
+     *
+     *     <li><p> A cycle is detected, say where module {@code m1} requires
+     *     module {@code m2} and {@code m2} requires {@code m1}. </p></li>
+     *
+     *     <li><p> Two or more modules in the configuration export the same
+     *     package to a module that reads both. This includes the case where a
+     *     module {@code M} containing package {@code p} reads another module
+     *     that exports {@code p} to {@code M}. </p></li>
+     *
+     *     <li><p> A module {@code M} declares that it "{@code uses p.S}" or
+     *     "{@code provides p.S with ...}" but package {@code p} is neither in
+     *     module {@code M} nor exported to {@code M} by any module that
+     *     {@code M} reads. </p></li>
+     *
+     *     <li><p> A module {@code M} declares that it
+     *     "{@code provides ... with q.T}" but package {@code q} is not in
+     *     module {@code M}. </p></li>
+     *
+     *     <li><p> Two or more modules in the configuration are specific to
+     *     different {@link ModuleDescriptor#osName() operating systems},
+     *     {@link ModuleDescriptor#osArch() architectures}, or {@link
+     *     ModuleDescriptor#osVersion() versions}. </p></li>
+     *
+     *     <li><p> Other implementation specific checks, for example referential
+     *     integrity checks to ensure that different versions of tighly coupled
+     *     modules cannot be combined in the same configuration. </p></li>
+     *
+     * </ul>
+     *
+     * @param  before
+     *         The <em>before</em> module finder to find modules
+     * @param  after
+     *         The <em>after</em> module finder to locate modules when not
+     *         located by the {@code before} module finder or in parent
+     *         configurations
+     * @param  roots
+     *         The possibly-empty collection of module names of the modules
+     *         to resolve
+     *
+     * @return The configuration that is the result of resolving the given
+     *         root modules
+     *
+     * @throws ResolutionException
+     *         If resolution or the post-resolution checks fail for any of the
+     *         reasons listed
+     * @throws SecurityException
+     *         If locating a module is denied by the security manager
+     */
+    public Configuration resolveRequires(ModuleFinder before,
+                                         ModuleFinder after,
+                                         Collection<String> roots)
+    {
+        Objects.requireNonNull(before);
+        Objects.requireNonNull(after);
+        Objects.requireNonNull(roots);
+
+        Resolver resolver = new Resolver(before, this, after);
+        resolver.resolveRequires(roots);
+
+        return new Configuration(this, resolver);
+    }
+
+
+    /**
+     * Resolves a collection of root modules, with service binding, and with
+     * this configuration as its parent, to create a new configuration.
+     *
+     * <p> This method works exactly as specified by {@link #resolveRequires
+     * resolveRequires} except that the graph of resolved modules is augmented
+     * with modules induced by the service-use dependence relation. </p>
+     *
+     * <p> More specifically, the root modules are resolved as if by calling
+     * {@code resolveRequires}. The resolved modules, and all modules in the
+     * parent configurations, with {@link ModuleDescriptor#uses() service
+     * dependences} are then examined. All modules found by the given module
+     * finders that {@link ModuleDescriptor#provides() provide} an
+     * implementation of one or more of the service types are added to the
+     * module graph and then resolved as if by calling the {@code
+     * resolveRequires} method. Adding modules to the module graph may
+     * introduce new service-use dependences and so the process works
+     * iteratively until no more modules are added. </p>
+     *
+     * <p> As service binding involves resolution then it may fail with {@link
+     * ResolutionException} for exactly the same reasons specified in
+     * {@code resolveRequires}.  </p>
+     *
+     * @param  before
+     *         The <em>before</em> module finder to find modules
+     * @param  after
+     *         The <em>after</em> module finder to locate modules when not
+     *         located by the {@code before} module finder or in parent
+     *         configurations
+     * @param  roots
+     *         The possibly-empty collection of module names of the modules
+     *         to resolve
+     *
+     * @return The configuration that is the result of resolving the given
+     *         root modules
+     *
+     * @throws ResolutionException
+     *         If resolution or the post-resolution checks fail for any of the
+     *         reasons listed
+     * @throws SecurityException
+     *         If locating a module is denied by the security manager
+     */
+    public Configuration resolveRequiresAndUses(ModuleFinder before,
+                                                ModuleFinder after,
+                                                Collection<String> roots)
+    {
+        Objects.requireNonNull(before);
+        Objects.requireNonNull(after);
+        Objects.requireNonNull(roots);
+
+        Resolver resolver = new Resolver(before, this, after);
+        resolver.resolveRequires(roots).resolveUses();
+
+        return new Configuration(this, resolver);
+    }
+
+
+    /**
+     * Returns the <em>empty</em> configuration. The empty configuration does
+     * not contain any modules and does not have a parent.
+     *
+     * @return The empty configuration
+     */
+    public static Configuration empty() {
+        return EMPTY_CONFIGURATION;
+    }
+
+
+    /**
+     * Returns this configuration's parent unless this is the {@linkplain #empty
+     * empty configuration}, which has no parent.
+     *
+     * @return This configuration's parent
+     */
+    public Optional<Configuration> parent() {
+        return Optional.ofNullable(parent);
+    }
+
+
+    /**
+     * Returns an immutable set of the resolved modules in this configuration.
+     *
+     * @return A possibly-empty unmodifiable set of the resolved modules
+     *         in this configuration
+     */
+    public Set<ResolvedModule> modules() {
+        return modules;
+    }
+
+
+    /**
+     * Finds a resolved module in this configuration, or if not in this
+     * configuration, the {@linkplain #parent parent} configurations.
+     *
+     * @param  name
+     *         The module name of the resolved module to find
+     *
+     * @return The resolved module with the given name or an empty {@code
+     *         Optional} if there isn't a module with this name in this
+     *         configuration or any parent configuration
+     */
+    public Optional<ResolvedModule> findModule(String name) {
+        Objects.requireNonNull(name);
+        if (parent == null)
+            return Optional.empty();
+        ResolvedModule m = nameToModule.get(name);
+        if (m != null)
+            return Optional.of(m);
+        return parent().flatMap(x -> x.findModule(name));
+    }
+
+
+    Set<ModuleDescriptor> descriptors() {
+        if (modules.isEmpty()) {
+            return Collections.emptySet();
+        } else {
+            return modules.stream()
+                    .map(ResolvedModule::reference)
+                    .map(ModuleReference::descriptor)
+                    .collect(Collectors.toSet());
+        }
+    }
+
+    Set<ResolvedModule> reads(ResolvedModule m) {
+        return Collections.unmodifiableSet(graph.get(m));
+    }
+
+    /**
+     * Returns a string describing this configuration.
+     *
+     * @return A string describing this configuration
+     */
+    @Override
+    public String toString() {
+        return modules().stream()
+                .map(ResolvedModule::name)
+                .collect(Collectors.joining(", "));
+    }
+}