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

Introduce ToXContentObject interface #22387

Merged
merged 7 commits into from
Jan 6, 2017

Conversation

javanna
Copy link
Member

@javanna javanna commented Dec 30, 2016

ToXContentObject extends ToXContent without adding new methods to it, while allowing to mark classes that output complete xcontent objects to distinguish them from classes that require starting and ending an anonymous object externally.

Ideally ToXContent would be renamed to ToXContentFragment or something along those lines, but that would be a huge change in our codebase, hence we can simply document the fact that ToXContent outputs fragments with no guarantees that the output is valid per se without an external ancestor.

As part of this PR also some of our response classes are migrated to the new interface. In general all of our response should implement ToXContentObject, but some don't even implement yet ToXContent, while some that do will be migrated step by step as part of the effort made for REST high level client (essentially while writing parsing code, we can also adjust the toXContent methods.

RestToXContentListener, String#toString and XContentHelper#toXContent used to take a boolean argument to indicate whether the output needed to be wrapped into a new object. This decision doesn't have to be made case by case now. We can rely on whether a class implements ToXContent (fragment) or ToXContentObject (complete valid object) instead.

There are many more things that can be done to improve how deal with ToXContent classes, like unifying their toStrings output etc. those improvements will come later, one at a time.

Relates to # 3889
Relates to #16347

Copy link
Member

@nik9000 nik9000 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 minor stuff, some questions about naming, etc. But I think this'll be lovely to have.

I wonder if you can also convert the methods in XContentBuilder that take a ToXContent to taking ToXContentObject instead?

try {
XContentBuilder builder = JsonXContent.contentBuilder();
if (wrapInObject) {
if (toXContent instanceof ToXContentObject == false) {
Copy link
Member

Choose a reason for hiding this comment

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

I'd probably pull the instanceof check into a boolean variable just to make sure it is only done once.

I keep thinking that we should be able to remove the instanceof check by making another method and we could do that for the most part but I don't think it'd be worth it. At least not for now, anyway.

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'd probably pull the instanceof check into a boolean variable just to make sure it is only done once.

will do, makes sense.

I keep thinking that we should be able to remove the instanceof check by making another method and we could do that for the most part but I don't think it'd be worth it. At least not for now, anyway.

agreed, the instanceof checks are not nice. Yet they are an improvement compared to the previous boolean flag.
If we had two methods, the method variant that accepts ToXContent would also accept a ToXContentObject which would throw error when adding the anonymous object as it already has it. Not too sure. As you said it may stay this way for now.

@@ -26,6 +26,8 @@

/**
* An interface allowing to transfer an object to "XContent" using an {@link XContentBuilder}.
* The output may not be valid alone without a valid ancestor, or without starting and ending an
Copy link
Member

Choose a reason for hiding this comment

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

What about "The output may or may not be a value object. Objects implementing {@link ToXContentObject} output a valid value but those that don't may or may not require emitting a startObject and an endObject."


/**
* An interface allowing to transfer an object to "XContent" using an {@link XContentBuilder}.
* The difference between {@link ToXContent} and {@link ToXContentObject} is that the former outputs a fragment that
Copy link
Member

Choose a reason for hiding this comment

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

the former may output a fragment that requires....

* may require to start and end a new anonymous object externally, while the latter guarantees that what gets printed
* out is fully valid syntax without any external addition.
*/
public interface ToXContentObject extends 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 wonder if ToXContentValue is better. The idea being that objects that implement the interface output valid left hand side "values".

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 do not know. When I read value, I don't think of a complete object. But I do see how this is debatable. I would much rather have ToXContent and ToXContentFragment, but that'd require a huge change that I don't want to make at the moment. How about ToXContentValueObject? :D

Copy link
Member

Choose a reason for hiding this comment

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

I would much rather have ToXContent and ToXContentFragment

That would be the best option, but in the meanwhile I like ToXContentObject for the marker interface.

public final RestResponse buildResponse(Response response, XContentBuilder builder) throws Exception {
if (wrapInObject()) {
protected final void toXContent(Response response, XContentBuilder builder) throws IOException {
if (response instanceof ToXContentObject == false) {
Copy link
Member

Choose a reason for hiding this comment

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

It'd be nice to be able to change this to only take ToXContentObject one day....

Copy link
Member

Choose a reason for hiding this comment

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

But that day can wait.

Copy link
Member Author

Choose a reason for hiding this comment

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

yes it would be, we are not that far from that day, yet some more work is required to migrate response that are reused throughout different apis.

@javanna javanna force-pushed the enhancement/toxcontent_object branch from c764688 to d2516ca Compare January 3, 2017 13:39
@javanna
Copy link
Member Author

javanna commented Jan 3, 2017

@nik9000 thanks for your review, I have addressed your comments. I will wait for some more comments on naming and the approach taken from others before pushing. I think this is an improvement but there are a few things in this PR that are not super nice (e.g. the instanceof checks)

@nik9000
Copy link
Member

nik9000 commented Jan 3, 2017

I was thinking we could consider those instanceof checks fairly temporary. At some point we can remove the versions that take ToXContent - like in the response handler. I figure at some point later we can make a ToXContentFragment for the toString ones and remove the toString version that takes ToXContent and use ToXContentFragment. Eventually. If it looks like a good choice.

* Return a {@link String} that is the json representation of the provided
* {@link ToXContent}.
* Return a {@link String} that is the json representation of the provided {@link ToXContent}.
* Wraps the output into an anonymous object depending on whether the provided argument implements {@link ToXContentObject} or not
*/
public static String toString(ToXContent toXContent) {
Copy link
Member

@tlrx tlrx Jan 5, 2017

Choose a reason for hiding this comment

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

I feel like we could already change this method to

public static String toString(ToXContentObject o) {
  return toString(o, false);
}

and instead change all Strings.toString(ToXContent) calls to Strings.toString(ToXContent, false), in which we can just check that when wrapInObject is true the object does not also implement ToXContentObject.

This would avoid most instanceof checks and it prepares the field for a toString(ToXContentFragment o) method. Both of them would later delegate to a private static toString(ToXContent toXContent, boolean wrapInObject)...

* may require to start and end a new anonymous object externally, while the latter guarantees that what gets printed
* out is fully valid syntax without any external addition.
*/
public interface ToXContentObject extends 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 would much rather have ToXContent and ToXContentFragment

That would be the best option, but in the meanwhile I like ToXContentObject for the marker interface.

Copy link
Member

@tlrx tlrx left a comment

Choose a reason for hiding this comment

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

I like this change as well as the ToXContentObject name. I also left a minor comment.

@javanna javanna force-pushed the enhancement/toxcontent_object branch 3 times, most recently from cb42070 to f25c498 Compare January 5, 2017 17:06
@javanna
Copy link
Member Author

javanna commented Jan 5, 2017

The instanceOf checks are gone now. I followed Tanguy's suggestion in XContentHelper#toXContent and Strings#toString, to have two method variants with either ToXContent or ToXContentObject as an argument. As for RestToXContentListener and RestStatusToXContentListener, I moved over to ToXContentObject all the responses that use such listeners, so that they now require ToXContentObject. This PR is getting big-ish so we should get it in soon-ish :)

@javanna
Copy link
Member Author

javanna commented Jan 5, 2017

@s1monw would you mind having a look too please?

@s1monw
Copy link
Contributor

s1monw commented Jan 6, 2017

I wonder if should rather have a default method on the interface rather than a marker. The problem with a marker is that you have to do instanceof checks to do the right thing which are easily missable and if you cast up you are lost if you miss them?

@javanna
Copy link
Member Author

javanna commented Jan 6, 2017

@s1monw good point I agree that the current toString etc. methods will not work for instance if one does

ToXContent toXContent = searchResponse;
Strings.toString(toXContent);

although SearchResponse implements ToXContentObject.

I was considering as well a default method but I am not super sure if a different method helps. Then you would have two ways of printing out the same object which may get confusing. What about looking into not having ToXContentObject extend ToXContent anymore (and copying the same method)? I think that should be possible in the state that this PR is now. I think that would resolve the ambiguity?

@s1monw
Copy link
Contributor

s1monw commented Jan 6, 2017

I was suggesting this:

diff --git a/core/src/main/java/org/elasticsearch/common/xcontent/ToXContent.java b/core/src/main/java/org/elasticsearch/common/xcontent/ToXContent.java
index 01111fa..9efdaaa 100644
--- a/core/src/main/java/org/elasticsearch/common/xcontent/ToXContent.java
+++ b/core/src/main/java/org/elasticsearch/common/xcontent/ToXContent.java
@@ -126,4 +126,8 @@ public interface ToXContent {
     }
 
     XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException;
+
+    default boolean isFragment() {
+        return true;
+    }
 }

@s1monw
Copy link
Contributor

s1monw commented Jan 6, 2017

if you don't like this then please make sure you are doing instance of checks where you distinguish. I don't see them yet

@javanna
Copy link
Member Author

javanna commented Jan 6, 2017

@s1monw I followed your advice to resolve the ambiguity, and also kept the ToXContentObject marker around. Should look better now.

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.

LGTM

@javanna javanna force-pushed the enhancement/toxcontent_object branch from 61c9f3a to 1504ba8 Compare January 6, 2017 16:41
`ToXContentObject` extends `ToXContent` without adding new methods to it, while allowing to mark classes that output complete xcontent objects to distinguish them from classes that require starting and ending an anonymous object externally.

Ideally ToXContent would be renamed to ToXContentFragment, but that would be a huge change in our codebase, hence we simply document the fact that toXContent outputs fragments with no guarantees that the output is valid per se without an external ancestor.

Relates to elastic#16347
This involved changing also index, delete, update so that bulk can print them out in its own format.
…ToXContentObject

This also allows to make RestToXContentListener require ToXContentObject rather than ToXContent
@javanna javanna force-pushed the enhancement/toxcontent_object branch from 1504ba8 to 8d08dc0 Compare January 6, 2017 19:06
@javanna javanna merged commit ded694f into elastic:master Jan 6, 2017
@javanna javanna added v5.3.0 and removed v5.2.0 labels Jan 7, 2017
ywelsch added a commit that referenced this pull request Jan 10, 2017
ywelsch added a commit that referenced this pull request Jan 10, 2017
imotov added a commit to imotov/elasticsearch that referenced this pull request Jan 21, 2017
imotov added a commit that referenced this pull request Jan 21, 2017
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.

4 participants