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

Plugins: Add plugin extension capabilities #27881

Merged
merged 19 commits into from
Jan 3, 2018
Merged

Conversation

rjernst
Copy link
Member

@rjernst rjernst commented Dec 19, 2017

This commit adds the infrastructure to plugin building and loading to
allow one plugin to extend another. That is, one plugin may extend
another by the "parent" plugin allowing itself to be extended through
java SPI. When all plugins extending a plugin are finished loading, the
"parent" plugin has a callback (through the ExtensiblePlugin interface)
allowing it to reload SPI.

This commit also adds an example plugin which uses as-yet implemented
extensibility (adding to the painless whitelist).

This commit adds the infrastructure to plugin building and loading to
allow one plugin to extend another. That is, one plugin may extend
another by the "parent" plugin allowing itself to be extended through
java SPI. When all plugins extending a plugin are finished loading, the
"parent" plugin has a callback (through the ExtensiblePlugin interface)
allowing it to reload SPI.

This commit also adds an example plugin which uses as-yet implemented
extensibility (adding to the painless whitelist).
@rjernst rjernst added :Core/Infra/Plugins Plugin API and infrastructure v6.2.0 v7.0.0 labels Dec 19, 2017
@rjernst
Copy link
Member Author

rjernst commented Dec 19, 2017

Note this PR currently has a couple nocommits for testing and moving the createClassLoader permission out to a separate jar (we don't actually want to add this back to core). But I wanted to get this PR up so everything else can be checked.

@rjernst rjernst requested review from jasontedor and s1monw December 19, 2017 00:37
Copy link
Contributor

@s1monw s1monw left a comment

Choose a reason for hiding this comment

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

I left some commetns. LGTM in general

@@ -34,7 +35,7 @@
/**
* Registers Painless as a plugin.
*/
public final class PainlessPlugin extends Plugin implements ScriptPlugin {
public final class PainlessPlugin extends Plugin implements ScriptPlugin, ExtensiblePlugin {
Copy link
Contributor

Choose a reason for hiding this comment

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

shouldn't this implement reloadSPI ?

Copy link
Member Author

Choose a reason for hiding this comment

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

Painless doesn't have any SPI interfaces yet. This is just the scaffolding to add it (as an example of how SPI works in general for this PR).


import org.elasticsearch.plugins.Plugin;

public class MyWhitelistPlugin extends Plugin {
Copy link
Contributor

Choose a reason for hiding this comment

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

hmm it's unclear to me what this extends can we have a real extension here?

Copy link
Member Author

Choose a reason for hiding this comment

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

We don't have any actual SPI to extend yet. This will be filled in when painless whitelisting is finished.

private static class ExtendedPluginsClassLoader extends ClassLoader {

/** Loaders of plugins extended by a plugin. */
private List<ClassLoader> extendedLoaders;
Copy link
Contributor

Choose a reason for hiding this comment

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

final?

@@ -82,6 +88,11 @@ public PluginInfo(final StreamInput in) throws IOException {
this.description = in.readString();
this.version = in.readString();
this.classname = in.readString();
if (in.getVersion().onOrAfter(Version.V_6_1_0)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

this should be 6.2 right?

Copy link
Member Author

Choose a reason for hiding this comment

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

Ooops, yes. Old branch!

Copy link
Member

Choose a reason for hiding this comment

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

This still needs to be updated to 6.2.

@@ -100,6 +111,9 @@ public void writeTo(final StreamOutput out) throws IOException {
out.writeString(description);
out.writeString(version);
out.writeString(classname);
if (out.getVersion().onOrAfter(Version.V_6_1_0)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

this should be 6.2

@@ -131,4 +131,7 @@ grant {
permission java.io.FilePermission "/sys/fs/cgroup/cpuacct/-", "read";
permission java.io.FilePermission "/sys/fs/cgroup/memory", "read";
permission java.io.FilePermission "/sys/fs/cgroup/memory/-", "read";

// nocommit: needed by plugin service for extensible plugins, move to a separate jar
Copy link
Contributor

Choose a reason for hiding this comment

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

do we want to add a elastic-commons jar to do that?

Copy link
Member Author

Choose a reason for hiding this comment

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

After discussing with @jasontedor this is my plan (although a jar on its own with the extension class loader, not in a general commons jar, since I want to reduce what code is allowed the extra permission to the bare minimum).

Copy link
Member

@jasontedor jasontedor left a comment

Choose a reason for hiding this comment

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

Looking good. I left a few comments, nothing major.

@@ -265,6 +291,7 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws
builder.field("version", version);
builder.field("description", description);
builder.field("classname", classname);
builder.array("extended_plugins", extendsPlugins);
Copy link
Member

Choose a reason for hiding this comment

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

Nit: For consistency, should all the extends plugins be extendedPlugins?

Copy link
Member Author

Choose a reason for hiding this comment

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

I went back and forth on the naming, but at this point I'm fine with that. I'll change them all.

@@ -47,6 +47,7 @@ public void testReadFromProperties() throws Exception {
assertEquals("fake desc", info.getDescription());
assertEquals("1.0", info.getVersion());
assertEquals("FakePlugin", info.getClassname());
assertTrue(info.getExtendsPlugins().isEmpty());
Copy link
Member

Choose a reason for hiding this comment

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

I prefer:

diff --git a/core/src/test/java/org/elasticsearch/plugins/PluginInfoTests.java b/core/src/test/java/org/elasticsearch/plugins/PluginInfoTests.java
index 2a96538e83..a188f3c765 100644
--- a/core/src/test/java/org/elasticsearch/plugins/PluginInfoTests.java
+++ b/core/src/test/java/org/elasticsearch/plugins/PluginInfoTests.java
@@ -30,6 +30,7 @@ import java.util.List;
 import java.util.stream.Collectors;
 
 import static org.hamcrest.Matchers.contains;
+import static org.hamcrest.Matchers.empty;
 
 public class PluginInfoTests extends ESTestCase {
 
@@ -47,7 +48,7 @@ public class PluginInfoTests extends ESTestCase {
         assertEquals("fake desc", info.getDescription());
         assertEquals("1.0", info.getVersion());
         assertEquals("FakePlugin", info.getClassname());
-        assertTrue(info.getExtendsPlugins().isEmpty());
+        assertThat(info.getExtendsPlugins(), empty());
     }
 
     public void testReadFromPropertiesNameMissing() throws Exception {

because it gives better output if the assertion fails.

"classname", "FakePlugin",
"extends.plugins", "foo");
PluginInfo info = PluginInfo.readFromProperties(pluginDir);
assertThat(info.getExtendsPlugins(), contains("foo"));
Copy link
Member

Choose a reason for hiding this comment

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

I think we can assert stronger here, that it is equal to the singleton list "foo"?

Copy link
Member Author

Choose a reason for hiding this comment

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

contains does an exact match. From the javadocs:

For a positive match, the examined iterable must be of the same length as the number of specified items.

Copy link
Member

Choose a reason for hiding this comment

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

I forget this every time because it's so horribly named. Thanks.

"classname", "FakePlugin",
"extends.plugins", "foo,bar,baz");
PluginInfo info = PluginInfo.readFromProperties(pluginDir);
assertThat(info.getExtendsPlugins(), contains("foo", "bar", "baz"));
Copy link
Member

Choose a reason for hiding this comment

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

I think we can assert stronger here, that it is equal to in any order the list "foo", "bar", "baz" (there is a matcher for this)?

Copy link
Member Author

Choose a reason for hiding this comment

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

Same here. I think expecting the exact order is fine too, since getExtendedPlugins returns a List.

Copy link
Member

Choose a reason for hiding this comment

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

Yes, it's poorly named and it traps me every time.

Copy link
Member Author

Choose a reason for hiding this comment

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

Same as before, this is already an exact match.

"classname", "FakePlugin",
"extends.plugins", "");
PluginInfo info = PluginInfo.readFromProperties(pluginDir);
assertTrue(info.getExtendsPlugins().isEmpty());
Copy link
Member

Choose a reason for hiding this comment

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

Can we use the empty collections matchers here?

@@ -82,6 +88,11 @@ public PluginInfo(final StreamInput in) throws IOException {
this.description = in.readString();
this.version = in.readString();
this.classname = in.readString();
if (in.getVersion().onOrAfter(Version.V_6_1_0)) {
Copy link
Member

Choose a reason for hiding this comment

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

This still needs to be updated to 6.2.

throw new IllegalStateException("Already added " + name + " codebase for testing");
}
} catch (ClassNotFoundException e) {
// no class, fall through to not add
Copy link
Member

Choose a reason for hiding this comment

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

When can this happen that it's okay to continue?

Copy link
Member Author

Choose a reason for hiding this comment

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

It would happen in any tests other than core, since we only add the plugin-classloader jar to the core tests runtime classpath. I'll add a comment.

@rjernst
Copy link
Member Author

rjernst commented Dec 21, 2017

@jasontedor @s1monw I believe I have addressed all your comments and all the nocommits are now gone.

Copy link
Member

@jasontedor jasontedor left a comment

Choose a reason for hiding this comment

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

LGTM.

@rjernst
Copy link
Member Author

rjernst commented Dec 21, 2017

@elasticmachine test this please

Tim-Brooks added a commit to Tim-Brooks/elasticsearch that referenced this pull request Dec 30, 2017
This commit disables the nio transport as an option for the test
transport in integration tests. This is because it does not currently
run properly in intellij due to socket permissions. It should be
reenabled once elastic#27881 is merged (and the proper permissions are added).
Tim-Brooks added a commit that referenced this pull request Dec 31, 2017
This commit disables the nio transport as an option for the test
transport in integration tests. This is because it does not currently
run properly in intellij due to socket permissions. It should be
reenabled once #27881 is merged (and the proper permissions are added).
@rjernst rjernst merged commit d36ec18 into elastic:master Jan 3, 2018
@rjernst rjernst deleted the plugin_spi branch January 3, 2018 19:12
rjernst added a commit that referenced this pull request Jan 4, 2018
This commit adds the infrastructure to plugin building and loading to
allow one plugin to extend another. That is, one plugin may extend
another by the "parent" plugin allowing itself to be extended through
java SPI. When all plugins extending a plugin are finished loading, the
"parent" plugin has a callback (through the ExtensiblePlugin interface)
allowing it to reload SPI.

This commit also adds an example plugin which uses as-yet implemented
extensibility (adding to the painless whitelist).
martijnvg added a commit that referenced this pull request Jan 4, 2018
* es/master: (53 commits)
  Bump compat version for local depdendent test to 6.2.0
  Pass `java.locale.providers=COMPAT` to Java 9 onwards (#28080)
  Allow shrinking of indices from a previous major (#28076)
  Remove deprecated exceptions (#28059)
  Add Writeable.Reader support to TransportResponseHandler (#28010)
  Plugins: Add plugin extension capabilities (#27881)
  Fix cluster.routing.allocation.enable and cluster.routing.rebalance.enable casing (#28037)
  [Test] Fix scores for dcg in RankEvalRequestIT and RankEvalYamlIT
  [Docs] Add note on limitation for significant_text with nested objects (#28052)
  Enable convert processor to support Long and Double. (#27957)
  Enable Wildfly tests on JDK 9 and JDK 10
  [Test] Fix allowed delta for calculated scores in DiscountedCumulativeGainTests
  [Test] Mute DiscountedCumulativeGainTests on ARM
  Only bind loopback addresses when binding to local
  Fix assertion in Wildfly build
  Fix typo in comment in Wildfly build
  Use ephemeral ports in Wildfly tests
  Update fuzzy-query.asciidoc (#28032)
  Just another elasticsearch library (#27996)
  Disable nio test transport (#28028)
  ...
jasontedor added a commit that referenced this pull request Jan 4, 2018
* master:
  Set the elasticsearch-nio codebase for tests (#28067)
  Bump compat version for local depdendent test to 6.2.0
  Pass `java.locale.providers=COMPAT` to Java 9 onwards (#28080)
  Allow shrinking of indices from a previous major (#28076)
  Remove deprecated exceptions (#28059)
  Add Writeable.Reader support to TransportResponseHandler (#28010)
  Plugins: Add plugin extension capabilities (#27881)
rjernst added a commit that referenced this pull request Jan 4, 2018
martijnvg added a commit that referenced this pull request Jan 5, 2018
* es/master:
  Introduce Gradle wrapper
  Ignore GIT_COMMIT when calculating commit hash
  Re-enable bwc tests after #27881 was backported
  Set the elasticsearch-nio codebase for tests (#28067)
martijnvg added a commit that referenced this pull request Jan 5, 2018
* es/6.x:
  Fix link to parent join (#28085)
  Introduce Gradle wrapper
  Ignore GIT_COMMIT when calculating commit hash
  Plugins: Add plugin extension capabilities (#27881)
jasontedor added a commit to jasontedor/elasticsearch that referenced this pull request Jan 5, 2018
* master:
  test: replaced try-catch statements with expectThrows(...)
  Add getWarmer and getTranslog method to NodeIndicesStats (elastic#28092)
  fix doc mistake
  Added ASN support for Ingest GeoIP plugin.
  Fix global aggregation that requires breadth first and scores (elastic#27942)
  Introduce Gradle wrapper
  Ignore GIT_COMMIT when calculating commit hash
  Re-enable bwc tests after elastic#27881 was backported
jasontedor added a commit to jasontedor/elasticsearch that referenced this pull request Jan 6, 2018
* master: (25 commits)
  Remove Gradle cheatsheet
  Fix reproduction info to point to Gradle wrapper
  Update platforms tests to use Gradle wrapper
  Update testing docs to reflect Gradle wrapper
  Painless: Modify Loader to Load Classes Directly from Definition (elastic#28088)
  Update contributing docs to use the Gradle wrapper
  Create nio-transport plugin for NioTransport (elastic#27949)
  test: replaced try-catch statements with expectThrows(...)
  Add getWarmer and getTranslog method to NodeIndicesStats (elastic#28092)
  fix doc mistake
  Added ASN support for Ingest GeoIP plugin.
  Fix global aggregation that requires breadth first and scores (elastic#27942)
  Introduce Gradle wrapper
  Ignore GIT_COMMIT when calculating commit hash
  Re-enable bwc tests after elastic#27881 was backported
  Set the elasticsearch-nio codebase for tests (elastic#28067)
  Bump compat version for local depdendent test to 6.2.0
  Pass `java.locale.providers=COMPAT` to Java 9 onwards (elastic#28080)
  Allow shrinking of indices from a previous major (elastic#28076)
  Remove deprecated exceptions (elastic#28059)
  ...
@sscarduzio
Copy link
Contributor

sscarduzio commented Feb 10, 2018

Hi guys, is there a way to load lib/plugin-classloader-6.2.0.jar from maven?
I ask because I'm trying to develop my plugin in IntelliJ, so I have the plugin project that depends on

compile 'org.elasticsearch:elasticsearch:' + esVersion
compile 'org.elasticsearch.plugin:transport-netty4-client:' + esVersion

And I keep on getting:

java.lang.NoClassDefFoundError: org/elasticsearch/plugins/ExtendedPluginsClassLoader

@rjernst
Copy link
Member Author

rjernst commented Feb 10, 2018

@sscarduzio Please ask questions on our forum. A comment on a closed PR is likely to be missed. And more information would be helpful (eg what you are running which results in the missing class). I had intended this to not be published as it is an implementation detail of how plugins are loaded, but I am happy to investigate if that won't work for plugin authors.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants