com.parse.CacheQueryController.java Source code

Java tutorial

Introduction

Here is the source code for com.parse.CacheQueryController.java

Source

/*
 * 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 org.json.JSONException;
import org.json.JSONObject;

import java.util.List;
import java.util.concurrent.Callable;

import bolts.Continuation;
import bolts.Task;

/** package */
class CacheQueryController extends AbstractQueryController {

    private final NetworkQueryController networkController;

    public CacheQueryController(NetworkQueryController network) {
        networkController = network;
    }

    @Override
    public <T extends ParseObject> Task<List<T>> findAsync(final ParseQuery.State<T> state, final ParseUser user,
            final Task<Void> cancellationToken) {
        final String sessionToken = user != null ? user.getSessionToken() : null;
        CommandDelegate<List<T>> callbacks = new CommandDelegate<List<T>>() {
            @Override
            public Task<List<T>> runOnNetworkAsync(boolean retry) {
                return networkController.findAsync(state, sessionToken, retry, cancellationToken);
            }

            @Override
            public Task<List<T>> runFromCacheAsync() {
                return findFromCacheAsync(state, sessionToken);
            }
        };
        return runCommandWithPolicyAsync(callbacks, state.cachePolicy());
    }

    @Override
    public <T extends ParseObject> Task<Integer> countAsync(final ParseQuery.State<T> state, final ParseUser user,
            final Task<Void> cancellationToken) {
        final String sessionToken = user != null ? user.getSessionToken() : null;
        CommandDelegate<Integer> callbacks = new CommandDelegate<Integer>() {
            @Override
            public Task<Integer> runOnNetworkAsync(boolean retry) {
                return networkController.countAsync(state, sessionToken, retry, cancellationToken);
            }

            @Override
            public Task<Integer> runFromCacheAsync() {
                return countFromCacheAsync(state, sessionToken);
            }
        };
        return runCommandWithPolicyAsync(callbacks, state.cachePolicy());
    }

    /**
     * Retrieves the results of the last time {@link ParseQuery#find()} was called on a query
     * identical to this one.
     *
     * @param sessionToken The user requesting access.
     * @return A list of {@link ParseObject}s corresponding to this query. Returns null if there is no
     *          cache for this query.
     */
    private <T extends ParseObject> Task<List<T>> findFromCacheAsync(final ParseQuery.State<T> state,
            String sessionToken) {
        final String cacheKey = ParseRESTQueryCommand.findCommand(state, sessionToken).getCacheKey();
        return Task.call(new Callable<List<T>>() {
            @Override
            public List<T> call() throws Exception {
                JSONObject cached = ParseKeyValueCache.jsonFromKeyValueCache(cacheKey, state.maxCacheAge());
                if (cached == null) {
                    throw new ParseException(ParseException.CACHE_MISS, "results not cached");
                }
                try {
                    return networkController.convertFindResponse(state, cached);
                } catch (JSONException e) {
                    throw new ParseException(ParseException.CACHE_MISS, "the cache contains corrupted json");
                }
            }
        }, Task.BACKGROUND_EXECUTOR);
    }

    /**
     * Retrieves the results of the last time {@link ParseQuery#count()} was called on a query
     * identical to this one.
     *
     * @param sessionToken The user requesting access.
     * @return A list of {@link ParseObject}s corresponding to this query. Returns null if there is no
     *          cache for this query.
     */
    private <T extends ParseObject> Task<Integer> countFromCacheAsync(final ParseQuery.State<T> state,
            String sessionToken) {
        final String cacheKey = ParseRESTQueryCommand.countCommand(state, sessionToken).getCacheKey();
        return Task.call(new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                JSONObject cached = ParseKeyValueCache.jsonFromKeyValueCache(cacheKey, state.maxCacheAge());
                if (cached == null) {
                    throw new ParseException(ParseException.CACHE_MISS, "results not cached");
                }
                try {
                    return cached.getInt("count");
                } catch (JSONException e) {
                    throw new ParseException(ParseException.CACHE_MISS, "the cache contains corrupted json");
                }
            }
        }, Task.BACKGROUND_EXECUTOR);
    }

    private <TResult> Task<TResult> runCommandWithPolicyAsync(final CommandDelegate<TResult> c,
            ParseQuery.CachePolicy policy) {
        switch (policy) {
        case IGNORE_CACHE:
        case NETWORK_ONLY:
            return c.runOnNetworkAsync(true);
        case CACHE_ONLY:
            return c.runFromCacheAsync();
        case CACHE_ELSE_NETWORK:
            return c.runFromCacheAsync().continueWithTask(new Continuation<TResult, Task<TResult>>() {
                @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
                @Override
                public Task<TResult> then(Task<TResult> task) throws Exception {
                    if (task.getError() instanceof ParseException) {
                        return c.runOnNetworkAsync(true);
                    }
                    return task;
                }
            });
        case NETWORK_ELSE_CACHE:
            return c.runOnNetworkAsync(false).continueWithTask(new Continuation<TResult, Task<TResult>>() {
                @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
                @Override
                public Task<TResult> then(Task<TResult> task) throws Exception {
                    Exception error = task.getError();
                    if (error instanceof ParseException
                            && ((ParseException) error).getCode() == ParseException.CONNECTION_FAILED) {
                        return c.runFromCacheAsync();
                    }
                    // Either the query succeeded, or there was an an error with the query, not the
                    // network
                    return task;
                }
            });
        case CACHE_THEN_NETWORK:
            throw new RuntimeException("You cannot use the cache policy CACHE_THEN_NETWORK with find()");
        default:
            throw new RuntimeException("Unknown cache policy: " + policy);
        }
    }

    /**
     * A callback that will be used to tell runCommandWithPolicy how to perform the command on the
     * network and form the cache.
     */
    private interface CommandDelegate<T> {
        // Fetches data from the network.
        Task<T> runOnNetworkAsync(boolean retry);

        // Fetches data from the cache.
        Task<T> runFromCacheAsync();
    }
}