gnodet-bot commented on code in PR #13494:
URL: https://github.com/apache/maven/pull/13494#discussion_r4496083225


##########
impl/maven-core/src/main/java/org/apache/maven/classrealm/DefaultClassRealmManager.java:
##########
@@ -364,4 +488,115 @@ private static Object getId(ClassLoader classLoader) {
         }
         return classLoader;
     }
+
+    private static final String MODULE_ACCESS_DESCRIPTOR = 
"META-INF/maven/module-access";
+
+    /**
+     * Reads {@code META-INF/maven/module-access} descriptors from the plugin 
realm's classpath and
+     * applies the access directives they contain.
+     *
+     * <h4>Purpose</h4>
+     * <p>
+     * When a plugin (or extension) runs on the <em>classpath</em> (unnamed 
module) it may need to
+     * call into packages of a <em>named</em> module that lives in Maven's 
boot/runtime layer —
+     * for example JLine modules in {@code lib/modules/}, or any future Maven 
API module that ships
+     * as a named module.  Named modules do not export their internals to the 
unnamed module by
+     * default; the JVM enforces this at runtime via strong encapsulation.
+     * </p>
+     * <p>
+     * The {@code META-INF/maven/module-access} file is a plugin-side escape 
hatch: it lets the
+     * plugin declare which packages of which boot-layer modules it needs 
opened or exported
+     * <strong>to itself</strong> (always {@code ALL-UNNAMED} as the target).  
Maven applies these
+     * directives programmatically via {@link Module#addExports}/{@link 
Module#addOpens} on startup
+     * of the plugin realm, before any plugin class is loaded.
+     * </p>
+     *
+     * <h4>Direction of access</h4>
+     * <p>
+     * Directives go from an <em>existing</em> named module in the 
boot/runtime layer
+     * <strong>to</strong> the unnamed-module classloader of the plugin.  In 
other words:
+     * a plugin cannot use this mechanism to export its <em>own</em> packages; 
it can only
+     * widen access from modules that Maven already owns (and that are loaded 
before the plugin).
+     * </p>
+     *
+     * <h4>File format</h4>
+     * <p>Each non-blank, non-comment line is one of:</p>
+     * <pre>
+     *   add-exports &lt;module&gt;/&lt;package&gt;=ALL-UNNAMED
+     *   add-opens   &lt;module&gt;/&lt;package&gt;=ALL-UNNAMED
+     *   add-reads   &lt;module&gt;
+     * </pre>
+     * <p>
+     * These mirror the JVM flags {@code --add-exports}, {@code --add-opens}, 
and
+     * {@code --add-reads} respectively.  Lines starting with {@code #} are 
comments.
+     * </p>
+     *
+     * <h4>Why not {@code module-info.java}?</h4>
+     * <p>
+     * The boot-layer modules (e.g. JLine) are compiled independently and 
cannot reference
+     * the plugin's module name at compile time.  Adding {@code exports … to 
plugin.module} in
+     * their {@code module-info} would create an unacceptable coupling.  
Programmatic
+     * {@code addExports}/{@code addOpens} is the standard JDK-endorsed 
workaround for
+     * this exact scenario (see {@link java.lang.Module#addExports}).
+     * </p>
+     *
+     * @param classRealm the plugin class realm whose JAR(s) are scanned for 
descriptors
+     */
+    private void applyModuleAccessDescriptors(ClassRealm classRealm) {
+        try {
+            Enumeration<URL> resources = 
classRealm.getResources(MODULE_ACCESS_DESCRIPTOR);
+            while (resources.hasMoreElements()) {
+                URL resource = resources.nextElement();
+                try (BufferedReader reader =
+                        new BufferedReader(new 
InputStreamReader(resource.openStream(), StandardCharsets.UTF_8))) {
+                    String line;
+                    while ((line = reader.readLine()) != null) {
+                        line = line.trim();
+                        if (line.isEmpty() || line.startsWith("#")) {
+                            continue;
+                        }
+                        applyModuleAccessDirective(classRealm, line);
+                    }
+                }
+            }
+        } catch (IOException e) {
+            logger.warn("Failed to read module-access descriptors for realm 
{}", classRealm.getId(), e);
+        }
+    }
+
+    private void 
applyModuleAccessDirective(org.codehaus.plexus.classworlds.realm.ClassRealm 
realm, String line) {
+        if (line.startsWith("add-exports ")) {
+            applyExportOrOpen(realm, line.substring("add-exports 
".length()).trim(), false);
+        } else if (line.startsWith("add-opens ")) {
+            applyExportOrOpen(realm, line.substring("add-opens 
".length()).trim(), true);
+        } else if (line.startsWith("add-reads ")) {
+            String module = line.substring("add-reads ".length()).trim();
+            realm.addReads(module);
+        } else {
+            logger.debug("Unknown module-access directive: {}", line);

Review Comment:
   💡 **Nit:** Unknown module-access directives (typos, unsupported keywords) 
are logged at `DEBUG`, making them invisible in a normal Maven build. A user 
who misspells `add-exports` as `addexports` gets no feedback — the directive is 
silently dropped.
   
   Both cases (`unknown directive` on line 576 and `invalid directive` on line 
583) have the same issue. `DEBUG` is appropriate only for expected/routine 
conditions; a directive that Maven cannot parse is a configuration authoring 
error that the user needs to see.
   
   ```suggestion
               logger.warn("Unknown module-access directive (skipped): {}", 
line);
   ```



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to