I have three modules: module-a, module-b, module-c. Module-a and module-b are in boot layer. Layer for module-c I create myself.
Module-a has one interface com.mod-a.Service
and in its module-info I have:
module module-a {
exports com.mod-a;
}
Module-c implements com.mod-a.Service
and in its module-info I have:
module module-c {
requires module-a;
provides com.mod-a.Service with com.mod-c.ServiceImpl;
}
Module-b creates new layer with module-c, and calls module-c service. In its module-info I have:
module module-b {
requires module-a;
requires java.management;
requires slf4j.api;
uses com.mod-a.Service;
}
In module-b I create new layer with module-c this way:
ModuleFinder finder = ModuleFinder.of(moduleCPath);
ModuleLayer parent = ModuleLayer.boot();
Configuration cf = parent.configuration().resolve(finder, ModuleFinder.of(), Set.of("module-c"));
ClassLoader scl = ClassLoader.getSystemClassLoader();
ModuleLayer layer = parent.defineModulesWithOneLoader(cf, scl);
//the following line prints "module-c"
layer.modules().stream().map(Module::getName).forEach(System.out::println);
However, after creating layer I can't in module-b call Service of module-c. The following code:
Iterable<Service> it = ServiceLoader.load(Service.class);
System.out.println("LINE 1");
for (Service service : it) {
System.out.println("Service was called");
service.doIt();
}
System.out.println("LINE 2");
outputs:
LINE 1
LINE 2
What is my mistake?
ServiceLoader.load(Class) uses the TCCL as the starting point to locate service providers for the service whereas your example should use the child layer or alternative the class loader of any class loader defining modules in the layer. So if you change the example to ServiceLoader.load(layer, Service.class)
then it should work as you expect.
Separately, you've used resolve
and specified the service provider module as the root module to resolve. Nothing wrong with that but an alternative would have been to use resolveAndBind
and not specify any root modules. The uses com.mod-a.Service
in module-b will ensure that modules that provides com.mod-a.Service
will be resolved.
The root cause of your issue is that the
ServiceLoader.load(Service.class)
which is an alternate of
ServiceLoader.load(Service.class, Thread.currentThread().getContextClassLoader())
doesn't end up finding any service provider for the Service
.
One way in which I was able to fix that was to open the package of the service provider to the module which owns the service, as :
module module-c {
requires module-a;
provides com.mod-a.Service with com.mod-c.ServiceImpl;
opens com.mod-c to module-a;
}
Also, would suggest going through the ServiceLoader
on how to deploy service providers as module and on the classpath.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With