Missing module discovery functionality in Jigsaw/JPMS API?

classic Classic list List threaded Threaded
2 messages Options
Reply | Threaded
Open this post in threaded view
|

Missing module discovery functionality in Jigsaw/JPMS API?

Luke Hutchison
I am the author of Fast Classpath Scanner
<https://github.com/lukehutch/fast-classpath-scanner/>. I am trying to
extend this library to work with JDK 9, and I'm running into a lot of
apparent shortcomings of the JPMS API.

These are the main issues I have come across so far:

(1) Automatic modules from the legacy classpath cannot be found using
ModuleFinder.ofSystem() (because they are not system modules), or using
ModuleLayer.boot().modules() (because this list does not include unnamed
modules).

(2) I would guess then that unnamed modules are in their own layer, but the
Layer API only allows you to look at parent layers, not child layers -- so
it is not even possible to enumerate all layers in the system. Why is there
no call for enumerating all layers, and/or for reading child layers?

(3) ModuleLayer.boot().configuration().findModule(name) cannot find unnamed
automatic modules either, because name cannot be null (and using the empty
string doesn't return unnamed layers).

(4) From within a class in a traditional (non-module) jar on the classpath,
using getClass().getModule(), does return a Module reference for the
unnamed module. However, there is no API that I can find for getting a
ModuleReference from a Module object, if the module is unnamed. For
example, the following should work if the module is named, but does not
work if the module is unnamed:

    Optional<ModuleReference> moduleReference =
        module
            .getLayer()
            .configuration()
            .findModule(module.getName())
            .map(ResolvedModule::reference);

(5) ModuleFinder.of(path) would find the module, if the path were known.
But there is no way to get the path from the Module reference obtained
using getClass().getModule() -- you need a ModuleReference object to get
the URI of the module, and you cannot get a ModuleReference, as described
in (4) above.

(6) Having to specify a path when calling ModuleFinder.of(), as shown in
(5), defeats the purpose of having a classpath and/or module path that is
specified outside of program code. I imagine it is primarily useful for
dynamic loading of modules, but what I'm interested in is the static
classpath / module path.

(7) Module has a method Set<String> getPackages(), which allows you to
enumerate package names within a module, but it does not have a method for
listing resource names within each package. For that, again you need a
ModuleReference, and then you can call ModuleReference#open() to get a
ModuleReader, then ModuleReader#list() to list the resources in the module.
Why is there no open() method in Module, and/or why is it not possible to
get a ModuleReference directly from a Module?

It seems like there are lots of obvious omissions in the API, from an
enumeration / scanning point of view. Or am I missing something?

Thanks,
Luke Hutchison
Reply | Threaded
Open this post in threaded view
|

Re: Missing module discovery functionality in Jigsaw/JPMS API?

Alan Bateman
On 24/12/2017 23:31, Luke Hutchison wrote:
> I am the author of Fast Classpath Scanner
> <https://github.com/lukehutch/fast-classpath-scanner/>. I am trying to
> extend this library to work with JDK 9, and I'm running into a lot of
> apparent shortcomings of the JPMS API.
If you are scanning the class path today then you should find it works
the same in JDK 9. The main thing that you will need to add support for
is Multi-Release JARs (details in JEP 238, the JAR file spec, and the
JarFile API).

Most of your questions are about enumerating the resources in unnamed
modules (meaning the class path) rather than resources in named modules.
If you are scanning the class path today then you are already scanning
the resources in the unnamed module of the application class loader. The
java.lang.module APIs that are attempting to lean on are used for module
resolution that creating configurations of named modules (which are then
instantiated in the Java virtual machine as module layers). This should
become clear once you spend a bit more time getting familiar with the
concepts.

For scanning then it might be useful to work through an example of
something looking to scan a set of observable modules and selecting the
root modules to resolve. This is where you might use the ModuleFinder
API, maybe combining it with your existing class path scanner.

Post-resolution examples may also be interesting. Here you might find it
useful to work through examples that need to locate resources in named
modules that are already loaded and instantiated.

>
> These are the main issues I have come across so far:
>
> (1) Automatic modules from the legacy classpath cannot be found using
> ModuleFinder.ofSystem() (because they are not system modules), or using
> ModuleLayer.boot().modules() (because this list does not include unnamed
> modules).
Automatic modules are named modules. They are deployed on the module
path, not the class path.

>
> (2) I would guess then that unnamed modules are in their own layer, but the
> Layer API only allows you to look at parent layers, not child layers -- so
> it is not even possible to enumerate all layers in the system. Why is there
> no call for enumerating all layers, and/or for reading child layers?
Unnamed modules are not in a module layer.

Module layers beyond the boot layer is an advanced topic. Once you are
further along then it may make sense to extend your API to support
scanning using a ModuleLayer as context.

>
> (3) ModuleLayer.boot().configuration().findModule(name) cannot find unnamed
> automatic modules either, because name cannot be null (and using the empty
> string doesn't return unnamed layers).
As (1), automatic modules are named modules. As (2), a module layer
contains only named modules.


>
> (4) From within a class in a traditional (non-module) jar on the classpath,
> using getClass().getModule(), does return a Module reference for the
> unnamed module. However, there is no API that I can find for getting a
> ModuleReference from a Module object, if the module is unnamed. For
> example, the following should work if the module is named, but does not
> work if the module is unnamed:
>
>      Optional<ModuleReference> moduleReference =
>          module
>              .getLayer()
>              .configuration()
>              .findModule(module.getName())
>              .map(ResolvedModule::reference);
This is expected, as unnamed modules are not in a layer or configuration.

Note that there is no guarantee that a named module is in a module layer
(Module::getLayer may return null). This is a corner case for where you
are now of course.

> (5) ModuleFinder.of(path) would find the module, if the path were known.
> But there is no way to get the path from the Module reference obtained
> using getClass().getModule() -- you need a ModuleReference object to get
> the URI of the module, and you cannot get a ModuleReference, as described
> in (4) above.
The main thing to understand is that Module/ModuleLayer is the
loaded/instantiated modules. The types you see in the java.lang.module
package (including ModuleReference) is the model world. When a Module is
in a module layer then you use the ModuleLayer::configuration method to
go back to the Configuration.


>
> (6) Having to specify a path when calling ModuleFinder.of(), as shown in
> (5), defeats the purpose of having a classpath and/or module path that is
> specified outside of program code. I imagine it is primarily useful for
> dynamic loading of modules, but what I'm interested in is the static
> classpath / module path.
For the static / out-of-the-box case, the value of the java.class.path
property is the class path (I suspect you are already using that).

The value of the jdk.module.path property (documented in
System.getProperties) is the application module path.

In JEP 261 you'll find all the details in the module path section on the
search order, which will be important if your extended APIs is used to
scan observable modules.


> It seems like there are lots of obvious omissions in the API, from an
> enumeration / scanning point of view. Or am I missing something?
>
I think you've found some of the APIs, it's mostly just getting the
concepts right to use them effectively.

-Alan.