-
Notifications
You must be signed in to change notification settings - Fork 24.9k
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
Add generic interface for loading service providers from plugins #88082
Conversation
Pinging @elastic/es-core-infra (Team:Core/Infra) |
@@ -170,4 +170,7 @@ grant { | |||
|
|||
// system memory on Linux systems affected by JDK bug (#66629) | |||
permission java.io.FilePermission "/proc/meminfo", "read"; | |||
|
|||
// needed for PluginsServiceTests | |||
permission java.lang.RuntimePermission "createClassLoader"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've made a mistake here, wrong policy file. I'll submit a new commit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have a few questions
* For example: | ||
* | ||
* var pluginHandlers = pluginsService.loadServiceProviders(OperatorHandlerProvider.class); | ||
* </p> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: I think the closing p tag is normally omitted in javadoc (or must be?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought it was allowed, I see we use it in few places, but it's stripped now and I'll remove it from other PR.
* <p> | ||
* For example: | ||
* | ||
* var pluginHandlers = pluginsService.loadServiceProviders(OperatorHandlerProvider.class); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be in a <pre>
block?
*/ | ||
public <T> List<? extends T> loadServiceProviders(Class<T> service) { | ||
List<T> result = new ArrayList<>(); | ||
getClass().getModule().addUses(service); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this necessary? If a type is known in server, then it can be declared explicitly in server's module-info?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought of it as a convenience, but we can always explicitly declare the use in module-info. I'll remove it.
|
||
service = spy(service); | ||
|
||
doReturn(List.of(provider1).iterator()).when(service).providersIterator(eq(OperatorHandlerProvider.class), eq(fakeClassLoader)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since we are dealing with different classloaders, we should probably test more real-world situations. Can the test use real classloaders instead of mocking them? We have utilities for creating dummy jars with module infos, classes, etc. It seems doable to actually test the service loading.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I brought back the real classloader test version.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still see mocking? To clarify what I was suggesting: dynamically build a dummy plugin jar and zip (with the utilities I mentioned) so the providersIterator method can actually be tested with the real ServiceLoader
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah I see, okay, I'll go further in building dummy plugins. This way I think I probably won't need to add extra permissions for the test framework.
@elasticmachine update branch |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry I had these comments stuck pending 🤦
* <pre> | ||
* For example: | ||
* | ||
* var pluginHandlers = pluginsService.loadServiceProviders(OperatorHandlerProvider.class); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: I think only the code should be in the <pre>
?
|
||
service = spy(service); | ||
|
||
doReturn(List.of(provider1).iterator()).when(service).providersIterator(eq(OperatorHandlerProvider.class), eq(fakeClassLoader)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still see mocking? To clarify what I was suggesting: dynamically build a dummy plugin jar and zip (with the utilities I mentioned) so the providersIterator method can actually be tested with the real ServiceLoader
Thanks for the review @rjernst, I removed all mocking, real SPI loading now. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new test looks much better! I'm glad the recently added utilities help make testing this easier. Just one last nit on the testing.
@@ -293,6 +294,33 @@ static void loadExtensions(Collection<LoadedPlugin> plugins) { | |||
} | |||
} | |||
|
|||
// package private for testing | |||
<T> Iterator<T> providersIterator(Class<T> service, ClassLoader classLoader) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this can be folded into loadServiceProviders now that the tests don't mock it?
import org.elasticsearch.plugins.spi.TestService; | ||
import org.elasticsearch.plugins.ActionPlugin; | ||
import org.elasticsearch.plugins.Plugin; | ||
public final class FooPlugin extends Plugin implements ActionPlugin, TestService { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would separate this out. Due to the current plugin design, a Plugin subclass must be present, but this class should not be required to be the one that implements all service providers. In fact, I think it is important we test them decoupled, since that is the future we are working towards.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure thing, I was just lazy :). I'll create a separate TestService provider class.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done now. I've split the provider class into separate file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the iterations. LGTM!
Since plugins/modules are loaded in their own class loader, loading service providers (SPI implementations) from various service interfaces in plugins is not a simple call to
ServiceLoader.load
. This PR adds a helper method on PluginsService to load providers from plugins/modules given a service class.Example usage of this helper can be found here (in the related PR #86224):
https://github.com/elastic/elasticsearch/pull/86224/files#diff-b56bed42f5b0025886eb243ecb48678ac048bfba0ef047545dbb6504e357edf2R901
Relates to #86224