3

I want to load and reload a service provider in a jar which is a module. It is my understanding that this can be achieved by loading the module in a new ModuleLayer.

Following the advice in this answer and the example in the docs, I have succeeded in loading the module at runtime and using the service provider.

var finder = ModuleFinder.of(Path.of("/path/to/module/"));

var bootLayer = ModuleLayer.boot();
var pluginLayer = bootLayer.defineModulesWithOneLoader(
        bootLayer.configuration().resolve(finder, ModuleFinder.of(), Set.of("myapp")),
        ClassLoader.getSystemClassLoader());

ServiceLoader.load(MyService.class, pluginLayer.findLoader("myapp"))
    .findFirst()
    .ifPresentOrElse(
        __ -> System.out.println("Found provider."),
        () -> System.out.println("Couldn't find provider."));

The only thing I am unsure about is how I should go about eventually unloading the module and reloading a new version of it. The linked answer says that the unloading happens when the ModuleLayer is GC'd. With some testing, I have confirmed this, though this feels very finicky. Obviously, I cannot control exactly when the layer will be garbage collected, even once it is unreachable.

What would be the best approach for allowing quick reloading of the module?

Panda
  • 877
  • 9
  • 21
  • 1
    As far as I know, the only option is to remove all references to the `ModuleLayer`. You should let the garbage collector decide when to actually collect the layer and just create your new layer as needed. Are you encountering any specific problems with that approach? – Slaw May 25 '19 at 08:26
  • The handle to the jar is not released until the layer is gc'd. This is problematic as I would like to be able to replace that jar. – Panda May 25 '19 at 08:34
  • @Slaw Do you have any ideas regarding releasing that file handle in a timely manner? – Panda May 26 '19 at 07:19
  • I don't, unfortunately. The module API doesn't provide a way to "close" anything, except `ModuleReader`. If an open `ModuleReader` is the problem you likely can't do much about it as you won't have access to the instance. And just like `ClassLoader`s, you can't actively dispose of `ModuleLayer`s. You also can't force a GC run. You could be _notified_ when the layer is GC'd by registering it with a `Cleaner` (but I feel that's an unintended use of the class). – Slaw May 26 '19 at 07:33
  • I'm quite disappointed by that. I thought perhaps we finally had safe, standard APIs for this kind of thing. Back to manually using ClassLoaders I suppose. – Panda May 26 '19 at 07:37
  • I had hopes for that as well when the JPMS was released, but I remember reading somewhere that hot (re)deployment was not a design goal. Maybe that will change in the future. For your case, I suppose you could put the replacement JAR somewhere else (e.g. a sibling directory), but I'm personally not a fan of that approach. – Slaw May 26 '19 at 08:04

0 Answers0