Search in sources :

Example 6 with TaskCompletionSource

use of com.parse.boltsinternal.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(task -> 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((Continuation<Void, Void>) task -> {
        tcs.setResult(newUUID);
        return null;
    });
    return tcs.getTask();
}
Also used : Context(android.content.Context) Arrays(java.util.Arrays) ConstraintMatcher(com.parse.OfflineQueryLogic.ConstraintMatcher) Pair(android.util.Pair) TextUtils(android.text.TextUtils) HashMap(java.util.HashMap) UUID(java.util.UUID) ArrayList(java.util.ArrayList) SQLiteDatabase(android.database.sqlite.SQLiteDatabase) List(java.util.List) JSONException(org.json.JSONException) Capture(com.parse.boltsinternal.Capture) Continuation(com.parse.boltsinternal.Continuation) JSONObject(org.json.JSONObject) Map(java.util.Map) ContentValues(android.content.ContentValues) LinkedList(java.util.LinkedList) Task(com.parse.boltsinternal.Task) TaskCompletionSource(com.parse.boltsinternal.TaskCompletionSource) WeakHashMap(java.util.WeakHashMap) Cursor(android.database.Cursor) ContentValues(android.content.ContentValues) TaskCompletionSource(com.parse.boltsinternal.TaskCompletionSource)

Example 7 with TaskCompletionSource

use of com.parse.boltsinternal.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();
}
Also used : TaskCompletionSource(com.parse.boltsinternal.TaskCompletionSource) JSONObject(org.json.JSONObject) UnsupportedEncodingException(java.io.UnsupportedEncodingException) IOException(java.io.IOException) File(java.io.File)

Example 8 with TaskCompletionSource

use of com.parse.boltsinternal.TaskCompletionSource in project Parse-SDK-Android by ParsePlatform.

the class ParseCommandCache method maybeRunAllCommandsNow.

/**
 * Attempts to run every command in the disk queue in order, synchronously. If there is no
 * network connection, returns immediately without doing anything. If there is supposedly a
 * connection, but parse can't be reached, waits timeoutRetryWaitSeconds before retrying up to
 * retriesRemaining times. Blocks until either there's a connection, or the retries are
 * exhausted. If any command fails, just deletes it and moves on to the next one.
 */
private void maybeRunAllCommandsNow(int retriesRemaining) {
    synchronized (lock) {
        unprocessedCommandsExist = false;
        if (!isConnected()) {
            // There's no way to do work when there's no network connection.
            return;
        }
        String[] fileNames = cachePath.list();
        if (fileNames == null || fileNames.length == 0) {
            return;
        }
        Arrays.sort(fileNames);
        for (String fileName : fileNames) {
            final File file = new File(cachePath, fileName);
            // Read one command from the cache.
            JSONObject json;
            try {
                json = ParseFileUtils.readFileToJSONObject(file);
            } catch (FileNotFoundException e) {
                // This shouldn't really be possible.
                if (Parse.LOG_LEVEL_ERROR >= Parse.getLogLevel()) {
                    log.log(Level.SEVERE, "File disappeared from cache while being read.", e);
                }
                continue;
            } catch (IOException e) {
                if (Parse.LOG_LEVEL_ERROR >= Parse.getLogLevel()) {
                    log.log(Level.SEVERE, "Unable to read contents of file in cache.", e);
                }
                removeFile(file);
                continue;
            } catch (JSONException e) {
                if (Parse.LOG_LEVEL_ERROR >= Parse.getLogLevel()) {
                    log.log(Level.SEVERE, "Error parsing JSON found in cache.", e);
                }
                removeFile(file);
                continue;
            }
            // Convert the command from a string.
            final ParseRESTCommand command;
            final TaskCompletionSource<JSONObject> tcs = pendingTasks.containsKey(file) ? pendingTasks.get(file) : null;
            try {
                command = commandFromJSON(json);
            } catch (JSONException e) {
                if (Parse.LOG_LEVEL_ERROR >= Parse.getLogLevel()) {
                    log.log(Level.SEVERE, "Unable to create ParseCommand from JSON.", e);
                }
                removeFile(file);
                continue;
            }
            try {
                Task<JSONObject> commandTask;
                if (command == null) {
                    commandTask = Task.forResult(null);
                    if (tcs != null) {
                        tcs.setResult(null);
                    }
                    notifyTestHelper(TestHelper.COMMAND_OLD_FORMAT_DISCARDED);
                } else {
                    commandTask = command.executeAsync(httpClient).continueWithTask(task -> {
                        String localId = command.getLocalId();
                        Exception error = task.getError();
                        if (error != null) {
                            if (!(error instanceof ParseException) || ((ParseException) error).getCode() != ParseException.CONNECTION_FAILED) {
                                if (tcs != null) {
                                    tcs.setError(error);
                                }
                            }
                            return task;
                        }
                        JSONObject json1 = task.getResult();
                        if (tcs != null) {
                            tcs.setResult(json1);
                        } else if (localId != null) {
                            // If this command created a new objectId,
                            // add it to the map.
                            String objectId = json1.optString("objectId", null);
                            if (objectId != null) {
                                ParseCorePlugins.getInstance().getLocalIdManager().setObjectId(localId, objectId);
                            }
                        }
                        return task;
                    });
                }
                waitForTaskWithoutLock(commandTask);
                if (tcs != null) {
                    waitForTaskWithoutLock(tcs.getTask());
                }
                // The command succeeded. Remove it from the cache.
                removeFile(file);
                notifyTestHelper(TestHelper.COMMAND_SUCCESSFUL);
            } catch (ParseException e) {
                if (e.getCode() == ParseException.CONNECTION_FAILED) {
                    if (retriesRemaining > 0) {
                        // anything else.
                        if (Parse.LOG_LEVEL_INFO >= Parse.getLogLevel()) {
                            log.info("Network timeout in command cache. Waiting for " + timeoutRetryWaitSeconds + " seconds and then retrying " + retriesRemaining + " times.");
                        }
                        long currentTime = System.currentTimeMillis();
                        long waitUntil = currentTime + (long) (timeoutRetryWaitSeconds * 1000);
                        while (currentTime < waitUntil) {
                            // or should stop, just quit.
                            if (!isConnected() || shouldStop) {
                                if (Parse.LOG_LEVEL_INFO >= Parse.getLogLevel()) {
                                    log.info("Aborting wait because runEventually thread should stop.");
                                }
                                return;
                            }
                            try {
                                lock.wait(waitUntil - currentTime);
                            } catch (InterruptedException ie) {
                                shouldStop = true;
                            }
                            currentTime = System.currentTimeMillis();
                            if (currentTime < (waitUntil - (long) (timeoutRetryWaitSeconds * 1000))) {
                                // This situation should be impossible, so it must mean the
                                // clock changed.
                                currentTime = (waitUntil - (long) (timeoutRetryWaitSeconds * 1000));
                            }
                        }
                        maybeRunAllCommandsNow(retriesRemaining - 1);
                    } else {
                        setConnected(false);
                        notifyTestHelper(TestHelper.NETWORK_DOWN);
                    }
                } else {
                    if (Parse.LOG_LEVEL_ERROR >= Parse.getLogLevel()) {
                        log.log(Level.SEVERE, "Failed to run command.", e);
                    }
                    // Delete the command from the cache, even though it failed.
                    // Otherwise, we'll just keep trying it forever.
                    removeFile(file);
                    notifyTestHelper(TestHelper.COMMAND_FAILED, e);
                }
            }
        }
    }
}
Also used : ConnectivityManager(android.net.ConnectivityManager) Context(android.content.Context) Arrays(java.util.Arrays) IOException(java.io.IOException) HashMap(java.util.HashMap) Callable(java.util.concurrent.Callable) Logger(java.util.logging.Logger) File(java.io.File) FileNotFoundException(java.io.FileNotFoundException) Level(java.util.logging.Level) Manifest(android.Manifest) JSONException(org.json.JSONException) Capture(com.parse.boltsinternal.Capture) Continuation(com.parse.boltsinternal.Continuation) JSONObject(org.json.JSONObject) Task(com.parse.boltsinternal.Task) TaskCompletionSource(com.parse.boltsinternal.TaskCompletionSource) UnsupportedEncodingException(java.io.UnsupportedEncodingException) FileNotFoundException(java.io.FileNotFoundException) JSONException(org.json.JSONException) IOException(java.io.IOException) IOException(java.io.IOException) FileNotFoundException(java.io.FileNotFoundException) JSONException(org.json.JSONException) UnsupportedEncodingException(java.io.UnsupportedEncodingException) JSONObject(org.json.JSONObject) File(java.io.File)

Example 9 with TaskCompletionSource

use of com.parse.boltsinternal.TaskCompletionSource in project Parse-SDK-Android by ParsePlatform.

the class ParseFile method getDataInBackground.

/**
 * Asynchronously gets the data from cache if available or fetches its content from the network.
 * A {@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 has been fetched.
 */
public Task<byte[]> getDataInBackground(final ProgressCallback progressCallback) {
    final TaskCompletionSource<Void> cts = new TaskCompletionSource<>();
    currentTasks.add(cts);
    return taskQueue.enqueue(toAwait -> fetchInBackground(progressCallback, toAwait, cts.getTask()).onSuccess(task -> {
        File file = task.getResult();
        try {
            return ParseFileUtils.readFileToByteArray(file);
        } catch (IOException e) {
        // do nothing
        }
        return null;
    })).continueWithTask(task -> {
        // release
        cts.trySetResult(null);
        currentTasks.remove(cts);
        return task;
    });
}
Also used : Parcelable(android.os.Parcelable) Set(java.util.Set) IOException(java.io.IOException) Callable(java.util.concurrent.Callable) FileInputStream(java.io.FileInputStream) Parcel(android.os.Parcel) File(java.io.File) HashSet(java.util.HashSet) JSONException(org.json.JSONException) Continuation(com.parse.boltsinternal.Continuation) JSONObject(org.json.JSONObject) Task(com.parse.boltsinternal.Task) TaskCompletionSource(com.parse.boltsinternal.TaskCompletionSource) Collections(java.util.Collections) InputStream(java.io.InputStream) TaskCompletionSource(com.parse.boltsinternal.TaskCompletionSource) IOException(java.io.IOException) File(java.io.File)

Aggregations

TaskCompletionSource (com.parse.boltsinternal.TaskCompletionSource)9 Continuation (com.parse.boltsinternal.Continuation)6 Task (com.parse.boltsinternal.Task)6 IOException (java.io.IOException)5 JSONObject (org.json.JSONObject)5 Capture (com.parse.boltsinternal.Capture)4 HashMap (java.util.HashMap)4 JSONException (org.json.JSONException)4 Context (android.content.Context)3 File (java.io.File)3 ArrayList (java.util.ArrayList)3 Arrays (java.util.Arrays)3 List (java.util.List)3 Map (java.util.Map)3 ContentValues (android.content.ContentValues)2 Cursor (android.database.Cursor)2 SQLiteDatabase (android.database.sqlite.SQLiteDatabase)2 TextUtils (android.text.TextUtils)2 Pair (android.util.Pair)2 ConstraintMatcher (com.parse.OfflineQueryLogic.ConstraintMatcher)2