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

Allow RestHighLevelClient to use plugins #25024

Conversation

tlrx
Copy link
Member

@tlrx tlrx commented Jun 2, 2017

This commit changes the RestHighLevelClient class so that it now accepts a list of plugin classes as a constructor parameter. Similarly to the PreBuiltTransportClient, the RestHighLevelClient uses the PluginService to load the plugins and then extracts their aggregations and suggesters specs. The aggregation specs have been changed in core in order to add a addResultParser method that allows to reference the parsing methods of an aggregation. This information is use in the RestHighLevelClient
to register additional NamedXContent entries to parse back custom aggregations/suggesters.

This commit changes the RestHighLevelClient class so that it now accepts
 a list of plugin classes as a constructor parameter. Similarly to the
 PreBuiltTransportClient, the RestHighLevelClient uses the PluginService
 to load the plugins and then extracts their aggregations and suggesters
 specs. The aggregation specs have been changed in core in order to add
 a `addResultParser` method that allows to reference the parsing methods
 of an aggregation. This information is use in the RestHighLevelClient
 to register additional NamedXContent entries to parse back custom
 aggregations/suggesters.
@rjernst
Copy link
Member

rjernst commented Jun 2, 2017

I'm not sure we should do this. This adds a tight coupling (the plugins service) between ES core and the high level client. Can we instead use java's mechanism for this, which is SPI?

@nik9000
Copy link
Member

nik9000 commented Jun 2, 2017

Can we instead use java's mechanism for this, which is SPI?

I'm not sure I like the idea of introducing another plugin lookup mechanism but I agree with Ryan that we should try not to introduce this level of coupling. I'd like to be able to reason about the Plugin API being internal to Elasticsearch.

@rjernst
Copy link
Member

rjernst commented Jun 2, 2017

introducing another plugin lookup mechanism

Its not "another", it is the original. SPI is perfect for this type of thing. Note that I am not suggesting a "plugin" be loaded through SPI, but instead named xcontent parsers. Then there is no need for an instance of the client to pass anything in, it is automatically loaded from the classpath by including these dependencies in your application classpath.

@nik9000
Copy link
Member

nik9000 commented Jun 2, 2017

@rjernst I agree with you that SPI is probably the way to go. It is totally another lookup mechanism because we don't use it now. I'm not talking about more code. I'm talking about more things to understand. SPI swaps one set of issues for another.

Copy link
Member

@cbuescher cbuescher left a comment

Choose a reason for hiding this comment

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

@tlrx thanks, I took a look and left a few minor comments, but I think the test are already great. I don't have an opinion on the lookup mechanism discussion of @nik9000 and @rjernst at this point though.

@@ -160,23 +165,52 @@
* Can be sub-classed to expose additional client methods that make use of endpoints added to Elasticsearch through plugins, or to
* add support for custom response sections, again added to Elasticsearch through plugins.
*/
@SuppressWarnings("varargs")
Copy link
Member

Choose a reason for hiding this comment

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

Nit: curious about what this does, it shows up as "Unsupported @SuppressWarnings" in Eclipse

}

/**
* Creates a {@link RestHighLevelClient} given the low level {@link RestClient} that it should use to perform requests and
* a list of entries that allow to parse custom response sections added to Elasticsearch through plugins.
*/
protected RestHighLevelClient(RestClient restClient, List<NamedXContentRegistry.Entry> namedXContentEntries) {
private RestHighLevelClient(RestClient restClient, List<NamedXContentRegistry.Entry> namedXContentEntries) {
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 be moved into the ctor where it is called now?

Collection<Class<? extends Plugin>> plugins) {
List<Class<? extends Plugin>> listOfPlugins = new ArrayList<>(preInstalledPlugins);
for (Class<? extends Plugin> plugin : plugins) {
if (listOfPlugins.contains(plugin)) {
Copy link
Member

Choose a reason for hiding this comment

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

Just out of curiosity: plugin equality here is exact object equality, since plugins are singletons, no?

@@ -555,4 +587,27 @@ static boolean convertExistsResponse(Response response) {
(parser, context) -> CompletionSuggestion.fromXContent(parser, (String)context)));
return entries;
}

static List<NamedXContentRegistry.Entry> pluginsNamedXContents(Settings settings,
Copy link
Member

Choose a reason for hiding this comment

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

nit: private?

@@ -542,8 +576,6 @@ static boolean convertExistsResponse(Response response) {
map.put(SignificantLongTerms.NAME, (p, c) -> ParsedSignificantLongTerms.fromXContent(p, (String) c));
map.put(SignificantStringTerms.NAME, (p, c) -> ParsedSignificantStringTerms.fromXContent(p, (String) c));
map.put(ScriptedMetricAggregationBuilder.NAME, (p, c) -> ParsedScriptedMetric.fromXContent(p, (String) c));
map.put(ChildrenAggregationBuilder.NAME, (p, c) -> ParsedChildren.fromXContent(p, (String) c));
map.put(MatrixStatsAggregationBuilder.NAME, (p, c) -> ParsedMatrixStats.fromXContent(p, (String) c));
Copy link
Member

Choose a reason for hiding this comment

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

Why are these removed? Should the user have to specify them as client plugins explicitely, even if they are bundled with core by default?

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 I understand now, is it because they are part of PRE_INSTALLED_PLUGINS?


TermSuggestion suggestion = new TermSuggestion(suggestEntry.getKey(), customNumber, SortBy.SCORE);
for (int i = 0; i < customNumber; i++) {
suggestion.addTerm(new TermSuggestion.Entry(new Text("term " + i), i, customNumber));
Copy link
Member

Choose a reason for hiding this comment

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

Nice, I just figured out how you check the response by preparing it here.

@@ -262,11 +267,33 @@ public AggregationSpec addResultReader(String writeableName, Writeable.Reader<?
}

/**
* Get the readers that must be registered for this aggregation's results.
* Get the readers that can be used to parse this aggregation's results when it has been printed out as a {@link ToXContent}.
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 this comment is off, the method still returns the readers for the transport level. This is the right comment for getResultParsers() below though.

/**
* Adds a {@link ContextParser} that can be used to parse this aggregation when it has been printed out as a {@link ToXContent}.
*/
public AggregationSpec addResultParser(String typeName, ContextParser<Object, ? extends ParsedAggregation> resultParser) {
Copy link
Member

Choose a reason for hiding this comment

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

This currently seems ony to be used by the other addResultParser() method, I assume this is still public so that plugin authors can have different ways of providing the name of the aggregation?

* Get the parsers that can be used to parse this aggregation's results when it has been printed out as a {@link ToXContent}.
*/
public Map<String, ContextParser<Object, ? extends ParsedAggregation>> getResultParsers() {
return resultParsers;
Copy link
Member

Choose a reason for hiding this comment

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

maybe immutable?

@@ -48,7 +48,7 @@ public final String getName() {
return name;
}

protected void setName(String name) {
public void setName(String name) {
Copy link
Member

Choose a reason for hiding this comment

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

Nit: Should still work as protected as far as I can see.

@tlrx
Copy link
Member Author

tlrx commented Jun 6, 2017

Thanks @cbuescher @rjernst @nik9000 for your feedback.

I agree this pull request introduces a tight coupling between the high level rest client and the PluginService and I didn't really like too while I was coding it (but I do like the similitude between TransportClient and RestHighLevelClient instantiation with plugin classes). I just didn't think of SPI at all and it's a good suggestion.

I could revisit this pull request so that it uses SPI to load implementations of a core interface (let's call it org.elasticsearch.plugins.spi.NamedXContentProvider but better suggestions are welcome)

I can open another pull request in core to create this NamedXContentProvider interface (that basically has 1 method List<NamedXContentRegistry.Entry> getNamedXContentParsers(), add some logic in PluginBuildPlugin so that the plugin META-INF service file is automatically created and changed parent-join and aggs-matrix-stats plugins to provide implementations of the service.

@nik9000
Copy link
Member

nik9000 commented Jun 6, 2017

I'd probably let this PR sit and open competing PR that uses the SPI. It doesn't have to do all the plugins, just a one or two to make it clear that it'd work.

@tlrx
Copy link
Member Author

tlrx commented Jun 7, 2017

I opened #25097 and #25098

@javanna
Copy link
Member

javanna commented Jun 8, 2017

I think that we need to extract the plugin test out of this PR, all the rest is in other PRs, right?

@tlrx tlrx closed this Jun 15, 2017
@tlrx tlrx deleted the use-custom-plugin-with-rest-high-level-client branch June 15, 2017 11:12
@tlrx
Copy link
Member Author

tlrx commented Jun 15, 2017

I think with #25098 and #25106 we're good.

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.

7 participants