Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Consider adding a classpath argument to the ServiceLoader.load call in ClassMappingType.kt #407

Open
bitspittle opened this issue Mar 21, 2023 · 0 comments
Labels

Comments

@bitspittle
Copy link

bitspittle commented Mar 21, 2023

Specifically, I think you can change this:

ServiceLoader.load(ClassMappingTypeService::class.java)

to this:

ServiceLoader.load(ClassMappingTypeService::class.java, ClassMappingTypeService::class.java.classLoader)
//                                                      ^ or KMongo::class.java.classLoader, or whatever

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 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!)

Finally -- if you make this change, I'm pretty sure that would fix stuff like #98


Workaround

Here's how I worked around this in my own code:

val prevContextClassLoader = Thread.currentThread().contextClassLoader
try {
    Thread.currentThread().contextClassLoader = KMongo::class.java.classLoader
    val client = KMongo.createClient().coroutine
    /* ... other KMongo init stuff ... */
} finally {
    Thread.currentThread().contextClassLoader = prevContextClassLoader
}

which I can remove if this issue ever gets fixed.

@zigzago zigzago added the bug label Apr 3, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants