Inconsistency with service loading by layer or by class loader

Alan Bateman alan.bateman at oracle.com
Thu Dec 12 13:27:02 UTC 2024


On 11/12/2024 19:31, David Lloyd wrote:
> I'm once again experimenting with modularizing some of our runtime 
> projects (Quarkus, WildFly) using JDK modules. Among the various 
> problems I've encountered, this one is one I haven't cracked yet and 
> seems like an oversight.
>
> I am currently experimenting with a one-module-per-layer design which 
> allows us certain capabilities (like lazy loading of modules, late 
> binding of dependencies, and circularity in dependencies), and also 
> allows us to create module graphs that can mix in "unnamed" modules 
> for libraries which don't yet work properly as named modules, as well 
> as automatic modules for libraries which can work as modules but don't 
> yet define a descriptor.
>
> However I have discovered an important difference between service 
> loaders which load services by class loader (for example, those found 
> in Microprofile and Jakarta frameworks) and those which load by module 
> layer (none?) which is preventing things from working properly.
>
> When I load a service by layer, after searching the given layer, the 
> parent layers of that layer are then searched in order, recursing up 
> the graph. (Sometimes the same service could be returned multiple 
> times when there are diamonds in the layer graph, but that's a 
> different problem.)
>
> When I load a service by class loader, if the service is not found in 
> the class loader's defined layers, then the search terminates and the 
> service is considered not found, even if the service provider exists 
> in a parent layer of one of those layers. This essentially means that 
> all frameworks which are loading services by class loader (and using 
> the class loader of the framework itself is typical here) will also 
> need all of their implementations to coexist in one of the layers 
> defined by that class loader - or else they must be defined in the 
> unnamed module (in which case it will always be found).
>
> Ironically, it *will* search the parent class loading delegation chain 
> for layers which contain service implementations. The point of using 
> module layers though is that they can have multiple parents, departing 
> from this single-delegation model. In our class-loader-per-module 
> setup, we do not use the parent class loader delegation as it would be 
> meaningless in that context.
>
> A good solution would be to modify service loading by class loader to 
> also search parent layers of each layer defined to that class loader. 
> This seems like it would be fairly easy to implement, but might 
> possibly have subtle compatibility implications for very specific 
> environments which have a module-per-class-loader situation along with 
> multiple layers.
>
If ServiceLoader.load is invoked with a ClassLoader then it lazily 
iterates through the class loader delegation chain. If 
ServiceLoader.load is invoked with a ModuleLayer then it iterates 
through the module layers.  The former has deliberately limited support 
for cases where a class loader is used by a module layer but doing what 
you propose is adding more complexity for what seems like a really niche 
usage. I would worry that changing it along the lines you propose would 
result in something that only a few people could understand.

Have you looked at changing these frameworks to work with modules and 
specify a module layer to ServiceLoader.load?

-Alan
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/jigsaw-dev/attachments/20241212/2a92263b/attachment-0001.htm>


More information about the jigsaw-dev mailing list