Переглянути джерело

Consolidate the last easy parser construction (#22095)

Moves the last of the "easy" parser construction into
`RestRequest`, this time with a new method
`RestRequest#contentParser`. The rest of the production
code that builds `XContentParser` isn't "easy" because it is
exposed in the Transport Client API (a Builder) object.
Nik Everett 9 роки тому
батько
коміт
749039ad4f
25 змінених файлів з 203 додано та 444 видалено
  1. 6 68
      core/src/main/java/org/elasticsearch/action/admin/cluster/repositories/put/PutRepositoryRequest.java
  2. 2 75
      core/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/create/CreateSnapshotRequest.java
  3. 3 88
      core/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/restore/RestoreSnapshotRequest.java
  4. 1 20
      core/src/main/java/org/elasticsearch/action/admin/indices/rollover/RolloverRequest.java
  5. 1 20
      core/src/main/java/org/elasticsearch/action/admin/indices/shrink/ShrinkRequest.java
  6. 4 3
      core/src/main/java/org/elasticsearch/action/bulk/BulkRequest.java
  7. 39 57
      core/src/main/java/org/elasticsearch/action/update/UpdateRequest.java
  8. 29 9
      core/src/main/java/org/elasticsearch/rest/RestRequest.java
  9. 1 5
      core/src/main/java/org/elasticsearch/rest/action/admin/cluster/RestClusterRerouteAction.java
  10. 1 2
      core/src/main/java/org/elasticsearch/rest/action/admin/cluster/RestClusterUpdateSettingsAction.java
  11. 1 1
      core/src/main/java/org/elasticsearch/rest/action/admin/cluster/RestCreateSnapshotAction.java
  12. 4 1
      core/src/main/java/org/elasticsearch/rest/action/admin/cluster/RestPutRepositoryAction.java
  13. 1 1
      core/src/main/java/org/elasticsearch/rest/action/admin/cluster/RestRestoreSnapshotAction.java
  14. 0 1
      core/src/main/java/org/elasticsearch/rest/action/admin/indices/RestCreateIndexAction.java
  15. 1 1
      core/src/main/java/org/elasticsearch/rest/action/admin/indices/RestIndexPutAliasAction.java
  16. 1 3
      core/src/main/java/org/elasticsearch/rest/action/admin/indices/RestIndicesAliasesAction.java
  17. 2 4
      core/src/main/java/org/elasticsearch/rest/action/admin/indices/RestRolloverIndexAction.java
  18. 2 4
      core/src/main/java/org/elasticsearch/rest/action/admin/indices/RestShrinkIndexAction.java
  19. 3 4
      core/src/main/java/org/elasticsearch/rest/action/document/RestUpdateAction.java
  20. 4 2
      core/src/test/java/org/elasticsearch/action/admin/indices/rollover/RolloverRequestTests.java
  21. 61 53
      core/src/test/java/org/elasticsearch/action/update/UpdateRequestTests.java
  22. 28 9
      core/src/test/java/org/elasticsearch/rest/RestRequestTests.java
  23. 5 6
      core/src/test/java/org/elasticsearch/snapshots/SnapshotRequestsTests.java
  24. 2 5
      modules/reindex/src/main/java/org/elasticsearch/index/reindex/RestReindexAction.java
  25. 1 2
      qa/smoke-test-http/src/test/java/org/elasticsearch/http/TestDeprecationHeaderRestAction.java

+ 6 - 68
core/src/main/java/org/elasticsearch/action/admin/cluster/repositories/put/PutRepositoryRequest.java

@@ -22,22 +22,20 @@ package org.elasticsearch.action.admin.cluster.repositories.put;
 import org.elasticsearch.ElasticsearchGenerationException;
 import org.elasticsearch.action.ActionRequestValidationException;
 import org.elasticsearch.action.support.master.AcknowledgedRequest;
-import org.elasticsearch.common.bytes.BytesReference;
 import org.elasticsearch.common.io.stream.StreamInput;
 import org.elasticsearch.common.io.stream.StreamOutput;
 import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.common.xcontent.XContentBuilder;
 import org.elasticsearch.common.xcontent.XContentFactory;
-import org.elasticsearch.common.xcontent.XContentParser;
 import org.elasticsearch.common.xcontent.XContentType;
 
 import java.io.IOException;
 import java.util.Map;
 
 import static org.elasticsearch.action.ValidateActions.addValidationError;
-import static org.elasticsearch.common.settings.Settings.Builder.EMPTY_SETTINGS;
 import static org.elasticsearch.common.settings.Settings.readSettingsFromStream;
 import static org.elasticsearch.common.settings.Settings.writeSettingsToStream;
+import static org.elasticsearch.common.settings.Settings.Builder.EMPTY_SETTINGS;
 
 /**
  * Register repository request.
@@ -198,18 +196,8 @@ public class PutRepositoryRequest extends AcknowledgedRequest<PutRepositoryReque
      *
      * @param repositoryDefinition repository definition
      */
-    public PutRepositoryRequest source(XContentBuilder repositoryDefinition) {
-        return source(repositoryDefinition.bytes());
-    }
-
-    /**
-     * Parses repository definition.
-     *
-     * @param repositoryDefinition repository definition
-     */
-    public PutRepositoryRequest source(Map repositoryDefinition) {
-        Map<String, Object> source = repositoryDefinition;
-        for (Map.Entry<String, Object> entry : source.entrySet()) {
+    public PutRepositoryRequest source(Map<String, Object> repositoryDefinition) {
+        for (Map.Entry<String, Object> entry : repositoryDefinition.entrySet()) {
             String name = entry.getKey();
             if (name.equals("type")) {
                 type(entry.getValue().toString());
@@ -217,64 +205,14 @@ public class PutRepositoryRequest extends AcknowledgedRequest<PutRepositoryReque
                 if (!(entry.getValue() instanceof Map)) {
                     throw new IllegalArgumentException("Malformed settings section, should include an inner object");
                 }
-                settings((Map<String, Object>) entry.getValue());
+                @SuppressWarnings("unchecked")
+                Map<String, Object> sub = (Map<String, Object>) entry.getValue();
+                settings(sub);
             }
         }
         return this;
     }
 
-    /**
-     * Parses repository definition.
-     * JSON, Smile and YAML formats are supported
-     *
-     * @param repositoryDefinition repository definition
-     */
-    public PutRepositoryRequest source(String repositoryDefinition) {
-        try (XContentParser parser = XContentFactory.xContent(repositoryDefinition).createParser(repositoryDefinition)) {
-            return source(parser.mapOrdered());
-        } catch (IOException e) {
-            throw new IllegalArgumentException("failed to parse repository source [" + repositoryDefinition + "]", e);
-        }
-    }
-
-    /**
-     * Parses repository definition.
-     * JSON, Smile and YAML formats are supported
-     *
-     * @param repositoryDefinition repository definition
-     */
-    public PutRepositoryRequest source(byte[] repositoryDefinition) {
-        return source(repositoryDefinition, 0, repositoryDefinition.length);
-    }
-
-    /**
-     * Parses repository definition.
-     * JSON, Smile and YAML formats are supported
-     *
-     * @param repositoryDefinition repository definition
-     */
-    public PutRepositoryRequest source(byte[] repositoryDefinition, int offset, int length) {
-        try (XContentParser parser = XContentFactory.xContent(repositoryDefinition, offset, length).createParser(repositoryDefinition, offset, length)) {
-            return source(parser.mapOrdered());
-        } catch (IOException e) {
-            throw new IllegalArgumentException("failed to parse repository source", e);
-        }
-    }
-
-    /**
-     * Parses repository definition.
-     * JSON, Smile and YAML formats are supported
-     *
-     * @param repositoryDefinition repository definition
-     */
-    public PutRepositoryRequest source(BytesReference repositoryDefinition) {
-        try (XContentParser parser = XContentFactory.xContent(repositoryDefinition).createParser(repositoryDefinition)) {
-            return source(parser.mapOrdered());
-        } catch (IOException e) {
-            throw new IllegalArgumentException("failed to parse template source", e);
-        }
-    }
-
     @Override
     public void readFrom(StreamInput in) throws IOException {
         super.readFrom(in);

+ 2 - 75
core/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/create/CreateSnapshotRequest.java

@@ -25,13 +25,11 @@ import org.elasticsearch.action.IndicesRequest;
 import org.elasticsearch.action.support.IndicesOptions;
 import org.elasticsearch.action.support.master.MasterNodeRequest;
 import org.elasticsearch.common.Strings;
-import org.elasticsearch.common.bytes.BytesReference;
 import org.elasticsearch.common.io.stream.StreamInput;
 import org.elasticsearch.common.io.stream.StreamOutput;
 import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.common.xcontent.XContentBuilder;
 import org.elasticsearch.common.xcontent.XContentFactory;
-import org.elasticsearch.common.xcontent.XContentParser;
 import org.elasticsearch.common.xcontent.XContentType;
 
 import java.io.IOException;
@@ -41,10 +39,9 @@ import java.util.Map;
 
 import static org.elasticsearch.action.ValidateActions.addValidationError;
 import static org.elasticsearch.common.Strings.EMPTY_ARRAY;
-import static org.elasticsearch.common.Strings.hasLength;
-import static org.elasticsearch.common.settings.Settings.Builder.EMPTY_SETTINGS;
 import static org.elasticsearch.common.settings.Settings.readSettingsFromStream;
 import static org.elasticsearch.common.settings.Settings.writeSettingsToStream;
+import static org.elasticsearch.common.settings.Settings.Builder.EMPTY_SETTINGS;
 import static org.elasticsearch.common.xcontent.support.XContentMapValues.lenientNodeBooleanValue;
 
 /**
@@ -357,17 +354,7 @@ public class CreateSnapshotRequest extends MasterNodeRequest<CreateSnapshotReque
      * @param source snapshot definition
      * @return this request
      */
-    public CreateSnapshotRequest source(XContentBuilder source) {
-        return source(source.bytes());
-    }
-
-    /**
-     * Parses snapshot definition.
-     *
-     * @param source snapshot definition
-     * @return this request
-     */
-    public CreateSnapshotRequest source(Map source) {
+    public CreateSnapshotRequest source(Map<String, Object> source) {
         for (Map.Entry<String, Object> entry : ((Map<String, Object>) source).entrySet()) {
             String name = entry.getKey();
             if (name.equals("indices")) {
@@ -393,66 +380,6 @@ public class CreateSnapshotRequest extends MasterNodeRequest<CreateSnapshotReque
         return this;
     }
 
-    /**
-     * Parses snapshot definition. JSON, YAML and properties formats are supported
-     *
-     * @param source snapshot definition
-     * @return this request
-     */
-    public CreateSnapshotRequest source(String source) {
-        if (hasLength(source)) {
-            try (XContentParser parser = XContentFactory.xContent(source).createParser(source)) {
-                return source(parser.mapOrdered());
-            } catch (Exception e) {
-                throw new IllegalArgumentException("failed to parse repository source [" + source + "]", e);
-            }
-        }
-        return this;
-    }
-
-    /**
-     * Parses snapshot definition. JSON, YAML and properties formats are supported
-     *
-     * @param source snapshot definition
-     * @return this request
-     */
-    public CreateSnapshotRequest source(byte[] source) {
-        return source(source, 0, source.length);
-    }
-
-    /**
-     * Parses snapshot definition. JSON, YAML and properties formats are supported
-     *
-     * @param source snapshot definition
-     * @param offset offset
-     * @param length length
-     * @return this request
-     */
-    public CreateSnapshotRequest source(byte[] source, int offset, int length) {
-        if (length > 0) {
-            try (XContentParser parser = XContentFactory.xContent(source, offset, length).createParser(source, offset, length)) {
-                return source(parser.mapOrdered());
-            } catch (IOException e) {
-                throw new IllegalArgumentException("failed to parse repository source", e);
-            }
-        }
-        return this;
-    }
-
-    /**
-     * Parses snapshot definition. JSON, YAML and properties formats are supported
-     *
-     * @param source snapshot definition
-     * @return this request
-     */
-    public CreateSnapshotRequest source(BytesReference source) {
-        try (XContentParser parser = XContentFactory.xContent(source).createParser(source)) {
-            return source(parser.mapOrdered());
-        } catch (IOException e) {
-            throw new IllegalArgumentException("failed to parse snapshot source", e);
-        }
-    }
-
     @Override
     public void readFrom(StreamInput in) throws IOException {
         super.readFrom(in);

+ 3 - 88
core/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/restore/RestoreSnapshotRequest.java

@@ -24,13 +24,11 @@ import org.elasticsearch.action.ActionRequestValidationException;
 import org.elasticsearch.action.support.IndicesOptions;
 import org.elasticsearch.action.support.master.MasterNodeRequest;
 import org.elasticsearch.common.Strings;
-import org.elasticsearch.common.bytes.BytesReference;
 import org.elasticsearch.common.io.stream.StreamInput;
 import org.elasticsearch.common.io.stream.StreamOutput;
 import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.common.xcontent.XContentBuilder;
 import org.elasticsearch.common.xcontent.XContentFactory;
-import org.elasticsearch.common.xcontent.XContentParser;
 import org.elasticsearch.common.xcontent.XContentType;
 
 import java.io.IOException;
@@ -39,10 +37,9 @@ import java.util.List;
 import java.util.Map;
 
 import static org.elasticsearch.action.ValidateActions.addValidationError;
-import static org.elasticsearch.common.Strings.hasLength;
-import static org.elasticsearch.common.settings.Settings.Builder.EMPTY_SETTINGS;
 import static org.elasticsearch.common.settings.Settings.readSettingsFromStream;
 import static org.elasticsearch.common.settings.Settings.writeSettingsToStream;
+import static org.elasticsearch.common.settings.Settings.Builder.EMPTY_SETTINGS;
 import static org.elasticsearch.common.xcontent.support.XContentMapValues.lenientNodeBooleanValue;
 
 /**
@@ -472,22 +469,8 @@ public class RestoreSnapshotRequest extends MasterNodeRequest<RestoreSnapshotReq
      * @param source restore definition
      * @return this request
      */
-    public RestoreSnapshotRequest source(XContentBuilder source) {
-        try {
-            return source(source.bytes());
-        } catch (Exception e) {
-            throw new IllegalArgumentException("Failed to build json for repository request", e);
-        }
-    }
-
-    /**
-     * Parses restore definition
-     *
-     * @param source restore definition
-     * @return this request
-     */
-    public RestoreSnapshotRequest source(Map source) {
-        for (Map.Entry<String, Object> entry : ((Map<String, Object>) source).entrySet()) {
+    public RestoreSnapshotRequest source(Map<String, Object> source) {
+        for (Map.Entry<String, Object> entry : source.entrySet()) {
             String name = entry.getKey();
             if (name.equals("indices")) {
                 if (entry.getValue() instanceof String) {
@@ -543,74 +526,6 @@ public class RestoreSnapshotRequest extends MasterNodeRequest<RestoreSnapshotReq
         return this;
     }
 
-    /**
-     * Parses restore definition
-     * <p>
-     * JSON, YAML and properties formats are supported
-     *
-     * @param source restore definition
-     * @return this request
-     */
-    public RestoreSnapshotRequest source(String source) {
-        if (hasLength(source)) {
-            try (XContentParser parser = XContentFactory.xContent(source).createParser(source)) {
-                return source(parser.mapOrdered());
-            } catch (Exception e) {
-                throw new IllegalArgumentException("failed to parse repository source [" + source + "]", e);
-            }
-        }
-        return this;
-    }
-
-    /**
-     * Parses restore definition
-     * <p>
-     * JSON, YAML and properties formats are supported
-     *
-     * @param source restore definition
-     * @return this request
-     */
-    public RestoreSnapshotRequest source(byte[] source) {
-        return source(source, 0, source.length);
-    }
-
-    /**
-     * Parses restore definition
-     * <p>
-     * JSON, YAML and properties formats are supported
-     *
-     * @param source restore definition
-     * @param offset offset
-     * @param length length
-     * @return this request
-     */
-    public RestoreSnapshotRequest source(byte[] source, int offset, int length) {
-        if (length > 0) {
-            try (XContentParser parser = XContentFactory.xContent(source, offset, length).createParser(source, offset, length)) {
-                return source(parser.mapOrdered());
-            } catch (IOException e) {
-                throw new IllegalArgumentException("failed to parse repository source", e);
-            }
-        }
-        return this;
-    }
-
-    /**
-     * Parses restore definition
-     * <p>
-     * JSON, YAML and properties formats are supported
-     *
-     * @param source restore definition
-     * @return this request
-     */
-    public RestoreSnapshotRequest source(BytesReference source) {
-        try (XContentParser parser = XContentFactory.xContent(source).createParser(source)) {
-            return source(parser.mapOrdered());
-        } catch (IOException e) {
-            throw new IllegalArgumentException("failed to parse template source", e);
-        }
-    }
-
     @Override
     public void readFrom(StreamInput in) throws IOException {
         super.readFrom(in);

+ 1 - 20
core/src/main/java/org/elasticsearch/action/admin/indices/rollover/RolloverRequest.java

@@ -18,7 +18,6 @@
  */
 package org.elasticsearch.action.admin.indices.rollover;
 
-import org.elasticsearch.ElasticsearchParseException;
 import org.elasticsearch.action.ActionRequestValidationException;
 import org.elasticsearch.action.IndicesRequest;
 import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
@@ -26,16 +25,11 @@ import org.elasticsearch.action.support.ActiveShardCount;
 import org.elasticsearch.action.support.IndicesOptions;
 import org.elasticsearch.action.support.master.AcknowledgedRequest;
 import org.elasticsearch.common.ParseField;
-import org.elasticsearch.common.ParseFieldMatcher;
 import org.elasticsearch.common.ParseFieldMatcherSupplier;
-import org.elasticsearch.common.bytes.BytesReference;
 import org.elasticsearch.common.io.stream.StreamInput;
 import org.elasticsearch.common.io.stream.StreamOutput;
 import org.elasticsearch.common.unit.TimeValue;
 import org.elasticsearch.common.xcontent.ObjectParser;
-import org.elasticsearch.common.xcontent.XContentFactory;
-import org.elasticsearch.common.xcontent.XContentParser;
-import org.elasticsearch.common.xcontent.XContentType;
 
 import java.io.IOException;
 import java.util.HashSet;
@@ -50,7 +44,7 @@ import static org.elasticsearch.action.ValidateActions.addValidationError;
  */
 public class RolloverRequest extends AcknowledgedRequest<RolloverRequest> implements IndicesRequest {
 
-    public static ObjectParser<RolloverRequest, ParseFieldMatcherSupplier> PARSER =
+    public static final ObjectParser<RolloverRequest, ParseFieldMatcherSupplier> PARSER =
         new ObjectParser<>("conditions", null);
     static {
         PARSER.declareField((parser, request, parseFieldMatcherSupplier) ->
@@ -194,19 +188,6 @@ public class RolloverRequest extends AcknowledgedRequest<RolloverRequest> implem
         return createIndexRequest;
     }
 
-    public void source(BytesReference source) {
-        XContentType xContentType = XContentFactory.xContentType(source);
-        if (xContentType != null) {
-            try (XContentParser parser = XContentFactory.xContent(xContentType).createParser(source)) {
-                PARSER.parse(parser, this, () -> ParseFieldMatcher.EMPTY);
-            } catch (IOException e) {
-                throw new ElasticsearchParseException("failed to parse source for rollover index", e);
-            }
-        } else {
-            throw new ElasticsearchParseException("failed to parse content type for rollover index source");
-        }
-    }
-
     /**
      * Sets the number of shard copies that should be active for creation of the
      * new rollover index to return. Defaults to {@link ActiveShardCount#DEFAULT}, which will

+ 1 - 20
core/src/main/java/org/elasticsearch/action/admin/indices/shrink/ShrinkRequest.java

@@ -18,7 +18,6 @@
  */
 package org.elasticsearch.action.admin.indices.shrink;
 
-import org.elasticsearch.ElasticsearchParseException;
 import org.elasticsearch.action.ActionRequestValidationException;
 import org.elasticsearch.action.IndicesRequest;
 import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
@@ -26,15 +25,10 @@ import org.elasticsearch.action.support.ActiveShardCount;
 import org.elasticsearch.action.support.IndicesOptions;
 import org.elasticsearch.action.support.master.AcknowledgedRequest;
 import org.elasticsearch.common.ParseField;
-import org.elasticsearch.common.ParseFieldMatcher;
 import org.elasticsearch.common.ParseFieldMatcherSupplier;
-import org.elasticsearch.common.bytes.BytesReference;
 import org.elasticsearch.common.io.stream.StreamInput;
 import org.elasticsearch.common.io.stream.StreamOutput;
 import org.elasticsearch.common.xcontent.ObjectParser;
-import org.elasticsearch.common.xcontent.XContentFactory;
-import org.elasticsearch.common.xcontent.XContentParser;
-import org.elasticsearch.common.xcontent.XContentType;
 
 import java.io.IOException;
 import java.util.Objects;
@@ -46,7 +40,7 @@ import static org.elasticsearch.action.ValidateActions.addValidationError;
  */
 public class ShrinkRequest extends AcknowledgedRequest<ShrinkRequest> implements IndicesRequest {
 
-    public static ObjectParser<ShrinkRequest, ParseFieldMatcherSupplier> PARSER =
+    public static final ObjectParser<ShrinkRequest, ParseFieldMatcherSupplier> PARSER =
         new ObjectParser<>("shrink_request", null);
     static {
         PARSER.declareField((parser, request, parseFieldMatcherSupplier) ->
@@ -152,17 +146,4 @@ public class ShrinkRequest extends AcknowledgedRequest<ShrinkRequest> implements
     public void setWaitForActiveShards(final int waitForActiveShards) {
         setWaitForActiveShards(ActiveShardCount.from(waitForActiveShards));
     }
-
-    public void source(BytesReference source) {
-        XContentType xContentType = XContentFactory.xContentType(source);
-        if (xContentType != null) {
-            try (XContentParser parser = XContentFactory.xContent(xContentType).createParser(source)) {
-                PARSER.parse(parser, this, () -> ParseFieldMatcher.EMPTY);
-            } catch (IOException e) {
-                throw new ElasticsearchParseException("failed to parse source for shrink index", e);
-            }
-        } else {
-            throw new ElasticsearchParseException("failed to parse content type for shrink index source");
-        }
-    }
 }

+ 4 - 3
core/src/main/java/org/elasticsearch/action/bulk/BulkRequest.java

@@ -23,7 +23,6 @@ import org.elasticsearch.action.ActionRequest;
 import org.elasticsearch.action.ActionRequestValidationException;
 import org.elasticsearch.action.CompositeIndicesRequest;
 import org.elasticsearch.action.DocWriteRequest;
-import org.elasticsearch.action.IndicesRequest;
 import org.elasticsearch.action.delete.DeleteRequest;
 import org.elasticsearch.action.index.IndexRequest;
 import org.elasticsearch.action.support.ActiveShardCount;
@@ -400,8 +399,10 @@ public class BulkRequest extends ActionRequest implements CompositeIndicesReques
                         UpdateRequest updateRequest = new UpdateRequest(index, type, id).routing(routing).parent(parent).retryOnConflict(retryOnConflict)
                                 .version(version).versionType(versionType)
                                 .routing(routing)
-                                .parent(parent)
-                                .fromXContent(data.slice(from, nextMarker - from));
+                                .parent(parent);
+                        try (XContentParser sliceParser = xContent.createParser(data.slice(from, nextMarker - from))) {
+                            updateRequest.fromXContent(sliceParser);
+                        }
                         if (fetchSourceContext != null) {
                             updateRequest.fetchSource(fetchSourceContext);
                         }

+ 39 - 57
core/src/main/java/org/elasticsearch/action/update/UpdateRequest.java

@@ -28,8 +28,6 @@ import org.elasticsearch.action.support.replication.ReplicationRequest;
 import org.elasticsearch.action.support.single.instance.InstanceShardOperationRequest;
 import org.elasticsearch.common.Nullable;
 import org.elasticsearch.common.ParseFieldMatcher;
-import org.elasticsearch.common.bytes.BytesArray;
-import org.elasticsearch.common.bytes.BytesReference;
 import org.elasticsearch.common.io.stream.StreamInput;
 import org.elasticsearch.common.io.stream.StreamOutput;
 import org.elasticsearch.common.logging.DeprecationLogger;
@@ -689,18 +687,6 @@ public class UpdateRequest extends InstanceShardOperationRequest<UpdateRequest>
         return upsertRequest;
     }
 
-    public UpdateRequest fromXContent(XContentBuilder source) throws Exception {
-        return fromXContent(source.bytes());
-    }
-
-    public UpdateRequest fromXContent(byte[] source) throws Exception {
-        return fromXContent(source, 0, source.length);
-    }
-
-    public UpdateRequest fromXContent(byte[] source, int offset, int length) throws Exception {
-        return fromXContent(new BytesArray(source, offset, length));
-    }
-
     /**
      * Should this update attempt to detect if it is a noop? Defaults to true.
      * @return this for chaining
@@ -717,52 +703,48 @@ public class UpdateRequest extends InstanceShardOperationRequest<UpdateRequest>
         return detectNoop;
     }
 
-    public UpdateRequest fromXContent(BytesReference source) throws IOException {
+    public UpdateRequest fromXContent(XContentParser parser) throws IOException {
         Script script = null;
-        try (XContentParser parser = XContentFactory.xContent(source).createParser(source)) {
-            XContentParser.Token token = parser.nextToken();
-            if (token == null) {
-                return this;
-            }
-            String currentFieldName = null;
-            while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
-                if (token == XContentParser.Token.FIELD_NAME) {
-                    currentFieldName = parser.currentName();
-                } else if ("script".equals(currentFieldName)) {
-                    script = Script.parse(parser, ParseFieldMatcher.EMPTY);
-                } else if ("scripted_upsert".equals(currentFieldName)) {
-                    scriptedUpsert = parser.booleanValue();
-                } else if ("upsert".equals(currentFieldName)) {
-                    XContentType xContentType = XContentFactory.xContentType(source);
-                    XContentBuilder builder = XContentFactory.contentBuilder(xContentType);
-                    builder.copyCurrentStructure(parser);
-                    safeUpsertRequest().source(builder);
-                } else if ("doc".equals(currentFieldName)) {
-                    XContentType xContentType = XContentFactory.xContentType(source);
-                    XContentBuilder docBuilder = XContentFactory.contentBuilder(xContentType);
-                    docBuilder.copyCurrentStructure(parser);
-                    safeDoc().source(docBuilder);
-                } else if ("doc_as_upsert".equals(currentFieldName)) {
-                    docAsUpsert(parser.booleanValue());
-                } else if ("detect_noop".equals(currentFieldName)) {
-                    detectNoop(parser.booleanValue());
-                } else if ("fields".equals(currentFieldName)) {
-                    List<Object> fields = null;
-                    if (token == XContentParser.Token.START_ARRAY) {
-                        fields = (List) parser.list();
-                    } else if (token.isValue()) {
-                        fields = Collections.singletonList(parser.text());
-                    }
-                    if (fields != null) {
-                        fields(fields.toArray(new String[fields.size()]));
-                    }
-                } else if ("_source".equals(currentFieldName)) {
-                    fetchSourceContext = FetchSourceContext.parse(parser);
+        XContentParser.Token token = parser.nextToken();
+        if (token == null) {
+            return this;
+        }
+        String currentFieldName = null;
+        while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
+            if (token == XContentParser.Token.FIELD_NAME) {
+                currentFieldName = parser.currentName();
+            } else if ("script".equals(currentFieldName)) {
+                script = Script.parse(parser, ParseFieldMatcher.EMPTY);
+            } else if ("scripted_upsert".equals(currentFieldName)) {
+                scriptedUpsert = parser.booleanValue();
+            } else if ("upsert".equals(currentFieldName)) {
+                XContentBuilder builder = XContentFactory.contentBuilder(parser.contentType());
+                builder.copyCurrentStructure(parser);
+                safeUpsertRequest().source(builder);
+            } else if ("doc".equals(currentFieldName)) {
+                XContentBuilder docBuilder = XContentFactory.contentBuilder(parser.contentType());
+                docBuilder.copyCurrentStructure(parser);
+                safeDoc().source(docBuilder);
+            } else if ("doc_as_upsert".equals(currentFieldName)) {
+                docAsUpsert(parser.booleanValue());
+            } else if ("detect_noop".equals(currentFieldName)) {
+                detectNoop(parser.booleanValue());
+            } else if ("fields".equals(currentFieldName)) {
+                List<Object> fields = null;
+                if (token == XContentParser.Token.START_ARRAY) {
+                    fields = (List) parser.list();
+                } else if (token.isValue()) {
+                    fields = Collections.singletonList(parser.text());
                 }
+                if (fields != null) {
+                    fields(fields.toArray(new String[fields.size()]));
+                }
+            } else if ("_source".equals(currentFieldName)) {
+                fetchSourceContext = FetchSourceContext.parse(parser);
             }
-            if (script != null) {
-                this.script = script;
-            }
+        }
+        if (script != null) {
+            this.script = script;
         }
         return this;
     }

+ 29 - 9
core/src/main/java/org/elasticsearch/rest/RestRequest.java

@@ -19,7 +19,6 @@
 
 package org.elasticsearch.rest;
 
-import org.apache.lucene.util.IOUtils;
 import org.elasticsearch.ElasticsearchParseException;
 import org.elasticsearch.common.Booleans;
 import org.elasticsearch.common.CheckedConsumer;
@@ -229,6 +228,30 @@ public abstract class RestRequest implements ToXContent.Params {
         return params;
     }
 
+    /**
+     * A parser for the contents of this request if there is a body, otherwise throws an {@link ElasticsearchParseException}. Use
+     * {@link #applyContentParser(CheckedConsumer)} if you want to gracefully handle when the request doesn't have any contents. Use
+     * {@link #contentOrSourceParamParser()} for requests that support specifying the request body in the {@code source} param.
+     */
+    public final XContentParser contentParser() throws IOException {
+        BytesReference content = content();
+        if (content.length() == 0) {
+            throw new ElasticsearchParseException("Body required");
+        }
+        return XContentFactory.xContent(content).createParser(content);
+    }
+
+    /**
+     * If there is any content then call {@code applyParser} with the parser, otherwise do nothing.
+     */
+    public final void applyContentParser(CheckedConsumer<XContentParser, IOException> applyParser) throws IOException {
+        if (hasContent()) {
+            try (XContentParser parser = contentParser()) {
+                applyParser.accept(parser);
+            }
+        }
+    }
+
     /**
      * Does this request have content or a {@code source} parameter? Use this instead of {@link #hasContent()} if this
      * {@linkplain RestHandler} treats the {@code source} parameter like the body content.
@@ -256,16 +279,13 @@ public abstract class RestRequest implements ToXContent.Params {
      * back to the user when there isn't request content.
      */
     public final void withContentOrSourceParamParserOrNull(CheckedConsumer<XContentParser, IOException> withParser) throws IOException {
-        XContentParser parser = null;
         BytesReference content = contentOrSourceParam();
         if (content.length() > 0) {
-            parser = XContentFactory.xContent(content).createParser(content);
-        }
-
-        try {
-            withParser.accept(parser);
-        } finally {
-            IOUtils.close(parser);
+            try (XContentParser parser = XContentFactory.xContent(content).createParser(content)) {
+                withParser.accept(parser);
+            }
+        } else {
+            withParser.accept(null);
         }
     }
 

+ 1 - 5
core/src/main/java/org/elasticsearch/rest/action/admin/cluster/RestClusterRerouteAction.java

@@ -121,11 +121,7 @@ public class RestClusterRerouteAction extends BaseRestHandler {
         clusterRerouteRequest.timeout(request.paramAsTime("timeout", clusterRerouteRequest.timeout()));
         clusterRerouteRequest.setRetryFailed(request.paramAsBoolean("retry_failed", clusterRerouteRequest.isRetryFailed()));
         clusterRerouteRequest.masterNodeTimeout(request.paramAsTime("master_timeout", clusterRerouteRequest.masterNodeTimeout()));
-        if (request.hasContent()) {
-            try (XContentParser parser = XContentHelper.createParser(request.content())) {
-                PARSER.parse(parser, clusterRerouteRequest, new ParseContext(registry, parseFieldMatcher));
-            }
-        }
+        request.applyContentParser(parser -> PARSER.parse(parser, clusterRerouteRequest, new ParseContext(registry, parseFieldMatcher)));
         return clusterRerouteRequest;
     }
 

+ 1 - 2
core/src/main/java/org/elasticsearch/rest/action/admin/cluster/RestClusterUpdateSettingsAction.java

@@ -26,7 +26,6 @@ import org.elasticsearch.client.node.NodeClient;
 import org.elasticsearch.common.inject.Inject;
 import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.common.xcontent.XContentBuilder;
-import org.elasticsearch.common.xcontent.XContentFactory;
 import org.elasticsearch.common.xcontent.XContentParser;
 import org.elasticsearch.rest.BaseRestHandler;
 import org.elasticsearch.rest.RestController;
@@ -52,7 +51,7 @@ public class RestClusterUpdateSettingsAction extends BaseRestHandler {
         clusterUpdateSettingsRequest.masterNodeTimeout(
                 request.paramAsTime("master_timeout", clusterUpdateSettingsRequest.masterNodeTimeout()));
         Map<String, Object> source;
-        try (XContentParser parser = XContentFactory.xContent(request.content()).createParser(request.content())) {
+        try (XContentParser parser = request.contentParser()) {
             source = parser.map();
         }
         if (source.containsKey("transient")) {

+ 1 - 1
core/src/main/java/org/elasticsearch/rest/action/admin/cluster/RestCreateSnapshotAction.java

@@ -49,7 +49,7 @@ public class RestCreateSnapshotAction extends BaseRestHandler {
     @Override
     public RestChannelConsumer prepareRequest(final RestRequest request, final NodeClient client) throws IOException {
         CreateSnapshotRequest createSnapshotRequest = createSnapshotRequest(request.param("repository"), request.param("snapshot"));
-        createSnapshotRequest.source(request.content().utf8ToString());
+        request.applyContentParser(p -> createSnapshotRequest.source(p.mapOrdered()));
         createSnapshotRequest.masterNodeTimeout(request.paramAsTime("master_timeout", createSnapshotRequest.masterNodeTimeout()));
         createSnapshotRequest.waitForCompletion(request.paramAsBoolean("wait_for_completion", false));
         return channel -> client.admin().cluster().createSnapshot(createSnapshotRequest, new RestToXContentListener<>(channel));

+ 4 - 1
core/src/main/java/org/elasticsearch/rest/action/admin/cluster/RestPutRepositoryAction.java

@@ -23,6 +23,7 @@ import org.elasticsearch.action.admin.cluster.repositories.put.PutRepositoryRequ
 import org.elasticsearch.client.node.NodeClient;
 import org.elasticsearch.common.inject.Inject;
 import org.elasticsearch.common.settings.Settings;
+import org.elasticsearch.common.xcontent.XContentParser;
 import org.elasticsearch.rest.BaseRestHandler;
 import org.elasticsearch.rest.RestController;
 import org.elasticsearch.rest.RestRequest;
@@ -50,7 +51,9 @@ public class RestPutRepositoryAction extends BaseRestHandler {
     @Override
     public RestChannelConsumer prepareRequest(final RestRequest request, final NodeClient client) throws IOException {
         PutRepositoryRequest putRepositoryRequest = putRepositoryRequest(request.param("repository"));
-        putRepositoryRequest.source(request.content().utf8ToString());
+        try (XContentParser parser = request.contentParser()) {
+            putRepositoryRequest.source(parser.mapOrdered());
+        }
         putRepositoryRequest.verify(request.paramAsBoolean("verify", true));
         putRepositoryRequest.masterNodeTimeout(request.paramAsTime("master_timeout", putRepositoryRequest.masterNodeTimeout()));
         putRepositoryRequest.timeout(request.paramAsTime("timeout", putRepositoryRequest.timeout()));

+ 1 - 1
core/src/main/java/org/elasticsearch/rest/action/admin/cluster/RestRestoreSnapshotAction.java

@@ -49,7 +49,7 @@ public class RestRestoreSnapshotAction extends BaseRestHandler {
         RestoreSnapshotRequest restoreSnapshotRequest = restoreSnapshotRequest(request.param("repository"), request.param("snapshot"));
         restoreSnapshotRequest.masterNodeTimeout(request.paramAsTime("master_timeout", restoreSnapshotRequest.masterNodeTimeout()));
         restoreSnapshotRequest.waitForCompletion(request.paramAsBoolean("wait_for_completion", false));
-        restoreSnapshotRequest.source(request.content().utf8ToString());
+        request.applyContentParser(p -> restoreSnapshotRequest.source(p.mapOrdered()));
         return channel -> client.admin().cluster().restoreSnapshot(restoreSnapshotRequest, new RestToXContentListener<>(channel));
     }
 }

+ 0 - 1
core/src/main/java/org/elasticsearch/rest/action/admin/indices/RestCreateIndexAction.java

@@ -41,7 +41,6 @@ public class RestCreateIndexAction extends BaseRestHandler {
         controller.registerHandler(RestRequest.Method.PUT, "/{index}", this);
     }
 
-    @SuppressWarnings({"unchecked"})
     @Override
     public RestChannelConsumer prepareRequest(final RestRequest request, final NodeClient client) throws IOException {
         CreateIndexRequest createIndexRequest = new CreateIndexRequest(request.param("index"));

+ 1 - 1
core/src/main/java/org/elasticsearch/rest/action/admin/indices/RestIndexPutAliasAction.java

@@ -67,7 +67,7 @@ public class RestIndexPutAliasAction extends BaseRestHandler {
         String searchRouting = null;
 
         if (request.hasContent()) {
-            try (XContentParser parser = XContentFactory.xContent(request.content()).createParser(request.content())) {
+            try (XContentParser parser = request.contentParser()) {
                 XContentParser.Token token = parser.nextToken();
                 if (token == null) {
                     throw new IllegalArgumentException("No index alias is specified");

+ 1 - 3
core/src/main/java/org/elasticsearch/rest/action/admin/indices/RestIndicesAliasesAction.java

@@ -21,7 +21,6 @@ package org.elasticsearch.rest.action.admin.indices;
 
 import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
 import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest.AliasActions;
-import org.elasticsearch.action.admin.indices.alias.IndicesAliasesResponse;
 import org.elasticsearch.client.node.NodeClient;
 import org.elasticsearch.common.ParseField;
 import org.elasticsearch.common.ParseFieldMatcher;
@@ -29,7 +28,6 @@ import org.elasticsearch.common.ParseFieldMatcherSupplier;
 import org.elasticsearch.common.inject.Inject;
 import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.common.xcontent.ObjectParser;
-import org.elasticsearch.common.xcontent.XContentFactory;
 import org.elasticsearch.common.xcontent.XContentParser;
 import org.elasticsearch.rest.BaseRestHandler;
 import org.elasticsearch.rest.RestController;
@@ -61,7 +59,7 @@ public class RestIndicesAliasesAction extends BaseRestHandler {
         IndicesAliasesRequest indicesAliasesRequest = new IndicesAliasesRequest();
         indicesAliasesRequest.masterNodeTimeout(request.paramAsTime("master_timeout", indicesAliasesRequest.masterNodeTimeout()));
         indicesAliasesRequest.timeout(request.paramAsTime("timeout", indicesAliasesRequest.timeout()));
-        try (XContentParser parser = XContentFactory.xContent(request.content()).createParser(request.content())) {
+        try (XContentParser parser = request.contentParser()) {
             PARSER.parse(parser, indicesAliasesRequest, () -> ParseFieldMatcher.STRICT);
         }
         if (indicesAliasesRequest.getAliasActions().isEmpty()) {

+ 2 - 4
core/src/main/java/org/elasticsearch/rest/action/admin/indices/RestRolloverIndexAction.java

@@ -22,6 +22,7 @@ package org.elasticsearch.rest.action.admin.indices;
 import org.elasticsearch.action.admin.indices.rollover.RolloverRequest;
 import org.elasticsearch.action.support.ActiveShardCount;
 import org.elasticsearch.client.node.NodeClient;
+import org.elasticsearch.common.ParseFieldMatcher;
 import org.elasticsearch.common.inject.Inject;
 import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.rest.BaseRestHandler;
@@ -40,13 +41,10 @@ public class RestRolloverIndexAction extends BaseRestHandler {
         controller.registerHandler(RestRequest.Method.POST, "/{index}/_rollover/{new_index}", this);
     }
 
-    @SuppressWarnings({"unchecked"})
     @Override
     public RestChannelConsumer prepareRequest(final RestRequest request, final NodeClient client) throws IOException {
         RolloverRequest rolloverIndexRequest = new RolloverRequest(request.param("index"), request.param("new_index"));
-        if (request.hasContent()) {
-            rolloverIndexRequest.source(request.content());
-        }
+        request.applyContentParser(parser -> RolloverRequest.PARSER.parse(parser, rolloverIndexRequest, () -> ParseFieldMatcher.EMPTY));
         rolloverIndexRequest.dryRun(request.paramAsBoolean("dry_run", false));
         rolloverIndexRequest.timeout(request.paramAsTime("timeout", rolloverIndexRequest.timeout()));
         rolloverIndexRequest.masterNodeTimeout(request.paramAsTime("master_timeout", rolloverIndexRequest.masterNodeTimeout()));

+ 2 - 4
core/src/main/java/org/elasticsearch/rest/action/admin/indices/RestShrinkIndexAction.java

@@ -23,6 +23,7 @@ import org.elasticsearch.action.admin.indices.shrink.ShrinkRequest;
 import org.elasticsearch.action.admin.indices.shrink.ShrinkResponse;
 import org.elasticsearch.action.support.ActiveShardCount;
 import org.elasticsearch.client.node.NodeClient;
+import org.elasticsearch.common.ParseFieldMatcher;
 import org.elasticsearch.common.inject.Inject;
 import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.common.xcontent.XContentBuilder;
@@ -42,7 +43,6 @@ public class RestShrinkIndexAction extends BaseRestHandler {
         controller.registerHandler(RestRequest.Method.POST, "/{index}/_shrink/{target}", this);
     }
 
-    @SuppressWarnings({"unchecked"})
     @Override
     public RestChannelConsumer prepareRequest(final RestRequest request, final NodeClient client) throws IOException {
         if (request.param("target") == null) {
@@ -52,9 +52,7 @@ public class RestShrinkIndexAction extends BaseRestHandler {
             throw new IllegalArgumentException("no source index");
         }
         ShrinkRequest shrinkIndexRequest = new ShrinkRequest(request.param("target"), request.param("index"));
-        if (request.hasContent()) {
-            shrinkIndexRequest.source(request.content());
-        }
+        request.applyContentParser(parser -> ShrinkRequest.PARSER.parse(parser, shrinkIndexRequest, () -> ParseFieldMatcher.EMPTY));
         shrinkIndexRequest.timeout(request.paramAsTime("timeout", shrinkIndexRequest.timeout()));
         shrinkIndexRequest.masterNodeTimeout(request.paramAsTime("master_timeout", shrinkIndexRequest.masterNodeTimeout()));
         shrinkIndexRequest.setWaitForActiveShards(ActiveShardCount.parseString(request.param("wait_for_active_shards")));

+ 3 - 4
core/src/main/java/org/elasticsearch/rest/action/document/RestUpdateAction.java

@@ -80,9 +80,8 @@ public class RestUpdateAction extends BaseRestHandler {
         updateRequest.versionType(VersionType.fromString(request.param("version_type"), updateRequest.versionType()));
 
 
-        // see if we have it in the body
-        if (request.hasContent()) {
-            updateRequest.fromXContent(request.content());
+        request.applyContentParser(parser -> {
+            updateRequest.fromXContent(parser);
             IndexRequest upsertRequest = updateRequest.upsertRequest();
             if (upsertRequest != null) {
                 upsertRequest.routing(request.param("routing"));
@@ -97,7 +96,7 @@ public class RestUpdateAction extends BaseRestHandler {
                 doc.version(RestActions.parseVersion(request));
                 doc.versionType(VersionType.fromString(request.param("version_type"), doc.versionType()));
             }
-        }
+        });
 
         return channel ->
             client.update(updateRequest, new RestStatusToXContentListener<>(channel, r -> r.getLocation(updateRequest.routing())));

+ 4 - 2
core/src/test/java/org/elasticsearch/action/admin/indices/rollover/RolloverRequestTests.java

@@ -19,9 +19,11 @@
 
 package org.elasticsearch.action.admin.indices.rollover;
 
+import org.elasticsearch.common.ParseFieldMatcher;
 import org.elasticsearch.common.unit.TimeValue;
 import org.elasticsearch.common.xcontent.XContentBuilder;
 import org.elasticsearch.common.xcontent.XContentFactory;
+import org.elasticsearch.common.xcontent.XContentHelper;
 import org.elasticsearch.test.ESTestCase;
 
 import java.util.Set;
@@ -39,7 +41,7 @@ public class RolloverRequestTests extends ESTestCase {
                 .field("max_docs", 100)
             .endObject()
             .endObject();
-        request.source(builder.bytes());
+        RolloverRequest.PARSER.parse(XContentHelper.createParser(builder.bytes()), request, () -> ParseFieldMatcher.EMPTY);
         Set<Condition> conditions = request.getConditions();
         assertThat(conditions.size(), equalTo(2));
         for (Condition condition : conditions) {
@@ -80,7 +82,7 @@ public class RolloverRequestTests extends ESTestCase {
                 .startObject("alias1").endObject()
             .endObject()
             .endObject();
-        request.source(builder.bytes());
+        RolloverRequest.PARSER.parse(XContentHelper.createParser(builder.bytes()), request, () -> ParseFieldMatcher.EMPTY);
         Set<Condition> conditions = request.getConditions();
         assertThat(conditions.size(), equalTo(2));
         assertThat(request.getCreateIndexRequest().mappings().size(), equalTo(1));

+ 61 - 53
core/src/test/java/org/elasticsearch/action/update/UpdateRequestTests.java

@@ -19,13 +19,11 @@
 
 package org.elasticsearch.action.update;
 
-import org.elasticsearch.ElasticsearchParseException;
 import org.elasticsearch.Version;
 import org.elasticsearch.action.index.IndexRequest;
 import org.elasticsearch.common.bytes.BytesArray;
 import org.elasticsearch.common.io.stream.Streamable;
 import org.elasticsearch.common.settings.Settings;
-import org.elasticsearch.common.unit.TimeValue;
 import org.elasticsearch.common.xcontent.XContentFactory;
 import org.elasticsearch.common.xcontent.XContentHelper;
 import org.elasticsearch.env.Environment;
@@ -36,8 +34,8 @@ import org.elasticsearch.script.Script;
 import org.elasticsearch.script.ScriptContextRegistry;
 import org.elasticsearch.script.ScriptEngineRegistry;
 import org.elasticsearch.script.ScriptService;
-import org.elasticsearch.script.ScriptType;
 import org.elasticsearch.script.ScriptSettings;
+import org.elasticsearch.script.ScriptType;
 import org.elasticsearch.test.ESTestCase;
 import org.elasticsearch.watcher.ResourceWatcherService;
 
@@ -52,17 +50,16 @@ import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
 import static org.hamcrest.Matchers.arrayContaining;
 import static org.hamcrest.Matchers.equalTo;
 import static org.hamcrest.Matchers.instanceOf;
-import static org.hamcrest.Matchers.is;
 import static org.hamcrest.Matchers.notNullValue;
-import static org.hamcrest.Matchers.nullValue;
 
 public class UpdateRequestTests extends ESTestCase {
     public void testUpdateRequest() throws Exception {
         UpdateRequest request = new UpdateRequest("test", "type", "1");
         // simple script
-        request.fromXContent(XContentFactory.jsonBuilder().startObject()
-                .field("script", "script1")
-                .endObject());
+        request.fromXContent(XContentHelper.createParser(XContentFactory.jsonBuilder()
+                .startObject()
+                    .field("script", "script1")
+                .endObject().bytes()));
         Script script = request.script();
         assertThat(script, notNullValue());
         assertThat(script.getIdOrCode(), equalTo("script1"));
@@ -72,9 +69,9 @@ public class UpdateRequestTests extends ESTestCase {
         assertThat(params, equalTo(Collections.emptyMap()));
 
         // simple verbose script
-        request.fromXContent(XContentFactory.jsonBuilder().startObject()
-                .startObject("script").field("inline", "script1").endObject()
-                .endObject());
+        request.fromXContent(XContentHelper.createParser(XContentFactory.jsonBuilder().startObject()
+                    .startObject("script").field("inline", "script1").endObject()
+                .endObject().bytes()));
         script = request.script();
         assertThat(script, notNullValue());
         assertThat(script.getIdOrCode(), equalTo("script1"));
@@ -85,13 +82,13 @@ public class UpdateRequestTests extends ESTestCase {
 
         // script with params
         request = new UpdateRequest("test", "type", "1");
-        request.fromXContent(XContentFactory.jsonBuilder().startObject()
+        request.fromXContent(XContentHelper.createParser(XContentFactory.jsonBuilder().startObject()
             .startObject("script")
                 .field("inline", "script1")
                 .startObject("params")
                     .field("param1", "value1")
                 .endObject()
-            .endObject().endObject());
+            .endObject().endObject().bytes()));
         script = request.script();
         assertThat(script, notNullValue());
         assertThat(script.getIdOrCode(), equalTo("script1"));
@@ -103,9 +100,15 @@ public class UpdateRequestTests extends ESTestCase {
         assertThat(params.get("param1").toString(), equalTo("value1"));
 
         request = new UpdateRequest("test", "type", "1");
-        request.fromXContent(XContentFactory.jsonBuilder().startObject().startObject("script")
-            .startObject("params").field("param1", "value1").endObject()
-            .field("inline", "script1").endObject().endObject());
+        request.fromXContent(XContentHelper.createParser(XContentFactory.jsonBuilder()
+                .startObject()
+                    .startObject("script")
+                        .startObject("params")
+                            .field("param1", "value1")
+                        .endObject()
+                        .field("inline", "script1")
+                    .endObject()
+                .endObject().bytes()));
         script = request.script();
         assertThat(script, notNullValue());
         assertThat(script.getIdOrCode(), equalTo("script1"));
@@ -118,7 +121,7 @@ public class UpdateRequestTests extends ESTestCase {
 
         // script with params and upsert
         request = new UpdateRequest("test", "type", "1");
-        request.fromXContent(XContentFactory.jsonBuilder().startObject()
+        request.fromXContent(XContentHelper.createParser(XContentFactory.jsonBuilder().startObject()
             .startObject("script")
                 .startObject("params")
                     .field("param1", "value1")
@@ -130,7 +133,7 @@ public class UpdateRequestTests extends ESTestCase {
                 .startObject("compound")
                     .field("field2", "value2")
                 .endObject()
-            .endObject().endObject());
+            .endObject().endObject().bytes()));
         script = request.script();
         assertThat(script, notNullValue());
         assertThat(script.getIdOrCode(), equalTo("script1"));
@@ -145,7 +148,7 @@ public class UpdateRequestTests extends ESTestCase {
         assertThat(((Map) upsertDoc.get("compound")).get("field2").toString(), equalTo("value2"));
 
         request = new UpdateRequest("test", "type", "1");
-        request.fromXContent(XContentFactory.jsonBuilder().startObject()
+        request.fromXContent(XContentHelper.createParser(XContentFactory.jsonBuilder().startObject()
             .startObject("upsert")
                 .field("field1", "value1")
                 .startObject("compound")
@@ -157,7 +160,7 @@ public class UpdateRequestTests extends ESTestCase {
                     .field("param1", "value1")
                 .endObject()
                 .field("inline", "script1")
-            .endObject().endObject());
+            .endObject().endObject().bytes()));
         script = request.script();
         assertThat(script, notNullValue());
         assertThat(script.getIdOrCode(), equalTo("script1"));
@@ -173,69 +176,70 @@ public class UpdateRequestTests extends ESTestCase {
 
         // script with doc
         request = new UpdateRequest("test", "type", "1");
-        request.fromXContent(XContentFactory.jsonBuilder().startObject()
-            .startObject("doc").field("field1", "value1").startObject("compound")
-            .field("field2", "value2").endObject().endObject().endObject());
+        request.fromXContent(XContentHelper.createParser(XContentFactory.jsonBuilder()
+                .startObject()
+                    .startObject("doc")
+                        .field("field1", "value1")
+                        .startObject("compound")
+                            .field("field2", "value2")
+                        .endObject()
+                    .endObject()
+                .endObject().bytes()));
         Map<String, Object> doc = request.doc().sourceAsMap();
         assertThat(doc.get("field1").toString(), equalTo("value1"));
         assertThat(((Map) doc.get("compound")).get("field2").toString(), equalTo("value2"));
     }
 
-    // Related to issue #15822
-    public void testInvalidBodyThrowsParseException() throws Exception {
-        UpdateRequest request = new UpdateRequest("test", "type", "1");
-        Exception e = expectThrows(ElasticsearchParseException.class, () -> request.fromXContent(new byte[] { (byte) '"' }));
-        assertThat(e.getMessage(), equalTo("Failed to derive xcontent"));
-    }
-
     // Related to issue 15338
     public void testFieldsParsing() throws Exception {
         UpdateRequest request = new UpdateRequest("test", "type1", "1")
-                .fromXContent(new BytesArray("{\"doc\": {\"field1\": \"value1\"}, \"fields\": \"_source\"}"));
+                .fromXContent(XContentHelper.createParser(new BytesArray("{\"doc\": {\"field1\": \"value1\"}, \"fields\": \"_source\"}")));
         assertThat(request.doc().sourceAsMap().get("field1").toString(), equalTo("value1"));
         assertThat(request.fields(), arrayContaining("_source"));
 
-        request = new UpdateRequest("test", "type2", "2")
-                .fromXContent(new BytesArray("{\"doc\": {\"field2\": \"value2\"}, \"fields\": [\"field1\", \"field2\"]}"));
+        request = new UpdateRequest("test", "type2", "2").fromXContent(
+                XContentHelper.createParser(new BytesArray("{\"doc\": {\"field2\": \"value2\"}, \"fields\": [\"field1\", \"field2\"]}")));
         assertThat(request.doc().sourceAsMap().get("field2").toString(), equalTo("value2"));
         assertThat(request.fields(), arrayContaining("field1", "field2"));
     }
 
     public void testFetchSourceParsing() throws Exception {
         UpdateRequest request = new UpdateRequest("test", "type1", "1");
-        request.fromXContent(
-            XContentFactory.jsonBuilder().startObject().field("_source", true).endObject()
-        );
+        request.fromXContent(XContentHelper.createParser(XContentFactory.jsonBuilder()
+                .startObject()
+                    .field("_source", true)
+                .endObject().bytes()));
         assertThat(request.fetchSource(), notNullValue());
         assertThat(request.fetchSource().includes().length, equalTo(0));
         assertThat(request.fetchSource().excludes().length, equalTo(0));
         assertThat(request.fetchSource().fetchSource(), equalTo(true));
 
-        request.fromXContent(
-            XContentFactory.jsonBuilder().startObject().field("_source", false).endObject()
-        );
+        request.fromXContent(XContentHelper.createParser(XContentFactory.jsonBuilder()
+                .startObject()
+                    .field("_source", false)
+                .endObject().bytes()));
         assertThat(request.fetchSource(), notNullValue());
         assertThat(request.fetchSource().includes().length, equalTo(0));
         assertThat(request.fetchSource().excludes().length, equalTo(0));
         assertThat(request.fetchSource().fetchSource(), equalTo(false));
 
-        request.fromXContent(
-            XContentFactory.jsonBuilder().startObject().field("_source", "path.inner.*").endObject()
-        );
+        request.fromXContent(XContentHelper.createParser(XContentFactory.jsonBuilder()
+                .startObject()
+                    .field("_source", "path.inner.*")
+                .endObject().bytes()));
         assertThat(request.fetchSource(), notNullValue());
         assertThat(request.fetchSource().fetchSource(), equalTo(true));
         assertThat(request.fetchSource().includes().length, equalTo(1));
         assertThat(request.fetchSource().excludes().length, equalTo(0));
         assertThat(request.fetchSource().includes()[0], equalTo("path.inner.*"));
 
-        request.fromXContent(
-            XContentFactory.jsonBuilder().startObject()
-                .startObject("_source")
-                    .field("includes", "path.inner.*")
-                    .field("excludes", "another.inner.*")
-                .endObject()
-            .endObject()
-        );
+        request.fromXContent(XContentHelper.createParser(XContentFactory.jsonBuilder()
+                .startObject()
+                    .startObject("_source")
+                        .field("includes", "path.inner.*")
+                        .field("excludes", "another.inner.*")
+                    .endObject()
+                .endObject().bytes()));
         assertThat(request.fetchSource(), notNullValue());
         assertThat(request.fetchSource().fetchSource(), equalTo(true));
         assertThat(request.fetchSource().includes().length, equalTo(1));
@@ -254,13 +258,17 @@ public class UpdateRequestTests extends ESTestCase {
         Map<String, Function<Map<String, Object>, Object>> scripts =  new HashMap<>();
         scripts.put("ctx._source.update_timestamp = ctx._now",
             (vars) -> {
-                Map<String, Object> ctx = (Map) vars.get("ctx");
-                Map<String, Object> source = (Map) ctx.get("_source");
+                Map<String, Object> vars2 = vars;
+                @SuppressWarnings("unchecked")
+                Map<String, Object> ctx = (Map<String, Object>) vars2.get("ctx");
+                @SuppressWarnings("unchecked")
+                Map<String, Object> source = (Map<String, Object>) ctx.get("_source");
                 source.put("update_timestamp", ctx.get("_now"));
                 return null;});
         scripts.put("ctx._timestamp = ctx._now",
             (vars) -> {
-                Map<String, Object> ctx = (Map) vars.get("ctx");
+                @SuppressWarnings("unchecked")
+                Map<String, Object> ctx = (Map<String, Object>) vars.get("ctx");
                 ctx.put("_timestamp", ctx.get("_now"));
                 return null;});
         ScriptContextRegistry scriptContextRegistry = new ScriptContextRegistry(Collections.emptyList());

+ 28 - 9
core/src/test/java/org/elasticsearch/rest/RestRequestTests.java

@@ -28,11 +28,30 @@ import org.elasticsearch.test.ESTestCase;
 import java.io.IOException;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.concurrent.atomic.AtomicReference;
 
 import static java.util.Collections.emptyMap;
 import static java.util.Collections.singletonMap;
 
 public class RestRequestTests extends ESTestCase {
+    public void testContentParser() throws IOException {
+        Exception e = expectThrows(ElasticsearchParseException.class, () ->
+            new ContentRestRequest("", emptyMap()).contentParser());
+        assertEquals("Body required", e.getMessage());
+        e = expectThrows(ElasticsearchParseException.class, () ->
+            new ContentRestRequest("", singletonMap("source", "{}")).contentParser());
+        assertEquals("Body required", e.getMessage());
+        assertEquals(emptyMap(), new ContentRestRequest("{}", emptyMap()).contentParser().map());
+    }
+
+    public void testApplyContentParser() throws IOException {
+        new ContentRestRequest("", emptyMap()).applyContentParser(p -> fail("Shouldn't have been called"));
+        new ContentRestRequest("", singletonMap("source", "{}")).applyContentParser(p -> fail("Shouldn't have been called"));
+        AtomicReference<Object> source = new AtomicReference<>();
+        new ContentRestRequest("{}", emptyMap()).applyContentParser(p -> source.set(p.map()));
+        assertEquals(emptyMap(), source.get());
+    }
+
     public void testContentOrSourceParam() throws IOException {
         assertEquals(BytesArray.EMPTY, new ContentRestRequest("", emptyMap()).contentOrSourceParam());
         assertEquals(new BytesArray("stuff"), new ContentRestRequest("stuff", emptyMap()).contentOrSourceParam());
@@ -47,15 +66,6 @@ public class RestRequestTests extends ESTestCase {
         assertEquals(true, new ContentRestRequest("", singletonMap("source", "stuff")).hasContentOrSourceParam());
     }
 
-    public void testContentOrSourceParamParserOrNull() throws IOException {
-        new ContentRestRequest("", emptyMap()).withContentOrSourceParamParserOrNull(parser -> assertNull(parser));
-        new ContentRestRequest("{}", emptyMap()).withContentOrSourceParamParserOrNull(parser -> assertEquals(emptyMap(), parser.map()));
-        new ContentRestRequest("{}", singletonMap("source", "stuff2")).withContentOrSourceParamParserOrNull(parser ->
-                assertEquals(emptyMap(), parser.map()));
-        new ContentRestRequest("", singletonMap("source", "{}")).withContentOrSourceParamParserOrNull(parser ->
-                assertEquals(emptyMap(), parser.map()));
-    }
-
     public void testContentOrSourceParamParser() throws IOException {
         Exception e = expectThrows(ElasticsearchParseException.class, () ->
             new ContentRestRequest("", emptyMap()).contentOrSourceParamParser());
@@ -65,6 +75,15 @@ public class RestRequestTests extends ESTestCase {
         assertEquals(emptyMap(), new ContentRestRequest("", singletonMap("source", "{}")).contentOrSourceParamParser().map());
     }
 
+    public void testWithContentOrSourceParamParserOrNull() throws IOException {
+        new ContentRestRequest("", emptyMap()).withContentOrSourceParamParserOrNull(parser -> assertNull(parser));
+        new ContentRestRequest("{}", emptyMap()).withContentOrSourceParamParserOrNull(parser -> assertEquals(emptyMap(), parser.map()));
+        new ContentRestRequest("{}", singletonMap("source", "stuff2")).withContentOrSourceParamParserOrNull(parser ->
+                assertEquals(emptyMap(), parser.map()));
+        new ContentRestRequest("", singletonMap("source", "{}")).withContentOrSourceParamParserOrNull(parser ->
+                assertEquals(emptyMap(), parser.map()));
+    }
+
     private static final class ContentRestRequest extends RestRequest {
         private final BytesArray content;
         public ContentRestRequest(String content, Map<String, String> params) {

+ 5 - 6
core/src/test/java/org/elasticsearch/snapshots/SnapshotRequestsTests.java

@@ -24,6 +24,7 @@ import org.elasticsearch.action.admin.cluster.snapshots.restore.RestoreSnapshotR
 import org.elasticsearch.action.support.IndicesOptions;
 import org.elasticsearch.common.bytes.BytesReference;
 import org.elasticsearch.common.xcontent.XContentBuilder;
+import org.elasticsearch.common.xcontent.XContentHelper;
 import org.elasticsearch.test.ESTestCase;
 
 import java.io.IOException;
@@ -76,10 +77,9 @@ public class SnapshotRequestsTests extends ESTestCase {
             builder.endArray();
         }
 
-        byte[] bytes = BytesReference.toBytes(builder.endObject().bytes());
+        BytesReference bytes = builder.endObject().bytes();
 
-
-        request.source(bytes);
+        request.source(XContentHelper.createParser(bytes).mapOrdered());
 
         assertEquals("test-repo", request.repository());
         assertEquals("test-snap", request.snapshot());
@@ -135,10 +135,9 @@ public class SnapshotRequestsTests extends ESTestCase {
             builder.endArray();
         }
 
-        byte[] bytes = BytesReference.toBytes(builder.endObject().bytes());
-
+        BytesReference bytes = builder.endObject().bytes();
 
-        request.source(bytes);
+        request.source(XContentHelper.createParser(bytes).mapOrdered());
 
         assertEquals("test-repo", request.repository());
         assertEquals("test-snap", request.snapshot());

+ 2 - 5
modules/reindex/src/main/java/org/elasticsearch/index/reindex/RestReindexAction.java

@@ -117,16 +117,13 @@ public class RestReindexAction extends AbstractBaseReindexRestHandler<ReindexReq
 
     @Override
     protected ReindexRequest buildRequest(RestRequest request) throws IOException {
-        if (false == request.hasContent()) {
-            throw new IllegalArgumentException("_reindex requires a request body");
-        }
         if (request.hasParam("pipeline")) {
             throw new IllegalArgumentException("_reindex doesn't support [pipeline] as a query parmaeter. "
                     + "Specify it in the [dest] object instead.");
         }
         ReindexRequest internal = new ReindexRequest(new SearchRequest(), new IndexRequest());
-        try (XContentParser xcontent = XContentFactory.xContent(request.content()).createParser(request.content())) {
-            PARSER.parse(xcontent, internal, new ReindexParseContext(searchRequestParsers, parseFieldMatcher));
+        try (XContentParser parser = request.contentParser()) {
+            PARSER.parse(parser, internal, new ReindexParseContext(searchRequestParsers, parseFieldMatcher));
         }
         return internal;
     }

+ 1 - 2
qa/smoke-test-http/src/test/java/org/elasticsearch/http/TestDeprecationHeaderRestAction.java

@@ -23,7 +23,6 @@ import org.elasticsearch.common.inject.Inject;
 import org.elasticsearch.common.settings.Setting;
 import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.common.xcontent.XContentBuilder;
-import org.elasticsearch.common.xcontent.XContentFactory;
 import org.elasticsearch.common.xcontent.XContentParser;
 import org.elasticsearch.rest.BaseRestHandler;
 import org.elasticsearch.rest.BytesRestResponse;
@@ -83,7 +82,7 @@ public class TestDeprecationHeaderRestAction extends BaseRestHandler {
     public RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) throws IOException {
         final List<String> settings;
 
-        try (XContentParser parser = XContentFactory.xContent(request.content()).createParser(request.content())) {
+        try (XContentParser parser = request.contentParser()) {
             final Map<String, Object> source = parser.map();
 
             if (source.containsKey("deprecated_settings")) {