use of bolts.TaskCompletionSource in project Parse-SDK-Android by ParsePlatform.
the class ParseCommandCache method enqueueEventuallyAsync.
/**
* Attempts to run the given command and any pending commands. Adds the command to the pending set
* if it can't be run yet.
*
* @param command
* - The command to run.
* @param preferOldest
* - When the disk is full, if preferOldest, drop new commands. Otherwise, the oldest
* commands will be deleted to make room.
* @param object
* - See runEventually.
*/
private Task<JSONObject> enqueueEventuallyAsync(ParseRESTCommand command, boolean preferOldest, ParseObject object) {
Parse.requirePermission(Manifest.permission.ACCESS_NETWORK_STATE);
TaskCompletionSource<JSONObject> tcs = new TaskCompletionSource<>();
byte[] json;
try {
// objectId after the save completes.
if (object != null && object.getObjectId() == null) {
command.setLocalId(object.getOrCreateLocalId());
}
JSONObject jsonObject = command.toJSONObject();
json = jsonObject.toString().getBytes("UTF-8");
} catch (UnsupportedEncodingException e) {
if (Parse.LOG_LEVEL_WARNING >= Parse.getLogLevel()) {
log.log(Level.WARNING, "UTF-8 isn't supported. This shouldn't happen.", e);
}
notifyTestHelper(TestHelper.COMMAND_NOT_ENQUEUED);
return Task.forResult(null);
}
// even bother trying.
if (json.length > maxCacheSizeBytes) {
if (Parse.LOG_LEVEL_WARNING >= Parse.getLogLevel()) {
log.warning("Unable to save command for later because it's too big.");
}
notifyTestHelper(TestHelper.COMMAND_NOT_ENQUEUED);
return Task.forResult(null);
}
synchronized (lock) {
try {
// Is there enough free storage space?
String[] fileNames = cachePath.list();
if (fileNames != null) {
Arrays.sort(fileNames);
int size = 0;
for (String fileName : fileNames) {
File file = new File(cachePath, fileName);
// Should be safe to convert long to int, because we don't allow
// files larger than 2GB.
size += (int) file.length();
}
size += json.length;
if (size > maxCacheSizeBytes) {
if (preferOldest) {
if (Parse.LOG_LEVEL_WARNING >= Parse.getLogLevel()) {
log.warning("Unable to save command for later because storage is full.");
}
return Task.forResult(null);
} else {
if (Parse.LOG_LEVEL_WARNING >= Parse.getLogLevel()) {
log.warning("Deleting old commands to make room in command cache.");
}
int indexToDelete = 0;
while (size > maxCacheSizeBytes && indexToDelete < fileNames.length) {
File file = new File(cachePath, fileNames[indexToDelete++]);
size -= (int) file.length();
removeFile(file);
}
}
}
}
// Get the current time to store in the filename, so that we process them in order.
String prefix1 = Long.toHexString(System.currentTimeMillis());
if (prefix1.length() < 16) {
char[] zeroes = new char[16 - prefix1.length()];
Arrays.fill(zeroes, '0');
prefix1 = new String(zeroes) + prefix1;
}
// Then add another incrementing number in case we enqueue items faster than the system's
// time granularity.
String prefix2 = Integer.toHexString(filenameCounter++);
if (prefix2.length() < 8) {
char[] zeroes = new char[8 - prefix2.length()];
Arrays.fill(zeroes, '0');
prefix2 = new String(zeroes) + prefix2;
}
String prefix = "CachedCommand_" + prefix1 + "_" + prefix2 + "_";
// Get a unique filename to store this command in.
File path = File.createTempFile(prefix, "", cachePath);
// Write the command to that file.
pendingTasks.put(path, tcs);
command.retainLocalIds();
ParseFileUtils.writeByteArrayToFile(path, json);
notifyTestHelper(TestHelper.COMMAND_ENQUEUED);
unprocessedCommandsExist = true;
} catch (IOException e) {
if (Parse.LOG_LEVEL_WARNING >= Parse.getLogLevel()) {
log.log(Level.WARNING, "Unable to save command for later.", e);
}
} finally {
lock.notifyAll();
}
}
return tcs.getTask();
}
use of bolts.TaskCompletionSource in project Parse-SDK-Android by ParsePlatform.
the class OfflineStore method getOrCreateUUIDAsync.
/**
* Gets the UUID for the given object, if it has one. Otherwise, creates a new UUID for the object
* and adds a new row to the database for the object with no data.
*/
private Task<String> getOrCreateUUIDAsync(final ParseObject object, ParseSQLiteDatabase db) {
final String newUUID = UUID.randomUUID().toString();
final TaskCompletionSource<String> tcs = new TaskCompletionSource<>();
synchronized (lock) {
Task<String> uuidTask = objectToUuidMap.get(object);
if (uuidTask != null) {
return uuidTask;
}
// The object doesn't have a UUID yet, so we're gonna have to make one.
objectToUuidMap.put(object, tcs.getTask());
uuidToObjectMap.put(newUUID, object);
fetchedObjects.put(object, tcs.getTask().onSuccess(new Continuation<String, ParseObject>() {
@Override
public ParseObject then(Task<String> task) throws Exception {
return object;
}
}));
}
/*
* We need to put a placeholder row in the database so that later on, the save can just be an
* update. This could be a pointer to an object that itself never gets saved offline, in which
* case the consumer will just have to deal with that.
*/
ContentValues values = new ContentValues();
values.put(OfflineSQLiteOpenHelper.KEY_UUID, newUUID);
values.put(OfflineSQLiteOpenHelper.KEY_CLASS_NAME, object.getClassName());
db.insertOrThrowAsync(OfflineSQLiteOpenHelper.TABLE_OBJECTS, values).continueWith(new Continuation<Void, Void>() {
@Override
public Void then(Task<Void> task) throws Exception {
// This will signal that the UUID does represent a row in the database.
tcs.setResult(newUUID);
return null;
}
});
return tcs.getTask();
}
use of bolts.TaskCompletionSource in project Parse-SDK-Android by ParsePlatform.
the class ParseFile method getDataStreamInBackground.
/**
* Asynchronously gets the data stream from cached file if available or fetches its content from
* the network, saves the content as cached file and returns the data stream of the cached file.
* The {@code ProgressCallback} will be called periodically with progress updates.
*
* @param progressCallback
* A {@code ProgressCallback} that is called periodically with progress updates.
* @return A Task that is resolved when the data stream of this object has been fetched.
*/
public Task<InputStream> getDataStreamInBackground(final ProgressCallback progressCallback) {
final TaskCompletionSource<Void> cts = new TaskCompletionSource<>();
currentTasks.add(cts);
return taskQueue.enqueue(new Continuation<Void, Task<InputStream>>() {
@Override
public Task<InputStream> then(Task<Void> toAwait) throws Exception {
return fetchInBackground(progressCallback, toAwait, cts.getTask()).onSuccess(new Continuation<File, InputStream>() {
@Override
public InputStream then(Task<File> task) throws Exception {
return new FileInputStream(task.getResult());
}
});
}
}).continueWithTask(new Continuation<InputStream, Task<InputStream>>() {
@Override
public Task<InputStream> then(Task<InputStream> task) throws Exception {
// release
cts.trySetResult(null);
currentTasks.remove(cts);
return task;
}
});
}
use of bolts.TaskCompletionSource in project Parse-SDK-Android by ParsePlatform.
the class ParseTaskUtils method callbackOnMainThreadAsync.
/**
* Calls the callback after a task completes on the main thread, returning a Task that completes
* with the same result as the input task after the callback has been run. If reportCancellation
* is false, the callback will not be called if the task was cancelled.
*/
/* package */
static <T> Task<T> callbackOnMainThreadAsync(Task<T> task, final ParseCallback2<T, ParseException> callback, final boolean reportCancellation) {
if (callback == null) {
return task;
}
final TaskCompletionSource<T> tcs = new TaskCompletionSource();
task.continueWith(new Continuation<T, Void>() {
@Override
public Void then(final Task<T> task) throws Exception {
if (task.isCancelled() && !reportCancellation) {
tcs.setCancelled();
return null;
}
ParseExecutors.main().execute(new Runnable() {
@Override
public void run() {
try {
Exception error = task.getError();
if (error != null && !(error instanceof ParseException)) {
error = new ParseException(error);
}
callback.done(task.getResult(), (ParseException) error);
} finally {
if (task.isCancelled()) {
tcs.setCancelled();
} else if (task.isFaulted()) {
tcs.setError(task.getError());
} else {
tcs.setResult(task.getResult());
}
}
}
});
return null;
}
});
return tcs.getTask();
}
use of bolts.TaskCompletionSource in project Parse-SDK-Android by ParsePlatform.
the class ParseRESTObjectBatchCommand method executeBatch.
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 e) {
throw new RuntimeException(e);
} catch (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;
}
Aggregations