12

I'm attempting to create a custom implementation of java.nio.FileSystem for an existing Application. The implementation itself seems to work correctly within its context. However, the parent Application doesn't load my implementation via regular ClassPath but via an additional URLClassLoader. Therefore, the implementation is currently only availabel via ServiceLoader.load(FileSystemProvider.class, specificClassLoaderOfMyCode) but it will not be used via APIs like java.nio.Files.

I can modify the parent application but cannot remove the intermediate ClassLoader. I do have access to the parent ServiceLoader and the child's ServiceLoader. My goal is to get all implementations from the child and put them into the parent's ServiceLoader. The Application itself is written in Spring, and the parent's ClassLoader is, therefore, the default Spring ClassLoader.

The only way I can see is to use reflection in order to modify the internal cache and force the implementation into it, but java.base doesn't open the relevant class. Therefore, I'm not allowed to access the private fields of ServiceLoader. I could add the relevant flags, but I'd like to avoid this approach.

Is there any alternative to dynamically add an implementation to a ServiceLoader, that is not known to the ClassLoader (not present in META-INF/services)?

1 Answer 1

0

From ServiceLoader JavaDoc:

The provider must be accessible from the same class loader that was initially queried to locate the configuration file; note that this is not necessarily the class loader from which the file was actually loaded.

By default, ServiceLoader uses System Classloader for this. But, if you initially loaded the META-INF/services/** resource file with your custom class loader (as author is doing in his example), then just issuing reload() should recreate an internal provides iterator and you should be good to go. Be careful however and make sure that ClassLoader.getResources() method will not returned the cached services file, but an updated one.

3
  • I think there's a misunderstanding. I'm using two class loaders, the default/root and a custom one (based on URLClassLoader). The custom ClassLoader delegates some commands back to the default ClassLoader. The META-INF/services/** files I'm interested in are located in the resources of the custom ClassLoader; I want them accessible from the ServiceLoader in default ClassLoader. I also can't use ServiceLoader using the custom ClassLoader, as I'm trying to extend standard lib.
    – Euklios
    Commented Mar 11 at 9:59
  • However, if I understand you correctly, Using a second custom ClassLoader as root would allow this. It would merge all available META-INF/services/** files from the URLClassLoader and call reload() when new URLClassLoaders appear.
    – Euklios
    Commented Mar 11 at 10:02
  • @Euklios it's very simple - reload() will, among other things, ask Classloader that you have passed to ServiceLaoder#load() (which is custom in your case) to reload the services files resources via ClassLoader#getResources method. If custom classloader will have no problem of loading a new, non-cached services files (and again - I have not seen source code of custom ClassLoader) - then we are good to go. I am just explaining to you what would happen, so you would understand yourself why it may or may not work in your case :) Hope it is clear :)
    – mipo256
    Commented Mar 11 at 10:08

Not the answer you're looking for? Browse other questions tagged or ask your own question.