Skip to content

Conversation

jdconrad
Copy link
Contributor

@jdconrad jdconrad commented Nov 18, 2024

This change loads all the modules and creates the module layers for plugins prior to entitlement checking during the 2nd phase of bootstrap initialization. This will allow us to know what modules exist for both validation and checking prior to actually loading any plugin classes (in a follow up change).

There are now two classes:

  • PluginsLoader which does the module loading and layer creation
  • PluginsService which uses a PluginsLoader to create the main plugin classes and start the plugins

@jdconrad jdconrad added :Core/Infra/Plugins Plugin API and infrastructure >refactoring labels Nov 18, 2024
@jdconrad jdconrad requested a review from a team as a code owner November 18, 2024 23:58
@elasticsearchmachine elasticsearchmachine added v9.0.0 Team:Core/Infra Meta label for core/infra team labels Nov 18, 2024
@elasticsearchmachine
Copy link
Collaborator

Pinging @elastic/es-core-infra (Team:Core/Infra)

Copy link
Contributor

@ldematte ldematte left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! A couple of (optional) notes.
I've tried to follow the movement of code from PluginsService to PluginsLoader, and it seems pretty much matching.

Copy link
Member

@rjernst rjernst left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, a few suggestions but most of them are subjective and could be followups if we decide to do them.

null,
null,
Path.of(System.getProperty("plugins.dir"))
new PluginsLoader(null, Path.of(System.getProperty("plugins.dir")))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Could this pass an empty directory for module dir? Then we could enforce non-null paths. We needed to support null before because the mock plugins service needed to call its super, but I think we could get around that with the mock loader.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's do this as a follow up PR as part of adding MockPluginsLoader potentially.

*/
public MockPluginsService(Settings settings, Environment environment, Collection<Class<? extends Plugin>> classpathPlugins) {
super(settings, environment.configFile(), environment.modulesFile(), environment.pluginsFile());
super(settings, environment.configFile(), new PluginsLoader(environment.modulesFile(), environment.pluginsFile()) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps we should have a MockPluginsLoader? This anonymous class is difficult to read within a super call

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another idea I had in my POC: what if we remove the protected method, and we pass down a ServerExportsService (or better name!) to the ctor?

Something like

@FunctionalInterface
public interface ServerExportsService {
   void add(Map<String, List<ModuleQualifiedExportsService>> qualifiedExports);
}

And then have

public static void noServerExportsService(Map<String, List<ModuleQualifiedExportsService>> qualifiedExports) {}

public static void defaultServerExportsService(Map<String, List<ModuleQualifiedExportsService>> qualifiedExports) {
        // current addServerExportsService implementation
}

The ctor called from prod code will pass down PluginsLoader::defaultServerExportsService, but test can call a full ctor passing explicitly PluginsLoader::noServerExportsService:

PluginsLoader(/* same args as today */) { // Called by Elasticsearch.java
   this(..., PluginsLoader::defaultServerExportsService)
}

PluginsLoader(/* same args as today */, ServerExportsService additionalServerExports) {
   this.additionalServerExports = additionalServerExports; // and then call additionalServerExports.add(...);
}

Test will call
new PluginsLoader(..., PluginsLoader::noServerExportService) (or whatever they want)
(this will also remove the need to suppress for this-escape)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rjernst What do you think of the @ldematte's idea instead of a MockPluginLoader? I don't have a strong opinion either way so I'm happy to implement either idea.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you both like my idea, I think it should go in a follow-up PR anyway :)

Copy link
Contributor Author

@jdconrad jdconrad Nov 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On second thought, with multiple different ideas here, let's get together to discuss this as a follow up PR.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, totally agree!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤔 FWIW I'm not seeing the problem. The code looks pretty clear to me.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem is (1) having an anonymous subclass with an overridden method inside a super call packs a lot into that super call. Additionally (2) the overridden implementation is the same in both places (makes the method a no-op).

But we can work this out in a followup.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree the anonymous class should go. It's confusing, but I'm wondering if we should also add in @ldematte's idea to replace the exports method. It does seem clearer to me on the surface. The main downside I see to some of these changes it pushes test-only code into PluginsLoader.

Copy link
Contributor

@ldematte ldematte left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM2; the optional suggestions still stand, but can go in another PR (or even discarded)

*/
public MockPluginsService(Settings settings, Environment environment, Collection<Class<? extends Plugin>> classpathPlugins) {
super(settings, environment.configFile(), environment.modulesFile(), environment.pluginsFile());
super(settings, environment.configFile(), new PluginsLoader(environment.modulesFile(), environment.pluginsFile()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another idea I had in my POC: what if we remove the protected method, and we pass down a ServerExportsService (or better name!) to the ctor?

Something like

@FunctionalInterface
public interface ServerExportsService {
   void add(Map<String, List<ModuleQualifiedExportsService>> qualifiedExports);
}

And then have

public static void noServerExportsService(Map<String, List<ModuleQualifiedExportsService>> qualifiedExports) {}

public static void defaultServerExportsService(Map<String, List<ModuleQualifiedExportsService>> qualifiedExports) {
        // current addServerExportsService implementation
}

The ctor called from prod code will pass down PluginsLoader::defaultServerExportsService, but test can call a full ctor passing explicitly PluginsLoader::noServerExportsService:

PluginsLoader(/* same args as today */) { // Called by Elasticsearch.java
   this(..., PluginsLoader::defaultServerExportsService)
}

PluginsLoader(/* same args as today */, ServerExportsService additionalServerExports) {
   this.additionalServerExports = additionalServerExports; // and then call additionalServerExports.add(...);
}

Test will call
new PluginsLoader(..., PluginsLoader::noServerExportService) (or whatever they want)
(this will also remove the need to suppress for this-escape)

@jdconrad
Copy link
Contributor Author

@rjernst @ldematte Thank you for the reviews. I will follow up with you both about refactoring the mock code a bit more, but will leave that for a follow up PR.

@ldematte
Copy link
Contributor

refactoring the mock code a bit more, but will leave that for a follow up PR.

Definitely, thanks for the changes, looks very clean! Merge at will, for me.

@jdconrad jdconrad merged commit 4f46924 into elastic:main Nov 20, 2024
15 of 16 checks passed
jdconrad added a commit to jdconrad/elasticsearch that referenced this pull request Nov 20, 2024
elastic#116998)

This change loads all the modules and creates the module layers for plugins prior to entitlement 
checking during the 2nd phase of bootstrap initialization. This will allow us to know what modules exist 
for both validation and checking prior to actually loading any plugin classes (in a follow up change).

There are now two classes:

    PluginsLoader which does the module loading and layer creation
    PluginsService which uses a PluginsLoader to create the main plugin classes and start the plugins
elasticsearchmachine pushed a commit that referenced this pull request Nov 21, 2024
#116998) (#117215)

This change loads all the modules and creates the module layers for plugins prior to entitlement 
checking during the 2nd phase of bootstrap initialization. This will allow us to know what modules exist 
for both validation and checking prior to actually loading any plugin classes (in a follow up change).

There are now two classes:

    PluginsLoader which does the module loading and layer creation
    PluginsService which uses a PluginsLoader to create the main plugin classes and start the plugins
alexey-ivanov-es pushed a commit to alexey-ivanov-es/elasticsearch that referenced this pull request Nov 28, 2024
elastic#116998)

This change loads all the modules and creates the module layers for plugins prior to entitlement 
checking during the 2nd phase of bootstrap initialization. This will allow us to know what modules exist 
for both validation and checking prior to actually loading any plugin classes (in a follow up change).

There are now two classes:

    PluginsLoader which does the module loading and layer creation
    PluginsService which uses a PluginsLoader to create the main plugin classes and start the plugins
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

:Core/Infra/Plugins Plugin API and infrastructure >refactoring Team:Core/Infra Meta label for core/infra team v9.0.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants