Problem with SPI components (e.g., AnalysisSPILoader) when refreshing from different classloader and closing previous classloader
- Dominant language
- Java
- Stars
- 3.6k
- Forks
- 1.4k
- Avg merge
- 2d 11h
- Merged PRs (30d)
- 88
Description
### Description
Applications like Solr allow users to reference token filters, tokenizers, etc. by their SPI names - and Lucene provides utility methods to discover and load these services.
The discovery relies on `AnalysisSPILoader#reload(ClassLoader)` method which scans the provided classloader's classpath, and loads and caches available services.
However, if this method is called again (with a different classloader), it makes no attempt to invalidate the cache of available services.
**This means that if:**
- the cache already contains a mapping `"myservice" -> org.example.MyServiceFactory`, where MyServiceFactory class was defined by classloader1,
- the `reload` method is called again with classloader2, so that
- `"myservice"` is discovered again
- `MyServiceFactory` class is defined again (but by classloader2)
**then:**
- the `AnalysisSPILoader` cache will retain the `MyServiceFactory` class defined by classloader1 (and not classloader2).
This behavior places restrictions on how the applications can reload the SPI - and it does not work well with how Solr manages the lifecycle of its classloaders.
### Specific example
Basically, whenever Solr adds jars to classpath at runtime, it replaces its current classloader with a new instance of URLClassLoader, closes the old classloader, and reloads the SPI.
So this may lead to a situation where the AnalysisSPILoader cache keeps a reference to `MyServiceFactory` class that was defined by classloader1 (which is now closed because Solr closed it and switched to classloader2).
As a result, when the application code retrieves `MyServiceFactory` by its SPI name, and the factory executes code which requires loading some other class from classloader1's classpath - the loading fails because Solr already invoked `((URLClassLoader) classloader1).close()`.
And from users's perspective, this issue manifests in rather cryptic `ClassNotFoundException`.
### Version and environment details
Lucene 9.8.0
Contributor guide
Assessment
This issue has not been assessed yet.