Anonymous
Anonymous

Reputation: 501

How to expand the module path at runtime

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

Answers (2)

Anonymous
Anonymous

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

Holger
Holger

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

Related Questions