From simone.bordet at gmail.com Fri Mar 14 10:54:54 2025 From: simone.bordet at gmail.com (Simone Bordet) Date: Fri, 14 Mar 2025 11:54:54 +0100 Subject: Adding modules from a child layer Message-ID: Hi, I'm working on improving JPMS support in Jetty. Jetty 12 has a set of core modules that do not depend on Jakarta EE, and then has "environments" such as ee9, ee10, etc. so it is possible to deploy on the same server both an EE9 web application and an EE10 web application: Jetty isolates the jakarta.servlet-api-5.0.0.jar (for EE9) from the jakarta.servlet-api-6.0.0.jar (for EE10). Previously, this was done using ClassLoaders only. Now I am trying to use ModuleLayers, with the following setup. - server ModuleLayer + jetty-util-12.1.0.jar (plus a bunch of other Jetty core jars) - ee10 ModuleLayer (child of the server ModuleLayer) + jakarta.servlet-api-6.0.0.jar + jetty-ee10-webapp-12.1.0.jar (plus a bunch of other Jetty ee10 jars) The server ModuleLayer is created by the JVM at startup, via command line (i.e. java -p jetty-util-12.1.0.jar:... -m ...). The ee10 ModuleLayer is created at runtime by the Jetty code (reading command line arguments). Now, it happens that jetty-ee10-webapp.jar requires java.instrument (declared in the jetty-ee10-webapp's module-info). Running in this setup yields: Exception in thread "main" java.lang.module.FindException: Module java.instrument not found at java.base/java.lang.module.Resolver.findFail(Resolver.java:893) at java.base/java.lang.module.Resolver.resolve(Resolver.java:129) at java.base/java.lang.module.Configuration.resolve(Configuration.java:421) at java.base/java.lang.module.Configuration.resolve(Configuration.java:255) My interpretation of this error is that the ee10 ModuleLayer tries to resolve the jetty-ee10-webapp dependencies; obviously the java.instrument module is not in the ee10 ModuleLayer, so it delegates to the server ModuleLayer. The server ModuleLayer has modules, but none depend on java.instrument, and there is no explicit --add-modules java.instrument directive (as it is not necessary for the server ModuleLayer), so the java.instrument module is not in the module graph, and hence the error. If I manually add the --add-modules java.instrument directive to the server ModuleLayer, the error is gone. The question is whether there is a way for a child ModuleLayer to perform the equivalent of "--add-modules", but programmatically at runtime, that would add the module to the graph? As a side question, I was wondering what is the benefit of using ModuleLayers versus just using ClassLoaders. I can think of earlier error detection: if the ModuleLayer is not setup correctly (as above) I get an early error, which I would only get much later at runtime if I was using ClassLoaders. Isolation is provided by the ClassLoaders anyway. Anything else I could not think of? Thanks! -- Simone Bordet --- Finally, no matter how good the architecture and design are, to deliver bug-free software with optimal performance and reliability, the implementation technique must be flawless. Victoria Livschitz From coderanger2025 at outlook.com Fri Mar 14 11:03:10 2025 From: coderanger2025 at outlook.com (Code Ranger) Date: Fri, 14 Mar 2025 13:03:10 +0200 Subject: Adding modules from a child layer In-Reply-To: References: Message-ID: There is a? framework for working with layers, see https://github.com/techsenger/alpha It has several demos and every demo shows how to work with Jetty 12 and Web application when they are in different layers. Maybe it can be interesting for you. On 3/14/25 12:54, Simone Bordet wrote: > Hi, > > I'm working on improving JPMS support in Jetty. > > Jetty 12 has a set of core modules that do not depend on Jakarta EE, > and then has "environments" such as ee9, ee10, etc. so it is possible > to deploy on the same server both an EE9 web application and an EE10 > web application: Jetty isolates the jakarta.servlet-api-5.0.0.jar (for > EE9) from the jakarta.servlet-api-6.0.0.jar (for EE10). > > Previously, this was done using ClassLoaders only. > > Now I am trying to use ModuleLayers, with the following setup. > > - server ModuleLayer > + jetty-util-12.1.0.jar (plus a bunch of other Jetty core jars) > > - ee10 ModuleLayer (child of the server ModuleLayer) > + jakarta.servlet-api-6.0.0.jar > + jetty-ee10-webapp-12.1.0.jar (plus a bunch of other Jetty ee10 jars) > > The server ModuleLayer is created by the JVM at startup, via command > line (i.e. java -p jetty-util-12.1.0.jar:... -m ...). > The ee10 ModuleLayer is created at runtime by the Jetty code (reading > command line arguments). > > Now, it happens that jetty-ee10-webapp.jar requires java.instrument > (declared in the jetty-ee10-webapp's module-info). > > Running in this setup yields: > > Exception in thread "main" java.lang.module.FindException: Module > java.instrument not found > at java.base/java.lang.module.Resolver.findFail(Resolver.java:893) > at java.base/java.lang.module.Resolver.resolve(Resolver.java:129) > at java.base/java.lang.module.Configuration.resolve(Configuration.java:421) > at java.base/java.lang.module.Configuration.resolve(Configuration.java:255) > > My interpretation of this error is that the ee10 ModuleLayer tries to > resolve the jetty-ee10-webapp dependencies; obviously the > java.instrument module is not in the ee10 ModuleLayer, so it delegates > to the server ModuleLayer. > The server ModuleLayer has modules, but none depend on > java.instrument, and there is no explicit --add-modules > java.instrument directive (as it is not necessary for the server > ModuleLayer), so the java.instrument module is not in the module > graph, and hence the error. > > If I manually add the --add-modules java.instrument directive to the > server ModuleLayer, the error is gone. > > The question is whether there is a way for a child ModuleLayer to > perform the equivalent of "--add-modules", but programmatically at > runtime, that would add the module to the graph? > > As a side question, I was wondering what is the benefit of using > ModuleLayers versus just using ClassLoaders. > I can think of earlier error detection: if the ModuleLayer is not > setup correctly (as above) I get an early error, which I would only > get much later at runtime if I was using ClassLoaders. > Isolation is provided by the ClassLoaders anyway. > Anything else I could not think of? > > Thanks! From alan.bateman at oracle.com Fri Mar 14 11:23:01 2025 From: alan.bateman at oracle.com (Alan Bateman) Date: Fri, 14 Mar 2025 11:23:01 +0000 Subject: Adding modules from a child layer In-Reply-To: References: Message-ID: On 14/03/2025 10:54, Simone Bordet wrote: > : > > The question is whether there is a way for a child ModuleLayer to > perform the equivalent of "--add-modules", but programmatically at > runtime, that would add the module to the graph? Module layers are immutable so you can't modify the boot layer at runtime. In this case, the initial module is a named module that doesn't transitively require java.instrument so that module is not in the boot layer. If they server was started with -javaagent then the java.instrument would be in the boot layer.? In JEP 261 the recommendation for container apps, like this one, is to run with --add-module ALL-DEFAULT and it will ensure that any module in the run-time that exports an API will be in the boot layer. A comment in passing is that it's surprising that there are modules in ee$N layers requiring java.instrument. The classes in this modules aren't normally used by libraries and applications, instead they work as the side channel for agents. > As a side question, I was wondering what is the benefit of using > ModuleLayers versus just using ClassLoaders. > I can think of earlier error detection: if the ModuleLayer is not > setup correctly (as above) I get an early error, which I would only > get much later at runtime if I was using ClassLoaders. Right, that's part of reliable configuration where errors are detected in pre-flight checks rather than failing mid-flight. The other part is strong encapsulation. ClassLoader can provide isolation but they are just part of the setup as class loaders otherwise just load classes. -Alan -------------- next part -------------- An HTML attachment was scrubbed... URL: From simone.bordet at gmail.com Fri Mar 14 12:10:02 2025 From: simone.bordet at gmail.com (Simone Bordet) Date: Fri, 14 Mar 2025 13:10:02 +0100 Subject: Adding modules from a child layer In-Reply-To: References: Message-ID: Hi Alan, On Fri, Mar 14, 2025 at 12:23?PM Alan Bateman wrote: > In this case, the initial module is a named module that doesn't transitively require java.instrument so that module is not in the boot layer. If they server was started with -javaagent then the java.instrument would be in the boot layer. In JEP 261 the recommendation for container apps, like this one, is to run with --add-module ALL-DEFAULT and it will ensure that any module in the run-time that exports an API will be in the boot layer. Thanks, trying ALL-DEFAULT, in conjunction with ALL-MODULE-PATH. > A comment in passing is that it's surprising that there are modules in ee$N layers requiring java.instrument. The classes in this modules aren't normally used by libraries and applications, instead they work as the side channel for agents. With some archeology, I found: https://bugs.eclipse.org/bugs/show_bug.cgi?id=347110 :) TLDR: Spring had (has?) a mechanism to instrument loaded class, I assume for dependency injection. The Spring agent requires custom class loaders to have an `addTransformer(ClassFileTransformer)` method, hence the dependency of Jetty's WebAppClassLoader to java.instrument. Thanks! -- Simone Bordet --- Finally, no matter how good the architecture and design are, to deliver bug-free software with optimal performance and reliability, the implementation technique must be flawless. Victoria Livschitz From alex.buckley at oracle.com Fri Mar 14 15:13:03 2025 From: alex.buckley at oracle.com (Alex Buckley) Date: Fri, 14 Mar 2025 08:13:03 -0700 Subject: Adding modules from a child layer In-Reply-To: References: Message-ID: On 3/14/2025 3:54 AM, Simone Bordet wrote: > As a side question, I was wondering what is the benefit of using > ModuleLayers versus just using ClassLoaders. As Alan mentioned, layers are how you get strong encapsulation, where the accessibility of classes in modules is enforced by the VM. This is not the same thing as isolating classes in different loaders, where reflection can be used to access any class. Please watch 15 minutes of my JVMLS 2016 presentation, starting at: https://www.youtube.com/watch?v=QnMDsI2GbOc&t=1479s "Creating a layer informs the module system about the packages that will exist in the program, and the modules they'll come from. The module system passes this information to the VM, so that when classes are eventually loaded and defined, the VM knows which module a class belongs to, and what that module reads and exports. It's like the VM has a shadow copy of the module graph that it uses when checking accessibility between classes." Alex From alan.bateman at oracle.com Sun Mar 16 08:37:04 2025 From: alan.bateman at oracle.com (Alan Bateman) Date: Sun, 16 Mar 2025 08:37:04 +0000 Subject: Adding modules from a child layer In-Reply-To: References: Message-ID: On 14/03/2025 12:10, Simone Bordet wrote: > : > With some archeology, I found: > https://urldefense.com/v3/__https://bugs.eclipse.org/bugs/show_bug.cgi?id=347110__;!!ACWV5N9M2RV99hQ!IW-lZcO8m0GajZDPc4BK-QlJTTyR0a8NZNZT4IFVuTZWTd4j0sJZ0MThsyXFb3W1ez6iScbbkrxHxFmx3lbHGnyQ$ :) > > TLDR: Spring had (has?) a mechanism to instrument loaded class, I > assume for dependency injection. > The Spring agent requires custom class loaders to have an > `addTransformer(ClassFileTransformer)` method, hence the dependency of > Jetty's WebAppClassLoader to java.instrument. > Okay, so I think this more about ClassFileTransformer being a generally useful interface and WebAppClassLoader uses it for users that wants to instrument at ClassLoader load class time. So Java agents aren't in the picture, it's just one of the interfaces from j.l.instrument is being used and so this module needs to be the boot layer. -Alan -------------- next part -------------- An HTML attachment was scrubbed... URL: