Reputation: 501
I like services. I also like the module system. Unfortunately for me, before I used Java 9 I got in the habit of getting service providers from jars loaded at runtime via URLClassLoader
, something like this (I'll use Java 10's var
for brevity):
var url = new File("myjar.jar").toURI().toURL();
var cl = new URLClassLoader(new URL[] {url}, getClass().getClassLoader());
var services = ServiceLoader.load(MyService.class, cl);
for (var service : services) {
...
}
This works fine, even in Java 9 and beyond, but it loads the jar on the classpath, which means that it uses the old META-INF\services
method to find service providers. I would rather use the module-info
method, but that requires the jar to be loaded on the module path instead, but I couldn't find any way I might go about doing that. So here I am, hoping someone here who understands the module system more thoroughly will tell me how this can be done (or that it can't, if such is the case).
Upvotes: 6
Views: 1325
Reputation: 501
Turns out I was using the wrong command line options (I didn't realize I wasn't supposed to use java -jar
with modular jars). Using the right commands, @Holger's answer worked, except that the set passed to Configuration.resolve
needed to contain all the names of the modules to be loaded, which was easy enough to fix:
var path = Path.of("myjar.jar");
var cl = new URLClassLoader(new URL[]{path.toUri().toURL()});
var mf = ModuleFinder.of(path);
var cfg = Configuration.resolve(mf, List.of(ModuleLayer.boot().configuration()), mf, mf.findAll().stream().map(module -> module.descriptor().name()).collect(Collectors.toSet()));
var ml = ModuleLayer.defineModulesWithOneLoader(cfg, List.of(ModuleLayer.boot()), cl).layer();
var services = ServiceLoader.load(ml, MyService.class);
services.forEach(System.out::println);
Upvotes: 0
Reputation: 298409
The smallest working example, I could assemble, is
var path = Path.of("myjar.jar");
var cl = new URLClassLoader(new URL[]{path.toUri().toURL()});
var mf = ModuleFinder.of(path);
var cfg = Configuration.resolve(mf, List.of(ModuleLayer.boot().configuration()), mf, Set.of());
var ml = ModuleLayer.defineModulesWithOneLoader(cfg, List.of(ModuleLayer.boot()), cl).layer();
var services = ServiceLoader.load(ml, MyService.class);
services.forEach(System.out::println);
Assuming myjar.jar
is a modular jar declaring to provide MyService
implementations.
Upvotes: 5