Package box

Source Code of box.BoxClientImpl$NullableItem

package box;

import static com.google.common.collect.Iterables.addAll;
import static com.google.common.collect.Iterables.filter;
import static com.google.common.collect.Iterables.transform;

import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutionException;

import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;

import oauth.signpost.OAuth;

import play.Logger;
import play.libs.WS;
import play.libs.WS.HttpResponse;
import play.libs.WS.WSRequest;
import rules.RuleUtils;
import box.Box.URLs;
import box.gson.BoxError;
import box.gson.BoxItem;
import box.gson.BoxMoveReq;
import box.gson.BoxCreateFolderItem;

import com.google.appengine.api.urlfetch.HTTPMethod;
import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.collect.Collections2;
import com.google.common.collect.Sets;
import com.google.gson.Gson;

import dropbox.client.FileMoveCollisionException;
import dropbox.client.InvalidTokenException;
import dropbox.client.NotADirectoryException;

public class BoxClientImpl implements BoxClient {
    public static class FileOrFolderPredicate implements Predicate<BoxItem> {
        private final ListingType type;
       
        public FileOrFolderPredicate(ListingType type) {
            this.type = type;
        }

        @Override
        public boolean apply(BoxItem item) {
            if (item.isFile()) {
                return this.type.includeFiles;
            }

            if (item.isFolder()) {
                return this.type.includeDirs;
            }

            Logger.warn("FileFolderPredicate: Unknown box item type: %s", item);
            return false;
        }
    }

    public final String token;

    private Cache<String, NullableItem> itemCache = CacheBuilder
            .newBuilder()
            .build(CacheLoader.from(new Function<String, NullableItem>() {
                @Override
                public NullableItem apply(String path) {
                    try {
                        if (path == null || "/".equals(path)) {
                            return new NullableItem(getMetadata("0", BoxItem.FOLDER));
                        }

                        BoxItem parent = getItem(RuleUtils.getParent(path));
                        if (parent == null || parent.id == null) {
                            return NULL_ITEM;
                        }

                        // Child metadata is loaded as a mini item, we do a new fetch to get the full listing
                        BoxItem miniChild = getChild(parent, RuleUtils.basename(path));
                        if (miniChild == null || miniChild.id == null) {
                            return NULL_ITEM;
                        }

                        if (miniChild.isFile()) {
                            return new NullableItem(miniChild);
                        }
                        // If we're fetching a folder, need to fetch it again to get contents
                        return new NullableItem(getMetadata(miniChild.id, miniChild.type));
                    } catch (InvalidTokenException e) {
                        return INVALID_TOKEN_ITEM;
                    }
                }
            }));

    private static final NullableItem NULL_ITEM = new NullableItem(null);
    private static final NullableItem INVALID_TOKEN_ITEM = new NullableItem(null);

    private static class ItemNameExtractor implements Function<BoxItem, String> {
        private final String parent;
       
        public ItemNameExtractor(String parent) {
            this.parent = parent;
        }

        @Override
        public String apply(BoxItem item) {
            return RuleUtils.normalize(parent + "/" + item.name, false);
        }
    }

    public static class NullableItem {
        public final BoxItem item;
       
        public NullableItem(BoxItem item) {
            this.item = item;
        }
    }

    BoxClientImpl(String token) {
        this.token = token;
    }

    @Override
    public void move(String from, String to) throws FileMoveCollisionException, InvalidTokenException {
        Logger.info("Move from %s to %s", from, to);
        Preconditions.checkNotNull(from);
        Preconditions.checkNotNull(to);
        String fromId = getId(from);
        if (fromId == null) {
            Logger.error("Failed to move file from %s to %s. Cannot resolve from file id.",
                         from, to);
            return;
        }

        String parent = RuleUtils.getParent(to);
        BoxItem toItem = getItem(parent);
        if (toItem == null) {
            Logger.warn("BoxClient.move: Parent folder missing: %s", parent);
            toItem = mkdirItem(parent);

            if (toItem == null) {
                Logger.error("Cannot move file, failed creating parent.");
                return;
            }
        }

        if (! toItem.isFolder()) {
            Logger.error("Cannot move, parent item is not a folder. Parent: %s Item: %s", parent, toItem);
            return;
        }

        Logger.info("Attempting to move file from: %s(%s) To: %s(%s)", from, fromId, RuleUtils.getParent(to), toItem);
        HttpResponse resp = req("/files/" + fromId)
                .body(new Gson().toJson(new BoxMoveReq(toItem.id, RuleUtils.basename(to))))
                .put();

        if (resp.success()) {
            BoxItem file = new Gson().fromJson(resp.getJson(), BoxItem.class);
            invalidate(RuleUtils.getParent(from));
            invalidate(from);
            invalidate(RuleUtils.getParent(to));
            invalidate(to);
            Logger.info("Successfully moved file from %s to %s. File: %s",
                        from, to, file);
            return;
        }

        Logger.error("Failed moving from %s to %s Error: %s", from, to, getError(resp));
        // 400 indicates file name collision
        if (Integer.valueOf(400).equals(resp.getStatus())) {
            throw new FileMoveCollisionException(String.format("From: %s To: %s", from, to));
        }
    }

    @Override
    public Set<String> listDir(String path) throws InvalidTokenException, NotADirectoryException {
        return listDir(path, ListingType.FILES);
    }

    @Override
    public Set<String> listDir(String path, ListingType listingType) throws InvalidTokenException, NotADirectoryException {
        Set<String> ret = Sets.newHashSet();
        BoxItem item = getItem(path);

        if (item != null && item.children != null && item.children.entries != null) {
            addAll(ret, transform(filter(item.children.entries,
                                         new FileOrFolderPredicate(listingType)),
                                  new ItemNameExtractor(path)));
            return ret;
        }

        Logger.warn("listDir: Cannot find well formed metadata entry for %s", path);
        return ret;
    }

    @Override
    public boolean mkdir(String path) throws InvalidTokenException {
        return mkdirItem(path) != null;
    }

    private BoxItem mkdirItem(String path) throws InvalidTokenException {
        Preconditions.checkNotNull(path, "Missing path.");
        Logger.info("BoxClient.mkdir: path %s", path);
        String parent = RuleUtils.getParent(path);
        BoxItem parentItem = getItem(parent);

        if (parentItem ==  null) {
            Logger.warn("Parent folder doesn't exist, creating. Path: %s Parent: %s", path, parent);
            parentItem = mkdirItem(parent);

            if (parentItem == null) {
                Logger.error("Could not create parent directory: %s", parent);
                return null;
            }
        }

        if (! parentItem.isFolder()) {
            Logger.error("Cannot create directory because parent is not a directory. Parent path: %s Item: %s", parent, parentItem);
            return null;
        }

        HttpResponse resp = req("/folders")
                .body(new Gson().toJson(new BoxCreateFolderItem(RuleUtils.basename(path), parentItem)))
                .post();

        if (resp.success()) {
            BoxItem folder = new Gson().fromJson(resp.getJson(), BoxItem.class);
            invalidate(path);
            Logger.info("Successfully created folder at path %s Folder: %s", path, folder);
            return folder;
        }

        Logger.error("Failed creating directory '%s' Error: %s", path, getError(resp));
        return null;
    }

    @Override
    public boolean exists(String path) throws InvalidTokenException {
        return getItem(path) != null;
    }

    /**
     * Make signed API request return the resulting HttpResponse
     * @param method HTTP method for the request
     * @param url full request URL with associated parameters
     * @return HTTP response
     */
    @Override
    @Nonnull
    public HttpResponse debug(HTTPMethod method, String url) throws InvalidTokenException {
        Preconditions.checkArgument(url.startsWith("/"), "url must start with /");
       
        WSRequest req = req(url);
        switch (method) {
        case GET:
            return req.get();
        case POST:
            return req.post();
        default:
            throw new IllegalArgumentException("Unhandled HTTP method");
        }
    }

    private static class ItemNameEquals implements Predicate<BoxItem> {
        private final String name;

        public ItemNameEquals(String name) {
            this.name = name;
        }

        @Override
        public boolean apply(BoxItem item) {
            return this.name.equalsIgnoreCase(item.name);
        }
    }

    public static @CheckForNull BoxItem getChild(BoxItem parent, String child) {
        if (parent == null || parent.children == null || parent.children.entries == null) {
            return null;
        }

        List<BoxItem> items = parent.children.entries;

        Collection<BoxItem> matching = Collections2.filter(items, new ItemNameEquals(child));
        if (matching.isEmpty()) {
            Logger.warn("Box: could not find child in parent. Child: %s Parent: %s", child, parent);
            return null;
        }

        if (matching.size() == 1) {
            return matching.iterator().next();
        }

        Logger.error("Box: more than one child returned from API under same parent. Child: %s Matches: %s Parent: %s",
                     child, matching, parent);
        return null;
    }

    @Override
    public BoxAccount getAccount() {
        WSRequest req = req("/users/me");
       
        HttpResponse resp = req.get();
        if (resp.success()) {
            return new Gson().fromJson(resp.getJson(), BoxAccount.class);
        }
       
        throw new IllegalStateException("Failed fetching box account info. Status: " + resp.getStatus() +
                                        " Error: " + resp.getString());
    }

    private @CheckForNull BoxItem getMetadata(@Nonnull String id, String type) throws InvalidTokenException {
        if (id == null) {
            throw new NullPointerException("Parent id cannot be null");
        }

        WSRequest request;
        if (BoxItem.FOLDER.equals(type)) {
            request = req("/folders/" + OAuth.percentEncode(id))
                          .setParameter("limit", 1000);
        } else {
            request = req("/files/" + OAuth.percentEncode(id));
        }

        Logger.info("getMetadata: id: %s type: %s", id, type);
        HttpResponse resp = request.get();
        if (resp.success()) {
            return new Gson().fromJson(resp.getJson(), BoxItem.class);
        }

        String err = getError(resp);
        if (Integer.valueOf(401).equals(resp.getStatus())) {
            Logger.error("Box: Failed to fetch metadata because auth token has been revoked. Id: %s Error: %s",
                         id, err);
            throw new InvalidTokenException("Box auth token has been revoked.");
        }

        Logger.error("Box: Failed to fetch metadata. Id: %s Error: %s",
                     id, err);
        return null;
    }

    private static String getError(HttpResponse resp) {
        assert ! resp.success() : "Cannot get error for successful response.";
        try {
            return new Gson().fromJson(resp.getJson(), BoxError.class).toString();
        } catch (RuntimeException e) {
            return resp.toString();
        }
    }

    private void invalidate(String path) {
        itemCache.invalidate(RuleUtils.normalize(path));
    }

    private BoxItem getItem(String path) throws InvalidTokenException {
        try {
            NullableItem ni = itemCache.get(RuleUtils.normalize(path));
            if (ni == INVALID_TOKEN_ITEM) {
                throw new InvalidTokenException("Box auth token has been revoked.");
            }
            return ni.item;
        } catch (ExecutionException e) {
            Logger.error(e, "Cannot load %s", path);
            return null;
        }
    }

    private String getId(String path) throws InvalidTokenException {
        BoxItem item = getItem(path);
        return item == null ? null : item.id;
    }

    private WSRequest req(String path) {
        path = path.startsWith("/") ? path : "/" + path;
        return WS.url(URLs.BASE_V2 + path)
                 .setHeader("Authorization",
                            String.format("Authorization: Bearer %s", WS.encode(this.token)));
    }
}
TOP

Related Classes of box.BoxClientImpl$NullableItem

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.