ServiceLoader.load(ClassMappingTypeService::class.java, ClassMappingTypeService::class.java.classLoader)
// ^ or KMongo::class.java.classLoader, or whatever
I'm working on a project that provides a webserver that users can register simple API endpoints against. They do this by building an uber jar which my server loads on demand at runtime. One of my users is using KMongo.
So what happens is, my server finds their jar and loads it with a custom classloader. That class loader is now the owner of the classes they bundled, which include KMongo, ClassMappingTypeService, etc.
My custom classloader can also find resources, one of which is the META-INF/services resource for ClassMappingTypeService.
If you provide ClassMappingTypeService::class.java.classLoader as a second argument (which in this case happens to be my class loader), then I can find that resource and return it to whatever Java machinery is looking for it.
Without passing in a specific classloader into ServiceLoader.load, the method ends up using a classloader associated with the parent thread. As classloaders don't search children classloaders, that means my uber jar has the service definition resource that the JVM wants but can never find.
I am pretty sure that specifying this second argument will help people in the uber jar situation, without affecting existing users. Since, in their case, the class loader for the parent thread ends up being the same as the classloader for KMongo. That's basically exactly the same case that you have now. (Of course, please don't blindly trust me, and double check!)
Specifically, I think you can change this:
to this:
For comparsion, look at what Jetbrains Exposed does with their service loaders: https://github.com/search?q=repo%3AJetBrains%2FExposed%20ServiceLoader&type=code
Background context
I'm working on a project that provides a webserver that users can register simple API endpoints against. They do this by building an uber jar which my server loads on demand at runtime. One of my users is using KMongo.
So what happens is, my server finds their jar and loads it with a custom classloader. That class loader is now the owner of the classes they bundled, which include
KMongo
,ClassMappingTypeService
, etc.My custom classloader can also find resources, one of which is the
META-INF/services
resource forClassMappingTypeService
.If you provide
ClassMappingTypeService::class.java.classLoader
as a second argument (which in this case happens to be my class loader), then I can find that resource and return it to whatever Java machinery is looking for it.Without passing in a specific classloader into
ServiceLoader.load
, the method ends up using a classloader associated with the parent thread. As classloaders don't search children classloaders, that means my uber jar has the service definition resource that the JVM wants but can never find.I am pretty sure that specifying this second argument will help people in the uber jar situation, without affecting existing users. Since, in their case, the class loader for the parent thread ends up being the same as the classloader for
KMongo
. That's basically exactly the same case that you have now. (Of course, please don't blindly trust me, and double check!)Finally -- if you make this change, I'm pretty sure that would fix stuff like https://github.com/Litote/kmongo/issues/98
Workaround
Here's how I worked around this in my own code:
which I can remove if this issue ever gets fixed.