Discussion:
override module with an alternative implementation in the child layer
Milen Dyankov
2018-07-30 17:16:17 UTC
Permalink
In response to a question posted on this list [1] some time ago, Alan
Bateman stated "You can override *any* module (except java.base) with an
alternative implementation in the child layer". Is that true for the
standard JDK modules? If so, is there any documentation / code samples that
explains how?

The obvious naive approach to load standard module in a layer:

ModuleFinder finder = ModuleFinder.ofSystem();

ModuleLayer bootLayer = ModuleLayer.boot();

Configuration cf = bootLayer.configuration().resolve(finder,
ModuleFinder.of(), Set.of("java.sql"));

ClassLoader scl = ClassLoader.getSystemClassLoader();

ModuleLayer layer1 = bootLayer.defineModulesWithOneLoader(cf, scl);


results in

Exception in thread "main" java.lang.LayerInstantiationException: Class
loader (instance of): jdk/internal/loader/Loader tried to define prohibited
package name: java.util.logging

Regards,
Milen

[1]
http://jigsaw-dev.1059479.n5.nabble.com/Whitelisting-modules-in-layers-td5717573.html#a5717574
Alan Bateman
2018-07-30 18:17:23 UTC
Permalink
Post by Milen Dyankov
In response to a question posted on this list [1] some time ago, Alan
Bateman stated "You can override *any* module (except java.base) with an
alternative implementation in the child layer". Is that true for the
standard JDK modules? If so, is there any documentation / code samples that
explains how?
The previous thread didn't get into this but the other restriction is
that you can't contain modules that export java.* APIs in custom layers
(javax.* APIs are not an issue). I don't have time to give a detailed
reply just now but look for the following in the javadoc of the
ModuleLayer defineModulesXXX methods:

"In addition, a layer cannot be created if the configuration contains a
module named "java.base", a configuration contains a module with a
package named "java" or a package name starting with "java.", or the
function to map a module name to a class loader returns null or the
platform class loader"

-Alan.
Milen Dyankov
2018-07-30 18:41:31 UTC
Permalink
Thank you Alan,

that is what I originally thought. It's just your comment in the other
thread made me think there may be a possibility I was not aware of.

So am I right to assume that any application server like software is
expected to always run on top of all standard JDK modules that export
"java.*" packages? I mean, say there is an application server that runs on
top of "minimal" JPMS created with jlink. If the base layer of that
application server does not have "java.sql" for example, there is
essentially no way for it to load it dynamically, when a JAR or WAR that
needs it is deployed, correct?
Post by Alan Bateman
Post by Milen Dyankov
In response to a question posted on this list [1] some time ago, Alan
Bateman stated "You can override *any* module (except java.base) with an
alternative implementation in the child layer". Is that true for the
standard JDK modules? If so, is there any documentation / code samples
that
Post by Milen Dyankov
explains how?
The previous thread didn't get into this but the other restriction is
that you can't contain modules that export java.* APIs in custom layers
(javax.* APIs are not an issue). I don't have time to give a detailed
reply just now but look for the following in the javadoc of the
"In addition, a layer cannot be created if the configuration contains a
module named "java.base", a configuration contains a module with a
package named "java" or a package name starting with "java.", or the
function to map a module name to a class loader returns null or the
platform class loader"
-Alan.
--
http://about.me/milen
Alan Bateman
2018-07-31 12:20:02 UTC
Permalink
Post by Milen Dyankov
So am I right to assume that any application server like software is
expected to always run on top of all standard JDK modules that export
"java.*" packages? I mean, say there is an application server that runs on
top of "minimal" JPMS created with jlink. If the base layer of that
application server does not have "java.sql" for example, there is
essentially no way for it to load it dynamically, when a JAR or WAR that
needs it is deployed, correct?
One thing to say is the restriction on the java.* class name space is
rooted in security and has been in the Java SE API specification for
many releases. The VM enforcement around this goes back to JDK 1.2 at
least. The specification was relaxed in Java SE 9 to allow java.*
classes be defined to the platform class loader but going further, to
allow java.* classes be defined by other class loaders, would require
significant effort.

Aside from java.base, there are 7 modules in Java SE with java.* APIs:

java.datatransfer
java.desktop
java.instrument
java.logging
java.management
java.net.http
java.prefs
java.rmi
java.sql

The long standing specified restriction on the java.* class name space
and the matching restrictions in the ModuleLayer API, mean that you
cannot load (or override) these 7 modules in a child layer created by
defineModulesXXX APIs.

In your case, the main application (container / app server) doesn't
transitively require java.sql so this module is not in the boot layer.
It can't dynamically load it (because it contains a java.* classes) so
it has ensure that the above 7 modules are in the boot layer in order to
support dynamically loading of application modules that might require
one or more of these modules. In JEP 261 we document --add-modules
ALL-DEFAULT for this purpose:

"As a special case at run time, if <module> is ALL-DEFAULT then the
default set of root modules for the unnamed module, as defined above, is
added to the root set. This is useful when the application is a
container that hosts other applications which can, in turn, depend upon
modules not required by the container itself."

Could you use that?

-Alan
Milen Dyankov
2018-07-31 13:33:51 UTC
Permalink
Thank you for the clarification Alan,

it seams ALL-DEFAULT does not work with jlink but that is OK as I can
easily find those modules while running my assembly tool and pass the list
to jlink.
The only difference between what I find and what you posted (I assume 7 was
a typo as your list contains 9 modules) is that there seems to be no `
java.net.http` module (at least not among the 99 modules shipped in 10.0.1
that I'm experimenting with)

Thanks for your help. It's much appreciated.
Post by Alan Bateman
Post by Milen Dyankov
So am I right to assume that any application server like software is
expected to always run on top of all standard JDK modules that export
"java.*" packages? I mean, say there is an application server that runs
on
Post by Milen Dyankov
top of "minimal" JPMS created with jlink. If the base layer of that
application server does not have "java.sql" for example, there is
essentially no way for it to load it dynamically, when a JAR or WAR that
needs it is deployed, correct?
One thing to say is the restriction on the java.* class name space is
rooted in security and has been in the Java SE API specification for
many releases. The VM enforcement around this goes back to JDK 1.2 at
least. The specification was relaxed in Java SE 9 to allow java.*
classes be defined to the platform class loader but going further, to
allow java.* classes be defined by other class loaders, would require
significant effort.
java.datatransfer
java.desktop
java.instrument
java.logging
java.management
java.net.http
java.prefs
java.rmi
java.sql
The long standing specified restriction on the java.* class name space
and the matching restrictions in the ModuleLayer API, mean that you
cannot load (or override) these 7 modules in a child layer created by
defineModulesXXX APIs.
In your case, the main application (container / app server) doesn't
transitively require java.sql so this module is not in the boot layer.
It can't dynamically load it (because it contains a java.* classes) so
it has ensure that the above 7 modules are in the boot layer in order to
support dynamically loading of application modules that might require
one or more of these modules. In JEP 261 we document --add-modules
"As a special case at run time, if <module> is ALL-DEFAULT then the
default set of root modules for the unnamed module, as defined above, is
added to the root set. This is useful when the application is a
container that hosts other applications which can, in turn, depend upon
modules not required by the container itself."
Could you use that?
-Alan
--
http://about.me/milen
Alan Bateman
2018-08-01 12:28:21 UTC
Permalink
Post by Milen Dyankov
Thank you for the clarification Alan,
it seams ALL-DEFAULT does not work with jlink but that is OK as I can
easily find those modules while running my assembly tool and pass the list
to jlink.
Right, the ALL-DEFAULT token doesn't mean anything at link time because
there is only one module path. Compare that to compile-time or run-time
where the module path is composed from an optional upgrade module path,
the system modules, and the application module path.
Post by Milen Dyankov
The only difference between what I find and what you posted (I assume 7 was
a typo as your list contains 9 modules) is that there seems to be no `
java.net.http` module (at least not among the 99 modules shipped in 10.0.1
that I'm experimenting with)
Sorry, I did mean 9, not 7. The java.net.http module is new for Java SE 11.

-Alan

Loading...