How to let a java class on classpath access a class belonging to a module on module-path

1.1k views Asked by At

I am trying out the various access rules about who can access and what and I saw this statement in The State of the module system document,

The unnamed module reads every other module. Code in any type loaded from the class path will thus be able to access the exported types of all other readable modules, which by default will include all of the named, built-in platform modules.

So, I wrote the following code to test it out with the following structure:

moduleA/modA.A --> automod/automod.Foo --> nonmodular.Junk --> moduleX/modX.X

Basically,

  1. moduleA's modA.A calls a method on a non-modular class automod.Foo. automod.Foo is packaged into automod.jar and put on the module-path. module-info for moduleA has requires automod; clause. This works fine, as expected.

  2. automod.Foo calls a method on nonmodular.Junk class. nonmodular.Junk is packaged into nonmodular.jar and put on classpath. This works fine, as expected.

  3. nonmodular.Junk calls a method on moduleX's modX.X. modX.X is packaged into moduleX.jar. It is this step that has a problem. It works if I put moduleX.jar on classpath but not if I put moduleX.jar on module-path. (module-info for moduleX does have exports modX; clause.)

In other words, the following command works:

java --module-path moduleA.jar;automod.jar; -classpath nonmodular.jar;moduleX.jar --module moduleA/modA.A

With the following output:

In modA.A.main() Calling automod.Foo()
In automod.Foo()
In modA.A.main() Calling automod.foo.main()
In automod.Foo.main() Calling nonmodular.Junk()
In automod.Foo.main() Calling nonmodular.Junk.main()
In nonmodular.Junk.main calling new modX.X()
In modX.X()

But the following command doesn't work:

java --module-path moduleA.jar;automod.jar;moduleX.jar -classpath nonmodular.jar; --module moduleA/modA.A

Here is the output:

In modA.A.main() Calling automod.Foo()
In automod.Foo()
In modA.A.main() Calling automod.foo.main()
In automod.Foo.main() Calling nonmodular.Junk()
In automod.Foo.main() Calling nonmodular.Junk.main()
In nonmodular.Junk.main calling new modX.X()
Exception in thread "main" java.lang.NoClassDefFoundError: modX/X
        at nonmodular.Junk.main(Junk.java:5)
        at automod/automod.Foo.main(Foo.java:10)
        at moduleA/modA.A.main(A.java:10)
Caused by: java.lang.ClassNotFoundException: modX.X
        at        java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:583)
    at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
    at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
    ... 3 more

Any idea why? Any class loaded from the classpath should be able to access any classes exported by a module.

1

There are 1 answers

0
Slaw On

When you start a Java application with the --module command, the value you pass is a "root" module. The same is true of modules added via --add-modules. The module system determines the entire module graph from these root modules. In other words, it reads the module-info file, finds the requires directives, and then searches the modulepath for those required modules. It does this transitively. Some modules also declare one or more uses directives on a service. Any modules on the modulepath that provides any of those services will also be loaded, regardless of if any module requires them.

This means if there's a module on the modulepath that isn't required by any loaded module and doesn't provide any services needed by any loaded module then said module won't be loaded. If you're interested in seeing what modules are resolved you can use the following command:

java --show-module-resolution --dry-run -p [MODULEPATH] -m [MODULE]

In your case I can only assume that none of your other modules require modularX, so when its on the modulepath it doesn't get loaded. However, when its on the classpath things work differently and its found by your non-modular code that's also on the classpath. You can still use the modulepath though, just make sure your moduleX module is loaded. This can be forced by using --add-modules:

java -p moduleA.jar;automod.jar;moduleX.jar --add-modules moduleX -cp nonmodular.jar -m moduleA/modA.A

Note you can also limit the modules via --limit-modules.