use of bolts.Continuation 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.Continuation in project Parse-SDK-Android by ParsePlatform.
the class NetworkObjectController method saveAllAsync.
@Override
public List<Task<ParseObject.State>> saveAllAsync(List<ParseObject.State> states, List<ParseOperationSet> operationsList, String sessionToken, List<ParseDecoder> decoders) {
int batchSize = states.size();
List<ParseRESTObjectCommand> commands = new ArrayList<>(batchSize);
ParseEncoder encoder = PointerEncoder.get();
for (int i = 0; i < batchSize; i++) {
ParseObject.State state = states.get(i);
ParseOperationSet operations = operationsList.get(i);
JSONObject objectJSON = coder.encode(state, operations, encoder);
ParseRESTObjectCommand command = ParseRESTObjectCommand.saveObjectCommand(state, objectJSON, sessionToken);
commands.add(command);
}
final List<Task<JSONObject>> batchTasks = ParseRESTObjectBatchCommand.executeBatch(client, commands, sessionToken);
final List<Task<ParseObject.State>> tasks = new ArrayList<>(batchSize);
for (int i = 0; i < batchSize; i++) {
final ParseObject.State state = states.get(i);
final ParseDecoder decoder = decoders.get(i);
tasks.add(batchTasks.get(i).onSuccess(new Continuation<JSONObject, ParseObject.State>() {
@Override
public ParseObject.State then(Task<JSONObject> task) throws Exception {
JSONObject result = task.getResult();
// Copy and clear to create an new empty instance of the same type as `state`
ParseObject.State.Init<?> builder = state.newBuilder().clear();
return coder.decode(builder, result, decoder).isComplete(false).build();
}
}));
}
return tasks;
}
use of bolts.Continuation in project Parse-SDK-Android by ParsePlatform.
the class OfflineQueryLogic method createMatcher.
/**
* Returns a ConstraintMatcher that return true iff the object matches QueryConstraints. This
* takes in a SQLiteDatabase connection because SQLite is finicky about nesting connections, so we
* want to reuse them whenever possible.
*/
private <T extends ParseObject> ConstraintMatcher<T> createMatcher(ParseUser user, QueryConstraints queryConstraints) {
// Make a list of all the matchers to AND together.
final ArrayList<ConstraintMatcher<T>> matchers = new ArrayList<>();
for (final String key : queryConstraints.keySet()) {
final Object queryConstraintValue = queryConstraints.get(key);
if (key.equals("$or")) {
/*
* A set of queries to be OR-ed together.
*/
@SuppressWarnings("unchecked") ConstraintMatcher<T> matcher = createOrMatcher(user, (ArrayList<QueryConstraints>) queryConstraintValue);
matchers.add(matcher);
} else if (queryConstraintValue instanceof KeyConstraints) {
/*
* It's a set of constraints that should be AND-ed together.
*/
KeyConstraints keyConstraints = (KeyConstraints) queryConstraintValue;
for (String operator : keyConstraints.keySet()) {
final Object keyConstraintValue = keyConstraints.get(operator);
ConstraintMatcher<T> matcher = createMatcher(user, operator, keyConstraintValue, key, keyConstraints);
matchers.add(matcher);
}
} else if (queryConstraintValue instanceof RelationConstraint) {
/*
* It's a $relatedTo constraint.
*/
final RelationConstraint relation = (RelationConstraint) queryConstraintValue;
matchers.add(new ConstraintMatcher<T>(user) {
@Override
public Task<Boolean> matchesAsync(T object, ParseSQLiteDatabase db) {
return Task.forResult(relation.getRelation().hasKnownObject(object));
}
});
} else {
/*
* It's not a set of constraints, so it's just a value to compare against.
*/
matchers.add(new ConstraintMatcher<T>(user) {
@Override
public Task<Boolean> matchesAsync(T object, ParseSQLiteDatabase db) {
Object objectValue;
try {
objectValue = getValue(object, key);
} catch (ParseException e) {
return Task.forError(e);
}
return Task.forResult(matchesEqualConstraint(queryConstraintValue, objectValue));
}
});
}
}
/*
* Now AND together the constraints for each key.
*/
return new ConstraintMatcher<T>(user) {
@Override
public Task<Boolean> matchesAsync(final T object, final ParseSQLiteDatabase db) {
Task<Boolean> task = Task.forResult(true);
for (final ConstraintMatcher<T> matcher : matchers) {
task = task.onSuccessTask(new Continuation<Boolean, Task<Boolean>>() {
@Override
public Task<Boolean> then(Task<Boolean> task) throws Exception {
if (!task.getResult()) {
return task;
}
return matcher.matchesAsync(object, db);
}
});
}
return task;
}
};
}
use of bolts.Continuation in project Parse-SDK-Android by ParsePlatform.
the class ParseObject method deepSaveAsync.
/**
* This saves all of the objects and files reachable from the given object. It does its work in
* multiple waves, saving as many as possible in each wave. If there's ever an error, it just
* gives up, sets error, and returns NO.
*/
private static Task<Void> deepSaveAsync(final Object object, final String sessionToken) {
Set<ParseObject> objects = new HashSet<>();
Set<ParseFile> files = new HashSet<>();
collectDirtyChildren(object, objects, files);
// This has to happen separately from everything else because ParseUser.save() is
// special-cased to work for lazy users, but new users can't be created by
// ParseMultiCommand's regular save.
Set<ParseUser> users = new HashSet<>();
for (ParseObject o : objects) {
if (o instanceof ParseUser) {
ParseUser user = (ParseUser) o;
if (user.isLazy()) {
users.add((ParseUser) o);
}
}
}
objects.removeAll(users);
// objects will need to wait for files to be complete since they may be nested children.
final AtomicBoolean filesComplete = new AtomicBoolean(false);
List<Task<Void>> tasks = new ArrayList<>();
for (ParseFile file : files) {
tasks.add(file.saveAsync(sessionToken, null, null));
}
Task<Void> filesTask = Task.whenAll(tasks).continueWith(new Continuation<Void, Void>() {
@Override
public Void then(Task<Void> task) throws Exception {
filesComplete.set(true);
return null;
}
});
// objects will need to wait for users to be complete since they may be nested children.
final AtomicBoolean usersComplete = new AtomicBoolean(false);
tasks = new ArrayList<>();
for (final ParseUser user : users) {
tasks.add(user.saveAsync(sessionToken));
}
Task<Void> usersTask = Task.whenAll(tasks).continueWith(new Continuation<Void, Void>() {
@Override
public Void then(Task<Void> task) throws Exception {
usersComplete.set(true);
return null;
}
});
final Capture<Set<ParseObject>> remaining = new Capture<>(objects);
Task<Void> objectsTask = Task.forResult(null).continueWhile(new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
return remaining.get().size() > 0;
}
}, new Continuation<Void, Task<Void>>() {
@Override
public Task<Void> then(Task<Void> task) throws Exception {
// Partition the objects into two sets: those that can be save immediately,
// and those that rely on other objects to be created first.
final List<ParseObject> current = new ArrayList<>();
final Set<ParseObject> nextBatch = new HashSet<>();
for (ParseObject obj : remaining.get()) {
if (obj.canBeSerialized()) {
current.add(obj);
} else {
nextBatch.add(obj);
}
}
remaining.set(nextBatch);
if (current.size() == 0 && filesComplete.get() && usersComplete.get()) {
// exception instead of an infinite loop.
throw new RuntimeException("Unable to save a ParseObject with a relation to a cycle.");
}
// Package all save commands together
if (current.size() == 0) {
return Task.forResult(null);
}
return enqueueForAll(current, new Continuation<Void, Task<Void>>() {
@Override
public Task<Void> then(Task<Void> toAwait) throws Exception {
return saveAllAsync(current, sessionToken, toAwait);
}
});
}
});
return Task.whenAll(Arrays.asList(filesTask, usersTask, objectsTask));
}
use of bolts.Continuation in project Parse-SDK-Android by ParsePlatform.
the class ParseFileController method fetchAsync.
public Task<File> fetchAsync(final ParseFile.State state, @SuppressWarnings("UnusedParameters") String sessionToken, final ProgressCallback downloadProgressCallback, final Task<Void> cancellationToken) {
if (cancellationToken != null && cancellationToken.isCancelled()) {
return Task.cancelled();
}
final File cacheFile = getCacheFile(state);
return Task.call(new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
return cacheFile.exists();
}
}, ParseExecutors.io()).continueWithTask(new Continuation<Boolean, Task<File>>() {
@Override
public Task<File> then(Task<Boolean> task) throws Exception {
boolean result = task.getResult();
if (result) {
return Task.forResult(cacheFile);
}
if (cancellationToken != null && cancellationToken.isCancelled()) {
return Task.cancelled();
}
// Generate the temp file path for caching ParseFile content based on ParseFile's url
// The reason we do not write to the cacheFile directly is because there is no way we can
// verify if a cacheFile is complete or not. If download is interrupted in the middle, next
// time when we download the ParseFile, since cacheFile has already existed, we will return
// this incomplete cacheFile
final File tempFile = getTempFile(state);
// network
final ParseAWSRequest request = new ParseAWSRequest(ParseHttpRequest.Method.GET, state.url(), tempFile);
// We do not need to delete the temp file since we always try to overwrite it
return request.executeAsync(awsClient(), null, downloadProgressCallback, cancellationToken).continueWithTask(new Continuation<Void, Task<File>>() {
@Override
public Task<File> then(Task<Void> task) throws Exception {
// If the top-level task was cancelled, don't actually set the data -- just move on.
if (cancellationToken != null && cancellationToken.isCancelled()) {
throw new CancellationException();
}
if (task.isFaulted()) {
ParseFileUtils.deleteQuietly(tempFile);
return task.cast();
}
// Since we give the cacheFile pointer to developers, it is not safe to guarantee
// cacheFile always does not exist here, so it is better to delete it manually,
// otherwise moveFile may throw an exception.
ParseFileUtils.deleteQuietly(cacheFile);
ParseFileUtils.moveFile(tempFile, cacheFile);
return Task.forResult(cacheFile);
}
}, ParseExecutors.io());
}
});
}
Aggregations