javajava-modulemanifest.mf

How to specify java Manifest Add-Exports for named modules


I have a named module that uses some --add-exports at compile time. How can I imply this directive without specifying it manually in the run command line ?

Compilation of named module

javac --add-exports java.base/sun.security.x509=mymodule --module-source-path src -d bin --module mymodule

Packaging as a JAR

jar -c --file=mymodule.jar --main-class=mymodule.Main -C bin/mymodule

Run

java --module-path . --module mymodule/mymodule.Main

causes :

cannot access class sun.security.x509.AlgorithmId (in module java.base) because module java.base does not export sun.security.x509 to module mymodule

cannot access class sun.security.x509.AlgorithmId (in module java.base) because module java.base does not export sun.security.x509 to unnamed module @0x53bd815b

http://openjdk.java.net/jeps/261 states :

A module/package pair in the value of an Add-Exports attribute has the same meaning as the command-line option --add-exports module/package=ALL-UNNAMED

How can I specify that the export is not to ALL-UNNAMED but mymodule instead ? How to actually make it work ?

If mymodule is not the main class but a library used deeper, then I must specify all exports for all possible modules that I may sometimes use directly from the command line ?


Solution

  • It seems that at this stage (JRE 17), this is not possible.

    The exports are performed only in 2 cases :

    1. when starting with the command line parameter --add-exports (obviously)

    2. when starting the program with the -jar option, it checks the MANIFEST.MF for the Add-Exports directive. This is the only case so far where the manifest is checked.

    This means that starting with java -cp my.jar my.Main will not check the manifest of the jar file.

    This means that starting with java --module my.module will not check the manifest either.

    My opinion

    This is rather sad because it spoils the java module system. If one of your dependent module needs those exports, you need to set them at the command line parameter and you need to know it in advance. This is usually the case with those sun.* classes that no one is supposed to use but that everyone uses anyways. @Oracle : So either expose it publicly (and thus no need to add-exports) or remove it completely from the JRE if we are unable to use it anyways...

    Workaround

    Load the dependent modules dynamically at runtime yourself using a URLClassLoader and do the exports yourself (ugly).

    String exports = jarFile.getManifest().getMainAttributes().getValue("Add-Exports");
    if( exports != null )
    {
        for (String moduleAndPackage : exports.split(" "))
        {
            String[] s = moduleAndPackage.trim().split("/");
            if (s.length != 2) continue;
            jdk.internal.module.Modules.addExports(ModuleLayer.boot().findModule(s[0]).orElseThrow(), s[1]);
        }
    }
    

    And guess what, in order to do this, you need to use the restricted java.base/jdk.internal.module package... which you can now export once for all using the manifest option if your program can be launched with -jar.

    This can thus prevent to add all the exports (that you may not be aware of) manually in the command line.