171 lines
5.6 KiB
Java
171 lines
5.6 KiB
Java
/*
|
|
* Copyright (c) 2015-present, Parse, LLC.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*/
|
|
package com.parse;
|
|
|
|
import com.parse.http.ParseHttpRequest;
|
|
import com.parse.http.ParseHttpResponse;
|
|
|
|
import org.json.JSONArray;
|
|
import org.json.JSONException;
|
|
import org.json.JSONObject;
|
|
|
|
import java.io.IOException;
|
|
import java.io.InputStream;
|
|
import java.net.MalformedURLException;
|
|
import java.net.URL;
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
|
|
import bolts.Continuation;
|
|
import bolts.Task;
|
|
import bolts.TaskCompletionSource;
|
|
|
|
/** package */ class ParseRESTObjectBatchCommand extends ParseRESTCommand {
|
|
public final static int COMMAND_OBJECT_BATCH_MAX_SIZE = 50;
|
|
|
|
private static final String KEY_RESULTS = "results";
|
|
|
|
public static List<Task<JSONObject>> executeBatch(
|
|
ParseHttpClient client, List<ParseRESTObjectCommand> commands, String sessionToken) {
|
|
final int batchSize = commands.size();
|
|
List<Task<JSONObject>> tasks = new ArrayList<>(batchSize);
|
|
|
|
if (batchSize == 1) {
|
|
// There's only one, just execute it
|
|
tasks.add(commands.get(0).executeAsync(client));
|
|
return tasks;
|
|
}
|
|
|
|
if (batchSize > COMMAND_OBJECT_BATCH_MAX_SIZE) {
|
|
// There's more than the max, split it up into batches
|
|
List<List<ParseRESTObjectCommand>> batches = Lists.partition(commands,
|
|
COMMAND_OBJECT_BATCH_MAX_SIZE);
|
|
for (int i = 0, size = batches.size(); i < size; i++) {
|
|
List<ParseRESTObjectCommand> batch = batches.get(i);
|
|
tasks.addAll(executeBatch(client, batch, sessionToken));
|
|
}
|
|
return tasks;
|
|
}
|
|
|
|
final List<TaskCompletionSource<JSONObject>> tcss = new ArrayList<>(batchSize);
|
|
for (int i = 0; i < batchSize; i++) {
|
|
TaskCompletionSource<JSONObject> tcs = new TaskCompletionSource<>();
|
|
tcss.add(tcs);
|
|
tasks.add(tcs.getTask());
|
|
}
|
|
|
|
JSONObject parameters = new JSONObject();
|
|
JSONArray requests = new JSONArray();
|
|
try {
|
|
for (ParseRESTObjectCommand command : commands) {
|
|
JSONObject requestParameters = new JSONObject();
|
|
requestParameters.put("method", command.method.toString());
|
|
requestParameters.put("path", new URL(server, command.httpPath).getPath());
|
|
JSONObject body = command.jsonParameters;
|
|
if (body != null) {
|
|
requestParameters.put("body", body);
|
|
}
|
|
requests.put(requestParameters);
|
|
}
|
|
parameters.put("requests", requests);
|
|
} catch (JSONException | MalformedURLException e) {
|
|
throw new RuntimeException(e);
|
|
}
|
|
|
|
ParseRESTCommand command = new ParseRESTObjectBatchCommand(
|
|
"batch", ParseHttpRequest.Method.POST, parameters, sessionToken);
|
|
|
|
command.executeAsync(client).continueWith(new Continuation<JSONObject, Void>() {
|
|
@Override
|
|
public Void then(Task<JSONObject> task) throws Exception {
|
|
TaskCompletionSource<JSONObject> tcs;
|
|
|
|
if (task.isFaulted() || task.isCancelled()) {
|
|
// REST command failed or canceled, fail or cancel all tasks
|
|
for (int i = 0; i < batchSize; i++) {
|
|
tcs = tcss.get(i);
|
|
if (task.isFaulted()) {
|
|
tcs.setError(task.getError());
|
|
} else {
|
|
tcs.setCancelled();
|
|
}
|
|
}
|
|
}
|
|
|
|
JSONObject json = task.getResult();
|
|
JSONArray results = json.getJSONArray(KEY_RESULTS);
|
|
|
|
int resultLength = results.length();
|
|
if (resultLength != batchSize) {
|
|
// Invalid response, fail all tasks
|
|
for (int i = 0; i < batchSize; i++) {
|
|
tcs = tcss.get(i);
|
|
tcs.setError(new IllegalStateException(
|
|
"Batch command result count expected: " + batchSize + " but was: " + resultLength));
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < batchSize; i++) {
|
|
JSONObject result = results.getJSONObject(i);
|
|
tcs = tcss.get(i);
|
|
|
|
if (result.has("success")) {
|
|
JSONObject success = result.getJSONObject("success");
|
|
tcs.setResult(success);
|
|
} else if (result.has("error")) {
|
|
JSONObject error = result.getJSONObject("error");
|
|
tcs.setError(new ParseException(error.getInt("code"), error.getString("error")));
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
});
|
|
|
|
return tasks;
|
|
}
|
|
|
|
private ParseRESTObjectBatchCommand(
|
|
String httpPath,
|
|
ParseHttpRequest.Method httpMethod,
|
|
JSONObject parameters,
|
|
String sessionToken) {
|
|
super(httpPath, httpMethod, parameters, sessionToken);
|
|
}
|
|
|
|
/**
|
|
* /batch is the only endpoint that doesn't return a JSONObject... It returns a JSONArray, but
|
|
* let's wrap that with a JSONObject {@code { "results": <original response%gt; }}.
|
|
*/
|
|
@Override
|
|
protected Task<JSONObject> onResponseAsync(ParseHttpResponse response,
|
|
ProgressCallback downloadProgressCallback) {
|
|
InputStream responseStream = null;
|
|
String content = null;
|
|
try {
|
|
responseStream = response.getContent();
|
|
content = new String(ParseIOUtils.toByteArray(responseStream));
|
|
} catch (IOException e) {
|
|
return Task.forError(e);
|
|
} finally {
|
|
ParseIOUtils.closeQuietly(responseStream);
|
|
}
|
|
|
|
JSONObject json;
|
|
try {
|
|
JSONArray results = new JSONArray(content);
|
|
json = new JSONObject();
|
|
json.put(KEY_RESULTS, results);
|
|
} catch (JSONException e) {
|
|
return Task.forError(newTemporaryException("bad json response", e));
|
|
}
|
|
|
|
return Task.forResult(json);
|
|
}
|
|
}
|