Java 9 ServiceLoader runtime module loading and replacement
Asked Answered
G

1

6

I just read about Java 9 module system and I'd like to ask about ServiceLoader. Is there any way how to add service implementation when the application is already started? How about removing some service implementation?

Use case: I will have some application that calculates something. The calculation algorithm will be defined in some service(Java 9 module). Are there any steps that can be done to replace this algorithm without stopping the application? When will I replace the jars is it just that the calculation will fail and I will need to catch the error and restart when the module is done loading?

Is there any other project that can support such use case?

Garrott answered 4/4, 2018 at 7:2 Comment(5)
Just to make it clear, are you saying that you want to make use of different providers of the algorithm(let's say) service?Lubric
This can be done with dynamic configurations and module layers. It's an advanced topic so probably best to get familiar with the module concepts before going there.Ie
@nullpointer This is example, but technicaly yes and on top of that I want to replace them without down time.Garrott
@AlanBateman So it is feasible with Java 9 modules. Without any details, do you know how the interruption of ongoing calculation will work out?Garrott
It's only going to work if there are points where the application can move to the new service. In module terms, it's a new service provider module in a new layer.Ie
C
0

Lets Assume BankController is your service . If you want to load its implementation dynamically from dynamic path,create a new module layer and load implementation.

private final BankController loadController(final BankConfig config) {
    System.out.println("Loading bank with config : " + JSON.toJson(config));
    try {
        //Curent ModuleLayer is usually boot layer. but it can be different if you are using multiple layers
        ModuleLayer currentModuleLayer       = this.getClass().getModule().getLayer(); //ModuleLayer.boot();
        final Set<Path> modulePathSet        = Set.of(new File("path of implementation").toPath());
        //ModuleFinder to find modules 
        final ModuleFinder moduleFinder      = ModuleFinder.of(modulePathSet.toArray(new Path[0]));
        //I really dont know why does it requires empty finder.
        final ModuleFinder emptyFinder       = ModuleFinder.of(new Path[0]);
        //ModuleNames to be loaded
        final Set<String>  moduleNames       = moduleFinder.findAll().stream().map(moduleRef -> moduleRef.descriptor().name()).collect(Collectors.toSet());
        // Unless you want to use URLClassloader for tomcat like situation, use Current Class Loader 
        final ClassLoader loader             = this.getClass().getClassLoader();
        //Derive new configuration from current module layer configuration
        final Configuration  configuration   = currentModuleLayer.configuration().resolveAndBind(moduleFinder, emptyFinder, moduleNames);
        //New Module layer derived from current modulee layer 
        final ModuleLayer    moduleLayer     = currentModuleLayer.defineModulesWithOneLoader(configuration, loader);
        //create new instance of Implementation, in this case org.util.npci.coreconnect.CoreController implements org.util.npci.api.BankController
        final BankController bankController  = ServiceLoader.load(moduleLayer, BankController.class);
        return bankController;
    } catch (Exception e) {BootLogger.info(e);}
    return null;
}
Conidiophore answered 24/8, 2019 at 15:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.