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